diff --git a/xCAT-server/lib/perl/xCAT/ADUtils.pm b/xCAT-server/lib/perl/xCAT/ADUtils.pm index eb7e44016..4e38b1659 100644 --- a/xCAT-server/lib/perl/xCAT/ADUtils.pm +++ b/xCAT-server/lib/perl/xCAT/ADUtils.pm @@ -5,43 +5,192 @@ #there exists direct perl ldap module, but too rare to bank on, going to use ldapmodify from #system calls - +package xCAT::ADUtils; use strict; use MIME::Base64; use Encode; use xCAT::Utils qw/genpassword/; use IPC::Open3; use IO::Select; +use Symbol qw/gensym/; -my $machineldiftemplate = 'dn: CN=##UPCASENODENAME##,##OU## +my $machineldiftemplate = 'dn: CN=##NODENAME##,##OU## changetype: add objectClass: top objectClass: person objectClass: organizationalPerson objectClass: user objectClass: computer -cn: ##UPCASENODENAME## -distinguishedName: CN=##UPCASENODENAME##,##OU## +cn: ##NODENAME## +distinguishedName: CN=##NODENAME##,##OU## objectCategory: CN=Computer,CN=Schema,CN=Configuration##REALMDCS## instanceType: 4 -displayName: ##UPCASENODENAME##$ -name: ##UPCASENODENAME## +displayName: ##NODENAME##$ +name: ##NODENAME## userAccountControl: 4096 codePage: 0 countryCode: 0 accountExpires: 0 -sAMAccountName: ##UPCASENODENAME##$ -dNSHostName: ##UPCASENODENAME####DNSDOMAIN## -servicePrincipalName: HOST/##UPCASENODENAME## -servicePrincipalName: HOST/##UPCASENODENAME####DNSDOMAIN## +sAMAccountName: ##NODENAME##$ +dNSHostName: ##NODENAME####DNSDOMAIN## +servicePrincipalName: HOST/##NODENAME## +servicePrincipalName: HOST/##NODENAME####DNSDOMAIN## -dn: CN=##UPCASENODENAME##,##OU## +dn: CN=##NODENAME##,##OU## changetype: modify replace: unicodePwd unicodePwd::##B64PASSWORD##'; +my $useraccounttemplate = 'dn: CN=##FULLNAME##,##OU## +changetype: add +objectClass: top +objectClass: person +objectClass: organizationalPerson +objectClass: user +objectCategory: CN=Person,CN=Schema,CN=Configuration##REALMDCS## +codePage: 0 +countryCode: 0 +distinguishedName: CN=##FULLNAME##,##OU## +cn: ##FULLNAME## +sn: ##LASTNAME## +givenName: ##FIRSTNAME## +displayName: ##FULLNAME## +name: ##FULLNAME## +instanceType: 4 +userAccountControl: 514 +accountExpires: 0 +uidNumber: ##UID## +gidNumber: ##GID## +sAMAccountName: ##USERNAME## +userPrincipalName: ##USERNAME##@##DNSDOMAIN## +mail: ##USERNAME##@##DNSDOMAIN## +homeDirectory: ##USERSMBHOME## +homeDrive: ##USERSMBDRIVELETTER## +unixHomeDirectory: ##USERHOME## +loginShell: ##USERSHELL## + +dn: CN=##FULLNAME##,##OU## +changetype: modify +replace: unicodePwd +unicodePwd::##B64PASSWORD## + +dn: CN=##FULLNAME##,##OU## +changetype: modify +replace: userAccountControl +userAccountControl: 512'; +=cut + example: add_user_account(username=>'fred',fullname=>'fred the great'); +=cut +sub add_user_account { + my %args = @_; + my $dnsdomain = $args{dnsdomain}; + unless ($dnsdomain =~ /^\./) { + $dnsdomain = '.'.$dnsdomain; + } + my $directoryserver = $args{directoryserver}; + my $domain_components = $dnsdomain; + $domain_components =~ s/\./,dc=/g; + unless ($domain_components =~ /^,dc=/) { + $domain_components = ",dc=".$domain_components; + } + my $ou = $args{ou}; + if ($ou) { + unless ($ou =~ /$domain_components\z/) { + $ou .= $domain_components; + } + } else { + $ou = "CN=Users".$domain_components; + } + unless ($domain_components and $dnsdomain and $ou and $directoryserver) { + return {error=>"Unable to determine all required parameters"}; + } + my $newpassword = $args{password}; + unless ($newpassword) { + $newpassword = '"'.genpassword(8).'"'; + } + Encode::from_to($newpassword,"utf8","utf16le"); #ms uses utf16le, we use utf8 + my $b64password = encode_base64($newpassword); + my $username = $args{username}; + my $fullname; + if ($args{fullname}) { + $fullname = $args{fullname}; + } else { + $fullname = $username; + } + my $firstname; + if ($args{firstname}) { + $firstname = $args{firstname}; + } else { + $firstname = $fullname; + $firstname =~ s/ .*//; #remove anything after any space + } + my $lastname; + if ($args{lastname}) { + $firstname = $args{lastname}; + } else { + $lastname = $fullname; + $lastname =~ s/.* //; #remove anything before any space + } + my $gid; + my $uid; + if ($args{gid}) { + $gid = $args{gid}; + } else { + $gid = 100; #TODO, something more generic? + } + if ($args{uid}) { + $uid = $args{uid}; + } else { + my $base = $domain_components; + $base =~ s/^,//; + my $parms = find_free_params(directoryserver=>$directoryserver,ou=>$base); + unless ($parms) { + return { error => "Unable to auto-detect a valid uid" }; + } + $uid = $parms->{uidNumber}; + } + my $shell = $args{shell}; + unless ($shell) { $shell = "/bin/bash"; } + my $uhome = $args{homedir}; + unless ($uhome) { $uhome = "/home/".$username; } + my $ldif = $useraccounttemplate; + if ($args{smbhome} and $args{smbhomeletter}) { + my $smbhome = $args{smbhome}; + my $smbhomeletter = $args{smbhomeletter}; + $ldif =~ s/##USERSMBHOME##/$smbhome/g; + $ldif =~ s/##USERSMBDRIVELETTER##/$smbhome/g; + } else { + $ldif =~ s/homeDirectory: ##USERSMBHOME## +//g; + $ldif =~ s/homeDrive: ##USERSMBDRIVELETTER## +//g; + } + $ldif =~ s/##FULLNAME##/$fullname/g; + $ldif =~ s/##USERNAME##/$username/g; + $ldif =~ s/##FIRSTNAME##/$firstname/g; + $ldif =~ s/##LASTNAME##/$lastname/g; + $ldif =~ s/##OU##/$ou/g; + $ldif =~ s/##DNSDOMAIN##/$dnsdomain/g; + $ldif =~ s/##REALMDCS##/$domain_components/g; + $ldif =~ s/##UID##/$uid/g; + $ldif =~ s/##GID##/$gid/g; + $ldif =~ s/##USERHOME##/$uhome/g; + $ldif =~ s/##USERSHELL##/$shell/g; + $ldif =~ s/##B64PASSWORD##/$b64password/g; + my $dn = "CN=$fullname,$ou"; + my $rc = system("ldapsearch -H ldaps://$directoryserver -b \"$dn\""); + if ($rc == 0) { + return {error=>"User already exists"}; + } elsif (not $rc==8192) { + return {error=>"Unknown error $rc"}; + } + open(HUH,">","/tmp/huhh"); + print HUH $ldif; + $rc = system("echo '$ldif'|ldapmodify -H ldaps://$directoryserver"); + return {password=>$newpassword}; +} =cut add_machine_account Arguments are in a hash: @@ -50,7 +199,6 @@ Arguments are in a hash: sub add_machine_account { my %args = @_; my $nodename = $args{node}; - my $ou = $args{ou}; my $dnsdomain = $args{dnsdomain}; if (not $dnsdomain and $nodename =~ /\./) { #if no domain provided, guess from nodename $dnsdomain = $nodename; @@ -60,12 +208,12 @@ sub add_machine_account { $dnsdomain = '.'.$dnsdomain; } $nodename =~ s/\..*//; #strip dns domain if part of nodename - my $upnodename = uc($nodename); my $domain_components = $dnsdomain; $domain_components =~ s/\./,dc=/g; unless ($domain_components =~ /^,dc=/) { $domain_components = ",dc=".$domain_components; } + my $ou = $args{ou}; if ($ou) { unless ($ou =~ /$domain_components\z/) { $ou .= $domain_components; @@ -74,7 +222,7 @@ sub add_machine_account { $ou = "CN=Computers".$domain_components; } my $directoryserver = $args{directoryserver}; - unless ($domain_components and $dnsdomain and $ou and $upnodename and $directoryserver) { + unless ($domain_components and $dnsdomain and $ou and $nodename and $directoryserver) { return {error=>"Unable to determine all required parameters"}; } my $newpassword = $args{password}; @@ -88,17 +236,53 @@ sub add_machine_account { $ldif =~ s/##OU##/$ou/g; $ldif =~ s/##REALMDCS##/$domain_components/g; $ldif =~ s/##DNSDOMAIN##/$dnsdomain/g; - $ldif =~ s/##UPCASENODENAME##/$upnodename/g; - my $dn = "CN=$upnodename,$ou"; + $ldif =~ s/##NODENAME##/$nodename/g; + my $dn = "CN=$nodename,$ou"; my $rc = system("ldapsearch -H ldaps://$directoryserver -b $dn"); if ($rc == 0) { return {error=>"System already exists"}; } elsif (not $rc==8192) { return {error=>"Unknown error $rc"}; } + open(HUH,">","/tmp/huhh"); + print HUH $ldif; $rc = system("echo '$ldif'|ldapmodify -H ldaps://$directoryserver"); return {password=>$newpassword}; } + +sub krb_login { + #TODO: klist -s to see if credentials are good + my %args = @_; + my $password = $args{password}; + my $username = $args{username}; + my $realm = $args{realm}; + my $krbin; + my $krbout; + my $krberr = gensym; + my $kinit = open3($krbin,$krbout,$krberr,qw/kinit/,$username."@".$realm); + my $ksel = IO::Select->new($krbout,$krberr); + my @handles; + while (@handles = $ksel->can_read()) { + foreach (@handles) { + my $line; + my $done = sysread $_,$line,180; + unless ($done) { + $ksel->remove($_); + } + if ($line =~ /Password for /) { + print $krbin $password."\n"; + } + } + } + if (waitpid($kinit,0)) { + return $?; + } else { + die "Bug, $kinit got reaped before we could get to it\n"; + } +} + + + sub find_free_params { #search for things like next available uidNumber my %args = @_; my @needed_parms = split /,/,$args{needed_params}; @@ -112,11 +296,12 @@ sub find_free_params { #search for things like next available uidNumber my $dc = $args{ou}; my $ldapout; my $ldapin; - my $ldaperr; + my $ldaperr=gensym; my $ldappid = open3($ldapin,$ldapout,$ldaperr,qw!ldapsearch -H !,"ldaps://$directoryserver","-b","$dc",qw!(uidNumber=*) uidNumber!); my $select = IO::Select->new($ldapout,$ldaperr); my @handles; my %useduids=(); + my $failure; while (@handles = $select->can_read()) { foreach (@handles) { my $line = <$_>; @@ -124,12 +309,18 @@ sub find_free_params { #search for things like next available uidNumber $select->remove($_); next; } - if ($line =~ /^uidNumber: (\d+)$/) { + if ($_ == $ldaperr) { + if ($line =~ /error/i or $line =~ /problem/i) { + print $line; + $failure=1; + } + } elsif ($line =~ /^uidNumber: (\d+)$/) { $useduids{$1}=1; - } + } } } + if ($failure) { return undef; } while (1) { #loop through until 'return' unless ($useduids{$uidnumber}) { return {uidNumber=>$uidnumber}; @@ -140,4 +331,8 @@ sub find_free_params { #search for things like next available uidNumber use Data::Dumper; -print Dumper(find_free_params(directoryserver=>"v4.xcat.e1350",ou=>"dc=xcat,dc=e1350")); +#print krb_login(username=>"Administrator",password=>"cluster",realm=>"XCAT.E1350"); +#print Dumper(find_free_params(directoryserver=>"v4.xcat.e1350",ou=>"dc=xcat,dc=e1350")); +#print Dumper add_user_account(dnsdomain=>'xcat.e1350',username=>'ffuu',directoryserver=>'v4.xcat.e1350'); +#print Dumper add_machine_account(node=>'ufred.xcat.e1350',directoryserver=>'v4.xcat.e1350'); +