#!/usr/bin/perl -w package esmith; use strict; use Errno; use esmith::ConfigDB; use esmith::AccountsDB; use esmith::util; use Net::LDAP; use Date::Parse; $ENV{'LANG'} = 'C'; $ENV{'TZ'} = ''; my $c = esmith::ConfigDB->open_ro; my $a = esmith::AccountsDB->open_ro; my $l = $c->get('ldap'); my $status = $l->prop('status') || "disabled"; unless ($status eq "enabled" ) { warn "Not running action script $0, LDAP service not enabled!\n"; exit(0); } exit(0) if ($c->get('ldap')->prop('Authentication') || 'disabled') eq 'enabled'; my $domain = $c->get('DomainName') || die("Couldn't determine domain name"); $domain = $domain->value; my $schema = '/etc/openldap/schema/samba.schema'; my $event = shift || die "Event name must be specified"; my @name = @ARGV; die "Account name argument missing." unless scalar (@name) >= 1; #------------------------------------------------------------ # Update LDAP database entry. #------------------------------------------------------------ my $base = esmith::util::ldapBase ($domain); my $pw = esmith::util::LdapPassword(); my $ldap = Net::LDAP->new('localhost') or die "$@"; $ldap->bind( dn => "cn=root,$base", password => $pw ); my @accounts; my $account; foreach my $name (@name) { $account = $a->get($name); die "Account $name not found.\n" unless defined $account; my $type = $account->prop('type') || "unknown"; die "Account $name is not a user, group, ibay, machine account; update LDAP entry failed.\n" unless ($type =~ m{^(?:user|group|ibay|machine)$} or $name eq 'admin'); push @accounts, $account; } #------------------------------------------------------------ # Read all samba groups (can't do individual lookups) #------------------------------------------------------------ my $groupmap = (); # Only do if schema is found if ( -f "$schema" and -x '/usr/bin/net' ) { foreach (`/usr/bin/net groupmap list 2> /dev/null`){ chomp; next if m{\(S-1-5-32-\d+\)}; $groupmap->{$3} = { name => "$1", sid => "$2" } if (/^(.*) \((S-.*-\d+)\) -> (.*)$/); } } #------------------------------------------------------------ # Create a list of updates that need to happen #------------------------------------------------------------ my $updates; foreach my $acct (@accounts) { my $key = $acct->key; my $type = $acct->prop('type'); my $desc = undef; my $dn; if ($type =~ m{^(?:user|group|ibay|machine)$} or $key eq 'admin') { #------------------------------------------------------------ # Do the user portion #------------------------------------------------------------ if ($type eq 'machine') { $dn = "uid=$key,ou=Computers,$base"; } else { $dn = "uid=$key,ou=Users,$base"; } utf8::upgrade($dn); # Read information from getent passwd @{$updates->{$dn}}{'uid','userPassword'} = getpwnam($key); unless ($updates->{$dn}->{uid}) { delete $updates->{$dn}; next; } $updates->{$dn}->{userPassword} = "!*" if $updates->{$dn}->{userPassword} eq '!!'; $updates->{$dn}->{userPassword} =~ s/^/{CRYPT}/ unless $updates->{$dn}->{userPassword} =~ m/^{/; # Samba parameters if we find the samba.schema if ( -f "$schema" and -x '/usr/bin/pdbedit' ) { my $line = `/usr/bin/pdbedit -wu '$key' 2> /dev/null`; chomp($line); if ($line) { @{$updates->{$dn}}{'junk','junk','sambaLMPassword','sambaNTPassword'} = split(/:/,$line); foreach $line (`/usr/bin/pdbedit -vu '$key' 2> /dev/null`) { chomp($line); $updates->{$dn}->{sambaSID} = $1 if $line =~ m{User SID:\s+(S-.*)$}; $updates->{$dn}->{displayName} = $1 if $line =~ m{Full Name:\s+(.*)$}; $updates->{$dn}->{sambaPrimaryGroupSID} = $1 if $line =~ m{Primary Group SID:\s+(S-.*)$}; $updates->{$dn}->{sambaAcctFlags} = $1 if $line =~ m{Account Flags:\s+(.*)$}; $updates->{$dn}->{sambaPwdLastSet} = str2time($1) if $line =~ m{Password last set:\s+(.*)$}; } push @{$updates->{$dn}->{objectClass}}, 'sambaSamAccount'; } else { $updates->{$dn}->{sambaLMPassword} = []; $updates->{$dn}->{sambaNTPassword} = []; $updates->{$dn}->{sambaSID} = []; $updates->{$dn}->{displayName} = []; $updates->{$dn}->{sambaPrimaryGroupSID} = []; $updates->{$dn}->{sambaAcctFlags} = []; $updates->{$dn}->{sambaPwdLastSet} = []; } } } } endpwent(); #------------------------------------------------------------ # Do the group portion (only if we have samba) #------------------------------------------------------------ if ( -f "$schema" ) { foreach my $group ( (map { $_->key } $a->users), (map { $_->key } $a->groups), qw/admin nobody shared/ ){ my $dn = "cn=$group,ou=Groups,$base"; utf8::upgrade($dn); if ( exists $groupmap->{$group} ) { push @{$updates->{$dn}->{objectClass}}, 'sambaGroupMapping'; $updates->{$dn}->{displayName} = $groupmap->{$group}->{name}; $updates->{$dn}->{sambaSID} = $groupmap->{$group}->{sid}; $updates->{$dn}->{sambaGroupType} = '2'; } else { $updates->{$dn}->{displayName} = []; $updates->{$dn}->{sambaSID} = []; $updates->{$dn}->{sambaGroupType} = []; } } } #------------------------------------------------------------ # Update LDAP database entry. #------------------------------------------------------------ foreach my $dn (keys %$updates) { # Try and find record my $result = $ldap->search( base => $dn, filter => '(objectClass=*)', scope => 'base' ); warn "failed looking up entry $dn: ", $result->error if $result->code && $result->code != 32; my $code = $result->code; my @objectClass = $code == 32 ? () : $result->entry(0)->get_value('objectClass'); # Clean up attributes and convert to utf8 delete $updates->{$dn}->{'junk'}; foreach my $attr ( keys %{$updates->{$dn}} ) { if ( ref($updates->{$dn}->{$attr}) eq 'ARRAY' ) { if ( $code == 32 and scalar(@{$updates->{$dn}->{$attr}}) == 0 ) { delete $updates->{$dn}->{$attr}; } else { for (my $c = 0; $c < scalar(@{$updates->{$dn}->{$attr}}); $c++) { utf8::upgrade($updates->{$dn}->{$attr}[$c]); } } } else { if ($updates->{$dn}->{$attr} !~ /^\s*$/) { utf8::upgrade($updates->{$dn}->{$attr}); } elsif ( $code == 32 ) { delete $updates->{$dn}->{$attr}; } else { $updates->{$dn}->{$attr} = []; } } } # Perform insert or update if ( $code == 32 ) { $result = $ldap->add( $dn, attrs => [ %{$updates->{$dn}} ] ); $result->code && warn "failed to add entry $dn: ", $result->error; } else { # Don't overwrite objectClass (just update if necessary) my $seen = (); # Remove samba objectClasses if removing samba attributes @{$seen}{'sambaSamAccount','sambaGroupMapping'} = (1,1) if ref($updates->{$dn}->{sambaSID}) eq 'ARRAY'; @{$updates->{$dn}->{objectClass}} = grep { ! $seen->{$_}++ } (@{$updates->{$dn}->{objectClass}}, @objectClass ); $result = $ldap->modify( $dn, replace => $updates->{$dn}); $result->code && warn "failed to modify entry $dn: ", $result->error; } } $ldap->unbind; exit (0);