| 10-02-04 09:56 Kako prebaciti korisnike iz LDAP imenika u (obrisan) /etc/passw
 
  piše ŽELJA BOROŠ
 
 
 Problem:
   Podaci o korisničkim računima su obrisani. Backup traka je zakazala, odnosno sadržava nešto starije podatke. Velika većina korisničkih direktorija se i dalje nalazi na disku (ili su vraćeni s trake), obrisani su "samo" korisnički passwordi i grupe.
   Što treba učiniti:
   Vratiti korisnicima mogućnost prijave na sustav, čitanja pošte te spajanja na CMU. Pri tom je nužno očuvati korisničke UID-ove kako ne bi došlo do problema s pravima pristupa na /home direktorije i problema s korisničkim webovima u ~/public_html. Uključiti i postaviti kvote korisnicima.
   Rješenje:
   Kako je postojala recentna kopija dumpa LDAP baze (LDIF datoteka), bilo je moguće vratiti korisnike i njihove zaporke na sustav pisanjem skripte u perlu. Skripta je potrebna jer bi ručno dodavanja korisnika bilo nepraktično. Perl je odabran zbog fleksibilnosti i brzine.
   Naknadno uočeni problemi:
   Nisu svi korisnici na sustavu uneseni u LDAP, dok neki korisnici koji postoje u LDAP-u ne postoje na sustavu (nemaju /home direktorij). Prvi slučaj se može riješiti uvidom u "papirnatu" evidenciju i ručnim dodavanjem imena i prezimena (nakon rekreiranja /etc/passwd datoteke) naredbom 'usermod'.
   Drugi slučaj se može riješiti skriptom, uz napomenu da takvi korisnici nužno dobivaju drugi UID (jer nemaju /home direktorij pa prema tome ni sačuvan UID). U ovom slučaju očuvanje UID-a ni nije prioritet, jer je sasvim svejedno koji će UID korisnik dobiti.
   Postupak:
   Iz LDIF se datoteke perl skriptom 'users_rescue.pl' izvlače podaci o korisnicima (ime, prezime, uid). Kako bi ova skripta pravilno radila, potrebno je kreirati dvije datoteke: 'uids.txt' i 'usernames.base64' (imena su proizvoljna, ali treba izmijeniti source perl skripte u slučaju promjene).
   U prvoj datoteci ("uids.txt") se nalaze dva stupca: u prvom korisnički uid, u drugom username. U drugoj datoteci ("usernames.base64") se nalaze "izgrepani" samo potrebni podaci iz LDIF datoteke (kako bi perl kod bio što jednostavniji). Datoteke je najlakše kreirati naredbama:
   ls -l /home | awk '{print $3, $9}' > uids.txt
 egrep '(cn:|uid:)' < organization.ldif > usernames.base64
   Nakon toga samo treba pokrenuti skriptu 'users_rescue.pl' koja će generirati
 ispis u /etc/passwd formatu sa svim korisnicima koji imaju uid na sustavu a
 ujedno se nalaze u LDAP-u. Skripta brine o imenima korisnika koji sadržavaju
 dijakritičke znakove, te ih konvertira u oblik bez "kvačica".
   Izlaz naredbe 'users_rescue.pl' preporučljivo je preusmjeriti u privremenu datoteku te ručno "zalijepiti" na /etc/passwd, pazeći da se ne preklapaju uid brojevi postojećih (sustavskih) i novih (korisničkih) računa.
   Iako je pripremljena i skripta 'pass_rescue.pl' koja ima zadatak kreirati izlaz u formatu /etc/shadow datoteke, ona nije upotrijebljena. 
   Razlog je slaba podrška za SHA (Secure Hash Algorithm) način "hashiranja"
 zaporki kao načina autentificiranja na Linuxu (a nikakva na Solarisu). Iz tog razloga (kao i što bržeg rješenja problema), išlo se na direktnu autentifikaciju preko PAM LDAP modula. Ovo je zapravo prednost, jer korisnik sad ima samo jednu zaporku za sve: e-mail, CMU, shell pristup itd.
   Kako Linux ima složeniju strukturu PAM-a (svaki servis ima svoju datoteku, za razliku od Solarisa koji rabi samo jednu, /etc/pam.conf), kreirana je skripta 'pam-fix.sh' koja automatski pravi nužne promjene u PAM konfiguracijskim datotekama. Naravno, predviđeno je kreiranje pričuvnih
 kopija datoteka u /etc/pam.d direktoriju (imaju dodatak .orig).
   U datoteku /etc/nsswitch.conf još treba dodati (izmijeniti) redak
   passwd:         compat
   u
   passwd:         ldap compat
   Korisnici se u LDAP dodaju preko standardne 'ldapmodify' naredbe (postoji
 "kuharica" na webu: http://cmung.cmu.carnet.hr).
   Vjerojatno se navedeno moglo i elegantnije izvesti, ali bitan ograničavajući
 čimbenik je bilo vrijeme, kako bi korisnici što prije mogli početi rabiti e-mail i CMU sustav, kao i ažurirati vlastite web stranice.
   NAPOMENA: Prije svih operacija potrebno je napraviti backup svih datoteka
 koje se mijenjaju: /etc/passwd, /etc/shadow itd. Skripte ne moraju nužno odmah proraditi kod vas, jer su pisane za specifične potrebe. No, u slučaju potrebe, s lakoćom se mogu modificirati jer su pisane u perlu.
   DISCLAIMER: Sve navedene skripte i upute koristite na vlastitu odgovornost!
   ------------
 # pam-fix.sh
 # Author: zelja@zelja.com  Wed, 4 Feb 2004 22:43:15 +0100
 # Script adjusts PAM files so that every service tries to authenticate
 # against LDAP first. Failing that, it tries previous configuration. 
 # Script makes backup files, but make your own backup first.
 # Use at your own risk!
 set -e
   cd /etc/pam.d
 perl -pi.orig -e 's/^(.*)(required)(.*pam_unix.*so.*)$/#$1$2$3\n$1sufficient$3\n$1required\tpam_ldap.so\n/g;' $1
   ----------
 # users_rescue.pl
 #!/usr/bin/perl
 # Author: zelja@zelja.com  Wed, 4 Feb 2004 22:43:15 +0100
 # Script recreates /etc/passwd from LDIF file, preserving UIDs found on system
 # Create $base64 file with:
 # egrep '(cn:|uid:)' < organization.ldif > usernames.base64
 # Create $uids file with:
 # ls -l /home | awk '{print $3, $9}' > uids.txt
 # DO backup before running this script.
 # Use at your own risk!
   # Numerical value of 'users' group on linux. Modify according to your system.
 $defgid = "100";
 $uids = "uids.txt";
 $base64 = "usernames.base64";
 $pisi = 0;
 %UIDS = ();
 %USERNAMES = ();
   # read uids in field UIDS
 open(UIDS, "<$uids") or die "No \'$uids\' file!";
 while (<UIDS>) {
 	chomp ($_);
 	($uid, $username) = split;
 	$UIDS{$username} = $uid;
 	$USERNAMES{$uid} = $username;
 }
 close UIDS;
 undef $uid, $username;
   # read uids and names from ldif
 open (B64, "<$base64") or die "No \'$base64\' file!";
 while (<B64>) {
 chomp ($_);
 ($what, $value) = split(/: /);
 $name = $value;
   next unless ($what eq "uid" || $what =~ "cn:");
   # if $what looks like 'cn::' don't decode
 if ($what eq "cn::") { 
 	$decoded = $name; 
 } 
 elsif ($what eq "cn:") 
 {
 	use MIME::Base64;
 	$decoded = decode_base64($name);
 	$decoded =~ s/�/c/g;	#meko ch
 	$decoded =~ s/�/c/g;	#ch
 	$decoded =~ s/�/dj/g;	#dj
 	$decoded =~ s/�/C/g;	#meko CH
 	$decoded =~ s/�/C/g;	#CH
 	$decoded =~ s/Ĺ /S/g;	#SH
 	$decoded =~ s/Ĺž/z/g;	#zh
 	$decoded =~ s/ĹĄ/s/g;	#sh
 	$decoded =~ s/�/Dj/g;	#DJ
 	$decoded =~ s/Ĺ˝/Z/g;	#ZH
 	$decoded =~ s/ĂŚ/c/g;	#meko ch 2
 	$decoded =~ s/Ăź/ue/g;	#u s prijeglasom
 }
     # read only users found in 'uids.txt' file
 if (($what eq "uid") && ($UIDS{$value} ne "")) {
 	$line = "$value:x:"; 
 	$line .= "$UIDS{$value}\:";
 	$line .= "$defgid:";
 	$line .= "$decoded\:";
 	$line .= "/home/$name:/bin/bash";
 	print "$line\n";
 	undef $line;
 	}
 }
   exit 0;
     ------------
 # pass_rescue.pl
 #!/usr/bin/perl
 # Author: zelja@zelja.com  Wed, 4 Feb 2004 22:43:15 +0100
 # Script tries to recreate /etc/shadow from LDIF file
 # Create $base64 file with: 
 # egrep '(cn:|uid:)' < organization.ldif > usernames.base64
 # DO backup before running this script.
 # Use at your own risk! This script is not tested in production!
   use MIME::Base64;
 $ldif = "organization.ldif";
 $base64 = "usernames.base64";
   %USERS = ();
 %PASS = ();
   open(USERS, "<$ldif") or die "No \'$ldif\' file!";
 while (<USERS>) {
 	if (/uid:/) { 
 		chomp ($_);
 		($dummy, $user) = split;
 		$USERS{$user} = decode_base64($pass);
 		# 12400 = number of days between epoch and last pass change
 		print "$user:$USERS{$user}:12400::::::\n";
 	} elsif (/userPassword:/) {
 	($dummy, $pass) = split;
 }
   }
   exit 0;
   # THE REST IS JUST AN EXAMPLE, MODIFY IF NEEDED (AND REMOVE exit 0; 
 open (B64, "<$base64");
 while (<B64>) {
 chomp ($_);
 ($prvi, $ime) = split(/: /);
   if ($prvi eq "uid") { 
 	$username = $ime;
 	undef $ime;
 	$line = "$username:x:"; 
 	if ($UIDS{$username} eq "") { 
 		$line .= "NOUID\:";
 	} else {
 		$line .= "$UIDS{$username}\:";
 	}
 	$line .= "100:";
 	next;
 }
   if ($prvi ne "cn:") { $decoded = $ime; goto OUT; }
   use MIME::Base64;
 $decoded = decode_base64($ime);
 $decoded =~ s/�/c/g;	#meko ch
 $decoded =~ s/�/c/g;	#ch
 $decoded =~ s/�/dj/g;	#dj
 $decoded =~ s/�/C/g;	#meko CH
 $decoded =~ s/�/C/g;	#CH
 $decoded =~ s/Ĺ /S/g;	#SH
 $decoded =~ s/Ĺž/z/g;	#zh
 $decoded =~ s/ĹĄ/s/g;	#sh
 $decoded =~ s/�/Dj/g;	#DJ
 $decoded =~ s/Ĺ˝/Z/g;	#ZH
   $decoded =~ s/ĂŚ/c/g;	#meko ch 2
 $decoded =~ s/Ăź/ue/g;	#u s prijeglasom
   OUT:
 #print "uid=$uid, $UIDOVI{$username}, $UIDS{$username}, $username\n";
 $line .= "$decoded\:";
 $line .= "/home/$username:/bin/bash";
 print "$line\n";
   }
   
 
 |