2010-02-08 22:08:34 +00:00
|
|
|
# IBM(c) 2010 EPL license http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
|
|
|
|
#This module provides simplified methods for adding machine/user accounts to active directory,
|
|
|
|
#as well as setting passwords for aforementioned accounts.
|
|
|
|
|
|
|
|
#there exists direct perl ldap module, but too rare to bank on, going to use ldapmodify from
|
|
|
|
#system calls
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use MIME::Base64;
|
|
|
|
use Encode;
|
|
|
|
use xCAT::Utils qw/genpassword/;
|
2010-02-15 22:18:37 +00:00
|
|
|
use IPC::Open3;
|
|
|
|
use IO::Select;
|
2010-02-08 22:08:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
my $machineldiftemplate = 'dn: CN=##UPCASENODENAME##,##OU##
|
|
|
|
changetype: add
|
|
|
|
objectClass: top
|
|
|
|
objectClass: person
|
|
|
|
objectClass: organizationalPerson
|
|
|
|
objectClass: user
|
|
|
|
objectClass: computer
|
2010-02-15 20:53:22 +00:00
|
|
|
cn: ##UPCASENODENAME##
|
2010-02-08 22:08:34 +00:00
|
|
|
distinguishedName: CN=##UPCASENODENAME##,##OU##
|
|
|
|
objectCategory: CN=Computer,CN=Schema,CN=Configuration##REALMDCS##
|
|
|
|
instanceType: 4
|
|
|
|
displayName: ##UPCASENODENAME##$
|
|
|
|
name: ##UPCASENODENAME##
|
|
|
|
userAccountControl: 4096
|
|
|
|
codePage: 0
|
|
|
|
countryCode: 0
|
|
|
|
accountExpires: 0
|
|
|
|
sAMAccountName: ##UPCASENODENAME##$
|
|
|
|
dNSHostName: ##UPCASENODENAME####DNSDOMAIN##
|
|
|
|
servicePrincipalName: HOST/##UPCASENODENAME##
|
|
|
|
servicePrincipalName: HOST/##UPCASENODENAME####DNSDOMAIN##
|
|
|
|
|
|
|
|
dn: CN=##UPCASENODENAME##,##OU##
|
|
|
|
changetype: modify
|
|
|
|
replace: unicodePwd
|
|
|
|
unicodePwd::##B64PASSWORD##';
|
|
|
|
|
|
|
|
|
|
|
|
=cut
|
|
|
|
add_machine_account
|
|
|
|
Arguments are in a hash:
|
|
|
|
node=>name of machine to add
|
|
|
|
=cut
|
|
|
|
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;
|
|
|
|
$dnsdomain =~ s/^[^\.]*//;
|
|
|
|
}
|
|
|
|
unless ($dnsdomain =~ /^\./) {
|
|
|
|
$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;
|
|
|
|
}
|
|
|
|
if ($ou) {
|
|
|
|
unless ($ou =~ /$domain_components\z/) {
|
|
|
|
$ou .= $domain_components;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$ou = "CN=Computers".$domain_components;
|
|
|
|
}
|
|
|
|
my $directoryserver = $args{directoryserver};
|
|
|
|
unless ($domain_components and $dnsdomain and $ou and $upnodename 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 $ldif = $machineldiftemplate;
|
|
|
|
$ldif =~ s/##B64PASSWORD##/$b64password/g;
|
|
|
|
$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";
|
|
|
|
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"};
|
|
|
|
}
|
|
|
|
$rc = system("echo '$ldif'|ldapmodify -H ldaps://$directoryserver");
|
|
|
|
return {password=>$newpassword};
|
|
|
|
}
|
2010-02-15 22:18:37 +00:00
|
|
|
sub find_free_params { #search for things like next available uidNumber
|
|
|
|
my %args = @_;
|
|
|
|
my @needed_parms = split /,/,$args{needed_params};
|
|
|
|
my $uidnumber = 10000; #common linux default is 500, some unix people would say 100, MS went with 10000. In this case, the highest number
|
|
|
|
#seems the best choice since it won't confuse any software assuming too low a number is a 'system' account
|
|
|
|
#also, a machine having local ids of 500-9999 won't as likely conflict with network accounts
|
|
|
|
#modern systems should tolerate 4.2 billion ids, so potentially wasting 9,500 isn't that big of a deal
|
|
|
|
|
|
|
|
#for now, just supporting uidNumber
|
|
|
|
my $directoryserver = $args{directoryserver};
|
|
|
|
my $dc = $args{ou};
|
|
|
|
my $ldapout;
|
|
|
|
my $ldapin;
|
|
|
|
my $ldaperr;
|
|
|
|
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=();
|
|
|
|
while (@handles = $select->can_read()) {
|
|
|
|
foreach (@handles) {
|
|
|
|
my $line = <$_>;
|
|
|
|
if (not defined $line) {
|
|
|
|
$select->remove($_);
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
if ($line =~ /^uidNumber: (\d+)$/) {
|
|
|
|
$useduids{$1}=1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) { #loop through until 'return'
|
|
|
|
unless ($useduids{$uidnumber}) {
|
|
|
|
return {uidNumber=>$uidnumber};
|
|
|
|
}
|
|
|
|
$uidnumber +=1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-08 22:08:34 +00:00
|
|
|
use Data::Dumper;
|
2010-02-15 22:18:37 +00:00
|
|
|
print Dumper(find_free_params(directoryserver=>"v4.xcat.e1350",ou=>"dc=xcat,dc=e1350"));
|