smbldap-migrate-unix-accounts [plain text]
use strict;
use Getopt::Std;
use FindBin;
use FindBin qw($RealBin);
use lib "$RealBin/";
use smbldap_tools;
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;
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 ( $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*$/ ); 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);
$entry = get_user_entry($ldap_master,$user);
$entry = migrate_user($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell);
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);
$entry = get_user_entry($ldap_master,$user);
$entry = migrate_user($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell,undef);
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);
$entry = get_user_entry($ldap_master,$user);
$entry = migrate_shadow_user($entry,$user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag);
}
if ($entry) {
if ($Options{'d'}) {
my @objectClass = $entry->get_value( 'objectClass' );
$entry->replace( 'objectClass' => [del_from_tab(\@objectClass,$Options{'d'})] );
}
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')] );
my $userUidNumber = $entry->get_value('uidNumber');
my $userRid = 2 * $userUidNumber + 1000;
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";
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'} ) {
$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);
$ldap_master and $ldap_master->unbind;
sub migrate_user
{
my($entry,$user,$pwd,$uid,$gid,$gecos,$homedir,$shell) = @_;
my($name,$office,$wphone,$hphone)=split(/,/,$gecos);
my($cn);
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 );
if ( exist_in_tab(\@objectClass,'inetOrgPerson') ) {
my @tmp = split(/\s+/,$cn);
my $sn = $tmp[$ $entry->replace( 'sn' => $sn );
}
($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;
}
sub migrate_shadow_user
{
my($entry,$user,$pwd,$lastchg,$min,$max,$warn,$inactive,$expire,$flag) = @_;
my @objectClass = $entry->get_value( '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;
}
sub get_user_entry
{
my($ldap_master,$user) = @_;
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;
}
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;
}
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;
}
sub add_to_tab
{
my($ref_tab,$text) = @_;
my @tab = @$ref_tab;
if ( !exist_in_tab(\@tab,$text) ) {
push(@tab,$text);
}
return @tab;
}
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*$/ ); 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