--- /dev/null
+#!/usr/bin/perl -w
+
+# Created by P.Wieleba@iem.pw.edu.pl in 2004
+
+use strict;
+use Getopt::Std;
+use FindBin;
+use FindBin qw($RealBin);
+use lib "$RealBin/";
+use smbldap_tools;
+
+# function declaration
+sub migrate_user;
+sub migrate_shadow_user;
+sub get_user_entry;
+sub exist_in_tab;
+sub del_from_tab;
+sub add_to_tab;
+sub read_shadow_file;
+
+# smbldap-migrate-unix-accounts (-? or -h for help)
+#
+#
+
+my %Options;
+
+my $ok = getopts('M:P:S:vn?hd:a', \%Options);
+
+if ( (!$ok) || ($Options{'?'}) || ($Options{'h'}) || (!keys(%Options)) ) {
+ print "Usage: $0 [-PSMvn?hda]\n";
+ print " -?|-h show this help message\n";
+ print " -P file import passwd file\n";
+ print " -S file import shadow file\n";
+ print " -M file import FreeBSD master.passwd\n";
+ print " -v displays modified entries to STDOUT\n";
+ print " -n do everything execpt updating LDAP\n";
+ print " -d obj_nam delete and add (not just update) existing entry in LDAP\n";
+ print " -a adds sambaSamAccount objectClass\n";
+ exit (1);
+}
+
+my $INFILE = undef;
+my %shadowUsers;
+
+if ( $Options{'M'} ) {
+ open($INFILE,$Options{'M'}) or
+ die "I cannot open file: " . $Options{'M'} . "\n";
+} elsif ( $Options{'P'} ) {
+ open($INFILE,$Options{'P'}) or
+ die "I cannot open file: " . $Options{'P'} . "\n";
+ # if defined -S option also read shadow file
+ if ( $Options{'S'} ) {
+ %shadowUsers = read_shadow_file($Options{'S'});
+ (%shadowUsers) or ( close($INFILE) and
+ die "I cannot open file: " . $Options{'S'} . "\n" );
+ }
+} elsif ( $Options{'S'} ) {
+ open($INFILE,$Options{'S'}) or
+ die "I cannot open file: " . $Options{'S'} . "\n";
+}
+
+my $ldap_master=connect_ldap_master();
+
+while ( my $line=<$INFILE> ) {
+ chop($line);
+ next if ( $line =~ /^\s*$/ ); # whitespace
+ next if ( $line =~ /^#/ );
+ next if ( $line =~ /^\+/ );
+ my $entry = undef;
+ if ($Options{'M'}) {
+ my($user,$pwd,$uid,$gid,$class,$change,$expire,$gecos,$homedir,$shell) = split(/:/,$line);
+ # if user is not in LDAP new entry will be created
+ $entry = get_user_entry($ldap_master,$user);
+ $entry = migrate_user($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell);
+ # for master.passwd file (nss_ldap)
+ if ($entry) {
+ my @objectClass = $entry->get_value( 'objectClass' );
+ $entry->replace( 'objectClass' => [add_to_tab(\@objectClass,'shadowAccount')] );
+ }
+ } elsif ($Options{'P'}) {
+ my($user,$pwd,$uid,$gid,$gecos,$homedir,$shell) = split(/:/,$line);
+ # if user is not in LDAP new entry will be created
+ $entry = get_user_entry($ldap_master,$user);
+ $entry = migrate_user($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell,undef);
+
+ # should I delete next functionality
+ # add shadow entries if also -S defined
+ if ($Options{'S'} and $shadowUsers{$user}) {
+ my($user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag) = split(/:/,$shadowUsers{$user});
+ $entry = migrate_shadow_user($entry,$user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag);
+ }
+ } elsif ($Options{'S'}) {
+ my($user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag)=split(/:/,$line);
+ # if user is not in LDAP new entry will be created
+ $entry = get_user_entry($ldap_master,$user);
+ $entry = migrate_shadow_user($entry,$user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag);
+ }
+
+ if ($entry) {
+ # objectClass $Options{'d'} will be removed
+ # from entry if it exists
+ if ($Options{'d'}) {
+ my @objectClass = $entry->get_value( 'objectClass' );
+ $entry->replace( 'objectClass' => [del_from_tab(\@objectClass,$Options{'d'})] );
+ #$entry->delete( 'objectClass' => [ $Options{'d'} ] );
+ }
+ # if used "-a" and sambaSamAccount doesn't exist.
+ if ( $Options{'a'} and !exist_in_tab([$entry->get_value('objectClass')],'sambaSamAccount') ) {
+ my @objectClass = $entry->get_value( 'objectClass' );
+ $entry->replace( 'objectclass' => [add_to_tab(\@objectClass,'sambaSamAccount')] );
+
+ # the below part comes from smbldap-useradd and
+ # maybe it should be replaced by a new subroutine.
+ my $userUidNumber = $entry->get_value('uidNumber');
+ # as rid we use 2 * uid + 1000
+ my $userRid = 2 * $userUidNumber + 1000;
+ # let's test if this SID already exist
+ my $user_sid = "$config{SID}-$userRid";
+ my $test_exist_sid = does_sid_exist($user_sid,$config{usersdn});
+ if ($test_exist_sid->count == 1) {
+ print "User SID already owned by\n";
+ # there should not exist more than one entry, but ...
+ foreach my $entry ($test_exist_sid->all_entries) {
+ my $dn= $entry->dn;
+ chomp($dn);
+ print "$dn\n";
+ }
+ } else {
+ $entry->replace( 'sambaSID' => $user_sid );
+ }
+ }
+ if ($Options{'v'}) {
+ $entry->dump();
+ }
+ if (!$Options{'n'}) {
+ my $mesg;
+ if ( $Options{'d'} ) {
+ # delete entry from LDAP if it exists
+ $mesg = $ldap_master->search( base => $entry->dn(),
+ scope => 'sub',
+ filter => '(objectClass=*)'
+ );
+ if ( $mesg->count() == 1 ) {
+ $mesg = $ldap_master->delete($entry->dn());
+ if ($mesg->is_error()) {
+ print "Error: " . $mesg->error() . "\n";
+ }
+ $entry->changetype('add');
+ }
+ }
+ $mesg = $entry->update($ldap_master);
+ if ($mesg->is_error()) {
+ print "Error: " . $mesg->error() . "\n";
+ }
+ }
+ }
+}
+
+$INFILE and close($INFILE);
+# take down the session
+$ldap_master and $ldap_master->unbind;
+
+# returns updated $entry
+sub migrate_user
+ {
+ my($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell) = @_;
+ my($name,$office,$wphone,$hphone)=split(/,/,$gecos);
+ my($cn);
+
+ # posixAccount MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
+ my @objectClass = $entry->get_value( 'objectClass' );
+ @objectClass = add_to_tab(\@objectClass,'posixAccount');
+ @objectClass = add_to_tab(\@objectClass,'inetOrgPerson');
+ $entry->replace( 'objectClass' => \@objectClass );
+
+ $entry->replace( 'uid' => $user );
+ if ($name) {
+ $cn = $name;
+ } else {
+ $cn = $user;
+ }
+ $entry->replace( 'cn' => $cn );
+ # perhaps I should delete it
+ if ( exist_in_tab(\@objectClass,'inetOrgPerson') ) {
+ # 'sn' is required by person objectClass from core.schema
+ my @tmp = split(/\s+/,$cn);
+ my $sn = $tmp[$#tmp];
+ $entry->replace( 'sn' => $sn );
+ # perhaps 'telephoneNumber' 'roomNumber' 'homePhone'
+ # and 'givenName' also should be modified ???????
+ }
+ ($pwd) and $entry->replace( 'userPassword' => "{crypt}" . $pwd );
+ ($uid ne "") and $entry->replace( 'uidNumber' => $uid );
+ ($gid ne "") and $entry->replace( 'gidNumber' => $gid );
+ ($gecos) and $entry->replace( 'gecos' => $gecos );
+ ($homedir) and $entry->replace( 'homeDirectory' => $homedir );
+ ($shell) and $entry->replace( 'loginShell' => $shell );
+
+ return $entry;
+ }
+
+# returns updated $entry
+sub migrate_shadow_user
+ {
+ my($entry,$user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag) = @_;
+
+ # shadowAccount MUST uid
+ my @objectClass = $entry->get_value( 'objectClass' );
+ # if the entry doesn't exist, it needs structural objectclass
+ (@objectClass) or push(@objectClass,'account');
+ $entry->replace( 'objectClass' => [add_to_tab(\@objectClass,'shadowAccount')] );
+
+ $entry->replace( 'uid' => $user );
+ ($pwd) and $entry->replace( 'userPassword' => "{crypt}" . $pwd );
+ ($lastchg) and $entry->replace( 'shadowLastChange' => $lastchg );
+ ($min) and $entry->replace( 'shadowMin' => $min );
+ ($max) and $entry->replace( 'shadowMax' => $max );
+ ($warn) and $entry->replace( 'shadowWarning' => $warn );
+ ($inactive) and $entry->replace( 'shadowInactive' => $inactive );
+ ($expire) and $entry->replace( 'shadowExpire' => $expire );
+ ($flag) and $entry->replace( 'shadowFlag' => $flag );
+
+ return $entry;
+ }
+
+# creates a _new_entry_ if user doesn't exist in ldap
+# else return's ldap user entry
+sub get_user_entry
+ {
+ my($ldap_master,$user) = @_;
+
+ # do not use read_user_entry()
+ my $mesg = $ldap_master->search( base => $config{usersdn},
+ scope => 'one',
+ filter => "(uid=$user)"
+ );
+ my $entry;
+ if ( $mesg->count() != 1 ) {
+ $entry = Net::LDAP::Entry->new();
+ $entry->dn("uid=$user,$config{usersdn}");
+ } else {
+ $entry = $mesg->entry(0); # ????
+ }
+ return $entry;
+ }
+
+# Check if a $text element exists in @table
+# eg. exist_in_tab(\@table,$text);
+sub exist_in_tab
+ {
+ my($ref_tab,$text) = @_;
+ my @tab = @$ref_tab;
+
+ foreach my $elem (@tab) {
+ if ( lc($elem) eq lc($text) ) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+# Delete $text element from @table
+# eg. del_from_tab(\@table,$text);
+sub del_from_tab
+ {
+ my($ref_tab,$text) = @_;
+ my @tab = @$ref_tab;
+ my @new_tab;
+
+ foreach my $elem (@tab) {
+ if ( lc($elem) ne lc($text) ) {
+ push(@new_tab,$elem);
+ }
+ }
+ return @new_tab;
+ }
+
+# Add $text to tab if it doesn't exist there
+sub add_to_tab
+ {
+ my($ref_tab,$text) = @_;
+ my @tab = @$ref_tab;
+
+ if ( !exist_in_tab(\@tab,$text) ) {
+ push(@tab,$text);
+ }
+ return @tab;
+ }
+
+# reads shadow file entries and places them in a hash
+sub read_shadow_file
+ {
+ my($shadow) = @_;
+
+ my $shadowUser;
+ my %shadowUsers;
+ open(SHADOW,$shadow) or
+ return ;
+ while (my $line=<SHADOW>) {
+ chop($line);
+ next if ( $line =~ /^\s*$/ ); # whitespace
+ next if ( $line =~ /^#/ );
+ ($shadowUser) = split(/:/, $line);
+ $shadowUsers{$shadowUser} = $line;
+ }
+ close(SHADOW);
+ return %shadowUsers;
+ }
+
+########################################
+
+=head1 NAME
+
+smbldap-migrate-unix-accounts - Migrate unix accounts to LDAP
+
+=head1 SYNOPSIS
+
+smbldap-migrate-unix-accounts [-P file] [-S file] [-M file] [-n] [-v]
+[-h] [-?] [-d]
+
+=head1 DESCRIPTION
+
+This command processes one file as defined by option and
+creates new or changes existing ldap user entry.
+New attributes are added, and existing are changed.
+None of the existing attributes is deleted.
+
+-P passwd_file
+ Processes passwd_file and uptades LDAP. Creates new ldap user
+ entry or just adds posixAccount objectclass and corresponding
+ attributes to the ldap user entry or just uptades their values.
+
+-S shadow_file
+ Reads shadow_file and uptades LDAP. Creates new ldap user
+ entry or just adds shadowAccount objectclass and corresponding
+ attributes to the ldap user entry or just uptades their values.
+
+-M master.passwd_file
+ Reads master.passwd_file and uptades LDAP. Creates new ldap user
+ entry or just adds shadowAccount and posixAccount objectclass
+ and corresponding attributes to the ldap user entry or just
+ uptades their values.
+
+-h show the help message
+
+-? the same as -h
+
+-v displayes modified entries to STDOUT
+
+-n do everything execpt updating LDAP. It is useful when used
+ with -v switch.
+
+-d objeClass_name
+ In spite of just updating existing user entry, the entry will be
+ deleted from LDAP and a new one will be added.
+ It is essential to use this option if you update users in LDAP
+ and want to change their structural objectClass.
+ Use it in the example schema:
+ There are no users in LDAP, and you migrate samba first.
+ # pdbedit -e ldapsam:ldap://localhost
+ # smbldap-migrate-passwd -P passwd -d 'account'
+
+-a adds sambaSamAccount objectClass and generates sambaSID attribute
+
+=cut
+
+#'
+
+# The End
+
+