###################################################################### # script name: PWManager.pl # file name: PWManager.pl # version: 1.0 # author: bob wells # date: 27 apr 98 # description: see Usage() subroutine below. # revision history: 1.00 - 27 apr 98 - Original. ###################################################################### use Win32::AdminMisc; ###################################################################### # If the first command line argument includes a '?', display script # usage instructions by calling the Usage() subroutine and exit. ###################################################################### ($ARGV[0] =~ /\?/) and Usage(); ###################################################################### # Parse command line arguments. ###################################################################### foreach (@ARGV) { (/^a=/i) and $sUsername = (split(/=/, $_, 2))[1]; (/^c=/i) and $sChangePasswordOn = uc((split(/=/, $_, 2))[1]); (/^db=/i) and $sPasswordDB = (split(/=/, $_, 2))[1]; (/^f=/i) and $sFetchPasswordFor = (split(/=/, $_, 2))[1]; (/^r/i) and $bCreateReport = TRUE; } ($sUsername) or ($sUsername = "Administrator"); ($sPasswordDB) or ($sPasswordDB = "Administrator.db"); ###################################################################### # Process 'f=hostname' switch by retrieving the password for # $sUsername on the user specified host. Print the results to STDOUT # and exit. The password is obtained from the script's password # database using the ReadRecord() subroutine, NOT from the SAM db. ###################################################################### if($sFetchPasswordFor) { $sPassword = ReadRecord($sPasswordDB, $sFetchPasswordFor); print "Host: $sFetchPasswordFor\n". "Username: $sUsername\n". "Password: $sPassword\n"; } ###################################################################### # The remainder of the script changes the $sUsername password on one # -or- ALL hosts based on the use of the 'c=' switch. # # If the 'c=ALL' mode is used, the password for $sUsername for each # host listed in the password database will be changed to a unique # randomly generated password. # # If the 'c=hostname' mode is used, only the password for $sUsername # on the specified host will be changed. ###################################################################### ###################################################################### # First step in processing the 'c=' switch is to build the list of # target host names. If 'c=ALL' is used, we use the CreateHostList() # subroutine to initialize @hosts which retrieves the hostnames from # the password database. Otherwise we initialize @hosts with the # single hostname specified in the 'c=hostname' switch. ###################################################################### if($sChangePasswordOn) { if($sChangePasswordOn eq "ALL") { @hosts = CreateHostList($sPasswordDB); } else { @hosts = $sChangePasswordOn; } ################################################################### # Backup current password database before we make any changes to # it. Note the script assumes the $sPasswordDB password database is # in the current working directory. ################################################################### $sPasswordDBBackup = ((split(/\./, $sPasswordDB))[0]).".bak"; open(DBIN, "<$sPasswordDB") || die "Unable to open current password database $sPasswordDB for backup. $!\n"; open(DBOUT, ">$sPasswordDBBackup") || die "Unable to create backup password database $sPasswordDBBackup. $!\n"; while() { print DBOUT; } close(DBIN); close(DBOUT); ################################################################### # Seed rand() used in the random Password() subroutine. ################################################################### srand(time() ^ ($$ + ($$ << 15))); print "\nWorking"; ################################################################### # Primay loop. ################################################################### foreach $host (@hosts) { print "."; # Initialize scalars. $bResult = TRUE; $sNewPassword = ""; $sOldPassword = ""; # Fetch old password. $sOldPassword = ReadRecord($sPasswordDB, $host); # Generate new password. $sNewPassword = Password(); $t = time(); # Change password. if(Win32::AdminMisc::UserChangePassword($host,$sUsername,$sOldPassword,$sNewPassword) != 1) { $bResult = FALSE; } # Update password database. WriteRecord($sPasswordDB, $host, $sNewPassword, $sOldPassword, $t, $bResult); } print "done"; } if($bCreateReport eq "TRUE") { print "\nCreating formatted report..."; # Sort password database. SortPasswordDB($sPasswordDB); # Create report. CreateReport($sPasswordDB, $sUsername); print "done\n"; } exit(1); ###################################################################### # End of Script ###################################################################### ###################################################################### # Begin Formats ###################################################################### format REPORT = @<<<<<<<<<<<<<<<<<<@<<<<<<<<<<<<<<@<<<<<<<<<<<<<<@<<<<<<<<<<<<<<@<<<<< $field1, $field2, $field3, $field4, $field5 . ###################################################################### # Begin User Defined Subroutines ###################################################################### ###################################################################### # CreateHostList() Subroutine ###################################################################### sub CreateHostList { my($db) = @_; open(DB, "<$db") || die "Unable to open current password database $db for CreateHostList(). $!\n"; my(@hostlist) = map(/^([^,]+)/, ); close(DB); return(@hostlist); } ###################################################################### # CreateReport() Subroutine ###################################################################### sub CreateReport { my($db, $user) = @_; $report = ((split(/\./, $db))[0]).".txt"; open(DBIN, "<$db") || die "Unable to open password database $db for CreateReport(). $!\n"; open(REPORT, ">$report") || die "Unable to create new report $report. $!\n"; print REPORT "$user Password Report\n". "-----------------------------\n\n". "Server New Password Old Password Last Change Status\n". "------ ------------ ------------ ----------- ------\n"; while() { /^#|^\s*$/ && next; chomp; ($field1, $field2, $field3, $field4, $field5) = split(/,/, $_, 5); ($sec,$min,$hour,$mday,$mon,$year,$sday,$yday,$isdst) = localtime($field4); $field4 = sprintf("%02d\%02d\%02d@%02d%02d",$mon+1,$mday,$year,$hour,$min); if($field5 == 0) { $field5 = "ERROR"; } else { $field5 = ""; } write(REPORT); } close(DBIN); close(REPORT); return(1); } ###################################################################### # Password() Subroutine ###################################################################### sub Password { my @pw_chars = ('0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F','G','H','I','J', 'K','L','M','N','O','P','Q','R','S','T', 'U','V','W','X','Y','Z','a','b','c','d', 'e','f','g','h','i','j','k','l','m','n', 'o','p','q','r','s','t','u','v','w','x', 'y','z'); my $pw_index = @pw_chars; my $pw = ""; for($num = 0; $num < 8; $num++) { $pw .= $pw_chars[rand($pw_index)]; } return($pw); } ###################################################################### # ReadRecord() Subroutine ###################################################################### sub ReadRecord { my($db, $server) = @_; open(DB, "<$db") || die "Unable to open current password database $db for ReadRecord(). $!\n"; my @dbrecord = grep(/^$server,/i, ); close(DB); my $pw = (split(/,/, $dbrecord[0]))[1]; return($pw); } ###################################################################### # SortPasswordDB() Subroutine ###################################################################### sub SortPasswordDB { my $db = shift; rename($db, "$db~"); open(DB, "<$db~") || die "Unable to open tilde password database $db~ for SortPasswordDB(). $!\n"; my @db = grep(/\S/, map {s/\s//g; $_} ); close(DB); my @sorteddb = sort @db; open(DB, ">$db") || die "Unable to create new password database $db for SortPasswordDB(). $!\n"; foreach(@sorteddb) { print DB "$_\n"; } close(DB); unlink("$db~"); return(1); } ###################################################################### # Usage() Subroutine ###################################################################### sub Usage { print < perl pwmanager.pl a=Username db=PasswordDBFilename {c=ALL | c=hostname | f=hostname | r} PWManager.pl is a Perl for Win32 script that manages the password for a specific NT User Account across multiple PDC's, Member Servers and Workstations. It is designed to ease the management of passwords for identically named accounts (i.e. NT's built-in Administrator account) located throughout the enterprise. The script supports both batch and interactive modes. PWManager.pl has the following dependencies: - User executing script must have Administrator privileges on ALL domains and hosts where the target account resides. - Password database. This is a CSV database that contains the password data for the managed account. The file must adhere to the following comma separated format and is expected to reside in the current working directory. Database format: ---------------- - One record per line. - Five fields per record. Field contents: --------------- 1. Hostname 2. Current password 3. Previous password 4. Last successful change time stamp (in seconds) 5. Status (0=Error / 1=Success) Example database (Administrator.db): ------------------------------------ MYPDC,db5FozSA,hRE4avT7,893688390,1 MYMMBRSRVR,I1JAYpgc,JIbrRRGh,893688390,1 MYWRKSTTN,3yy21sb6,47JrgrjT,893688390,1 - Command line arguments: a=Username - The account username. (Default: Administrator) db=PasswordDBFilename - The password database filename. (Default: Administrator.db) c=ALL - Directs the script to change the account password on ALL systems for which records exist in the password database file. c=hostname - Directs the script to change the account password only on the specified hostname. f=hostname - Display the password for hostname. r - Create formatted report. Examples: c:\\> perl pwmanager.pl a=Administrator db=Administrator.db c=all Script will change the password of the Administrator account on ALL hosts in the Administrator.db password database. c:\\> perl pwmanager.pl c=mymmbrsrvr Script will change the password of the Administrator account on the host named mymmbrsrvr. c:\\> perl pwmanager.pl f=mywrksttn Script will display the password of the Administrator account on the host named mywrksttn. c:\\> perl pwmanager.pl r Script will create a formatted report based on the contents of the default password database file. Extension will be changed to *.txt. END exit(0); } ###################################################################### # WriteRecord() Subroutine ###################################################################### sub WriteRecord { my($db, $server, $newpw, $oldpw, $time, $status) = @_; rename($db, "$db~"); open(DB1, "<$db~") || die "Unable to open tilde password database $db~ for WriteRecord() update. $!\n"; open(DB2, ">$db") || die "Unable to open new password database $db for WriteRecord() update. $!\n"; while() { if(/^$server,/i) { if($status eq "TRUE") { print DB2 "$server,$newpw,$oldpw,$time,1\n"; } else { my($f1, $f2, $f3, $f4, $f5) = split(/,/, $_, 5); print DB2 "$f1,$f2,$f3,$f4,0\n"; } } else { print DB2; } } close(DB1); close(DB2); unlink("$db~"); return(1); }