# # Analyze output from Daigpwd and produce a useable list of eDirectory accounts who's eDirectory passwords # are not synchronized with their Universal or Simple passwords. # # Chris Randles - chrandles@novell.com (bugs found, improvements and enhancement requests welcome). # # v1.0 2008-05-07 # # Input Requirements - The output from Diagpwd: # # Use Daigpwd with the following syntax: # # diagpwd .der sub # # e.g. diagpwd 192.168.0.1 636 MyTree.der o=Novell sub cn=admin,o=Novell mypassword # # Use redirection to output the data to a text file. e.g. add '> diagpwd.txt' to the end of the statement: # # e.g. diagpwd 192.168.0.1 636 MyTree.der o=Novell sub cn=admin,o=Novell mypassword > diagpwd.txt # # To aquire the Diagpwd utility go to the Novell downloads web page and search for 'diagpwd*' # NOTE: Diagpwd4 was the version at the time of writing this document. # Do not modify the output file from diagpwd! # Diagpwd takes a while to run. You can use LDAP tracing to follow it's progress. # Example Input Data (output data from diagpwd):- # #Object DN: cn=MyAccount,ou=IT,ou=CA,o=Novell # EMail: ChRandles@novell.com # Last Changed Date: 2008-04-21 22:40:45 Z # Password Status: Enabled, Set # Distribution Password Status: Set # Simple Password Status: Set # Password Policy DN: cn=Password Policy,cn=Password Policies,cn=Security # #Object DN: cn=ThatAccount,ou=Accounts,ou=CA,o=Novell # EMail: NotReal@novell.com # Last Changed Date: [UNKNOWN] # Password Status: Enabled, Set # Distribution Password Status: Not set # Simple Password Status: Set # Password Policy DN: cn=Password Policy,cn=Password Policies,cn=Security # #Object DN: cn=NFAUUser,o=novell # EMail: [NONE] # Last Changed Date: [UNKNOWN] # Password Status: Universal Password disabled, Not set # Distribution Password Status: Not set # Simple Password Status: Not set # Password Policy DN: [NONE] # Program Output Includes:- # # Password_Totals.txt # A file containing all of the totals derived by the program which appear # in the various output files. # # Bad_Passwords.txt # List of objects where Universal and/or Simple passwords do not match NDS password: # Number of objects with bad Universal and Simple Passwords # Number of objects with bad Universal Password Only # Number of objects with bad Simple Password Only # Total number of objects with bad passwords # # Universal_Password_Not_Enabled.txt # List of and Total number of objects with Universal Password NOT enabled # # Universal_Password_Enabled.txt # List of and Total number of objects with Universal Password Enabled # # Universal_Password_Set.txt # List of and Total number of objects with Universal Password Set # # Universal_Password_Not_Set.txt # List of and Total number of objects with Universal Password NOT Set # # Distribution_Password_Set.txt # List of and Total number of objects with Distribution Password Set # # Distribution_Password_Not_Set.txt # List of and Total number of objects with Distribution Password NOT Set # # Simple_Password_Set.txt # List of and Total number of objects with Simple Password Set # # Simple_Password_Not_Set.txt # List of and Total number of objects with Simple Password NOT Set # # Users_By_Last_Password_Change.txt # List of objects ordered by password last changed date # Number Users without a password last changed date # Number of Users with a password last changed date # # Users_By_Password_Policies.txt # List of objects ordered by assigned password policy # Number of Users assigned to each password policy # # Excluded_Objects.txt # List of and Total number of objects excluded from the input data. # # Passwords.csv # A csv formatted file containing the input data. One object per line. # # A Total of 14 output files are created per program run. NOTE: Output files are over-written with each run. # # Exlcusions file format is a simple text list of object CNs to ignore. # Create a trext file called 'exclude.txt' (case sensitive on Linux/Unix) # and enter one CN per line (CN is case insensitive). # # e.g. Backup_Exec # Administrator # Admin # Proxy # UNIX Service Handler # NFAUUser use Fcntl; use strict; my $OS; my $Win; my $inputf=""; $OS=$^O; $OS=uc($OS); $Win=index($OS,"WIN",0); if ($Win eq -1) {system "reset"}; if ($Win ne -1) {system "cls"}; print "\nName of input file [Default=diagpwd.txt]: "; chomp($inputf = ); if ($inputf eq "") {$inputf="diagpwd.txt"}; print "\n"; if (-e "$inputf") {goto BEG01}; print "Error - File $inputf does not exist !!!\n\n"; exit; BEG01: open (DIAGPWD,"<$inputf"); open (EXC,">Excluded_Objects.txt"); if ($Win eq -1) {system "reset"}; if ($Win ne -1) {system "cls"}; my $carreturn=v13; my $newline=v10; my $quote=v34; my $comma=","; my $tab=v9; my $rcount=0; my $linecount=0; my $linein; my $inpos; my $RDN; my $RevRDN; my $PolRDN; my $RevPolRDN; my $LastChanged; my $NoLastChanged=0; my $NumLastChanged=0; my $PasswordStatus; my $UPEnabled; my $UPENcount; my $UPNENcount; my $UPtot; my $UPSet; my $UPNEQ; my $UPNEQcount=0; my $UPScount=0; my $DistPassStatus; my $SimplePassStatus; my $SimpleNE; my $SNEcount=0; my $key; my $val1; my $totpols=0; my $totusers=0; my $TotBad=0; my $UPEN=0; my $UPS; my $UPNS; my $DPS; my $DPNS; my $SPS; my $SPNS; my $ShortCN; my $excluded=0; # Vars used by reverse context my $RDNsep=","; my $count1; my $count2; my $count3, my $getfield; my $context; my $context3; my $context2; my $swcontext; my $bit; # Arrays my %RevUsers=(); # Clear down Array - Objects by context my %DateChanged=(); # Clear down Array - Users by last password date change my %UsersByPolicies=(); # Clear down Array - Objects by assigned password policy my %PassPolicies=(); # Clear down Array - Password Ploicy Assignment counts my %Exclude=(); # Clear down Array - Objects to Exclude # * * * Read in objects to exclude * * * print "\nProgram: pwdcheck.pl - C.Randles - v1.0 2008-05-07\n\n"; if (-e "exclude.txt") { print "\nReading in Exclusions file !\n"; open (EXCLUDE,"; if ($linein eq "") {goto BEG05}; strip (); $linein=~tr/a-z/A-Z/; $Exclude{$linein}=""; goto BEG05; BEG10: close (EXCLUDE); } # foreach $key (sort keys %Exclude) {print "$key\n"}; # test exclusions read in # exit; print "\nProcessing Input File !\n\n"; $rcount=0; NL01: if (eof DIAGPWD) {goto NL10}; $linein = ; $linecount++; $inpos=index($linein,"Object DN:",0); if ($inpos eq -1) {goto NL01}; $inpos=index($linein,"cn=",0); if ($inpos eq -1) {goto NL01}; strip (); $RDN=substr($linein,11); $inpos=index($RDN,",",0); $ShortCN=substr($RDN,3,$inpos-3); $ShortCN=~tr/a-z/A-Z/; # print $ShortCN."\n"; # print "$RDN \n"; $getfield=$RDN; ReverseRDN (); $RevRDN=$context2; # Test if excluded CN object if (defined($Exclude{$ShortCN})) { $excluded++; print EXC $RevRDN."\n"; goto NL01; } # print "$RevRDN \n"; if (eof DIAGPWD) {goto NL10}; $linein = ; # EMail: - not used or recorded $linecount++; if (eof DIAGPWD) {goto NL10}; $linein = ; # Last Changed Date: $linecount++; $inpos=index($linein,"Last Changed Date:",0); if ($inpos eq -1) { print "Error - Expected Last Changed Date at Line $linecount!!!\n\n"; exit; } strip (); $LastChanged=substr($linein,27); if ($LastChanged eq "[UNKNOWN]") {$LastChanged="0000-00-00 00:00:00"}; $LastChanged=substr($LastChanged,0,19); # print "$LastChanged \n\n"; if (eof DIAGPWD) {goto NL10}; $linein = ; # Password Status: $linecount++; $inpos=index($linein,"Password Status:",0); if ($inpos eq -1) { print "Error - Expected Password Status at Line $linecount!!!\n\n"; exit; } strip (); $PasswordStatus=substr($linein,25); # print "$PasswordStatus \n"; $UPEnabled=0; $inpos=index($linein,": Enabled,",0); if ($inpos ne -1) {$UPEnabled=1}; # print "Enabled: $UPEnabled \n"; $UPSet=1; $inpos=index($linein,"Not set",0); if ($inpos ne -1) {$UPSet=0}; # print "Set: $UPSet \n\n"; $UPNEQ=0; $inpos=index($linein,"!= NDS",0); if ($inpos ne -1) {$UPNEQ=1}; if (eof DIAGPWD) {goto NL10}; $linein = ;# Distribution Password Status: $linecount++; $inpos=index($linein,"Distribution Password Status:",0); if ($inpos eq -1) { print "Error - Expected Distribution Password Status at Line $linecount!!!\n\n"; exit; } strip (); $DistPassStatus=substr($linein,38); # print "$DistPassStatus \n"; if ($DistPassStatus eq "Set") { $DistPassStatus=1; } else { $DistPassStatus=0; } # print "$DistPassStatus \n\n"; if (eof DIAGPWD) {goto NL10}; $linein = ;# Simple Password Status: $linecount++; $inpos=index($linein,"Simple Password Status:",0); if ($inpos eq -1) { print "Error - Expected Simple Password Status at Line $linecount!!!\n\n"; exit; } strip (); $SimplePassStatus=substr($linein,32); # print "$SimplePassStatus \n"; $SimpleNE=0; $inpos=index($linein,"!= NDS",0); if ($inpos ne -1) {$SimpleNE=1}; if ($SimplePassStatus eq "Set") { $SimplePassStatus=1; } else { $SimplePassStatus=0; } # print "$SimplePassStatus \n\n"; if (eof DIAGPWD) {goto NL10}; $linein = ;# Password Policy DN: $linecount++; $inpos=index($linein,"Password Policy DN:",0); if ($inpos eq -1) { print "Error - Expected Password Policy DN at Line $linecount!!!\n\n"; exit; } strip (); $PolRDN=substr($linein,28); # print "$PolRDN \n"; $getfield=$PolRDN; ReverseRDN (); $RevPolRDN=$context2; # print "$RevPolRDN \n\n"; $RevUsers{$RevRDN}=$LastChanged.$UPEnabled.$UPSet.$UPNEQ.$DistPassStatus.$SimplePassStatus.$SimpleNE.$RevPolRDN; $key=$LastChanged.' '.$RevRDN; $DateChanged{$key}=""; $key=$RevPolRDN.' '.$RevRDN; $UsersByPolicies{$key}=""; $key=$RevPolRDN; $val1=$PassPolicies{$key}; $val1++; $PassPolicies{$key}=$val1; if ($LastChanged eq "0000-00-00 00:00:00") { $NoLastChanged++; } else { $NumLastChanged++; } # print "$SimplePassStatus \n\n"; goto NL01; # * * * Output Section * * * NL10: print "End of input file !\n"; close (DIAGPWD); print EXC "\nTotal number of objects Excluded: $excluded\n\n"; close (EXC); # open (USERS,">Users.txt"); open (BADPASS,">Bad_Passwords.txt"); open (UPNE,">Universal_Password_Not_Enabled.txt"); open (UPE,">Universal_Password_Enabled.txt"); open (UPS,">Universal_Password_Set.txt"); open (UPNS,">Universal_Password_Not_Set.txt"); open (DPS,">Distribution_Password_Set.txt"); open (DPNS,">Distribution_Password_Not_Set.txt"); open (SPS,">Simple_Password_Set.txt"); open (SPNS,">Simple_Password_Not_Set.txt"); open (PASS,">Passwords.csv"); print "\nCreating results files !\n\n"; print PASS $quote."Object Reverse RDN".$quote.$comma.$quote."Last Pass Change".$quote.$comma.$quote; print PASS "UP Enabled ?".$quote.$comma.$quote."UP Set ?".$quote.$comma.$quote."UP Not=NDS ?".$quote.$comma.$quote; print PASS "Distribution Set ?".$quote.$comma.$quote."Simple Set ?".$quote.$comma.$quote; print PASS "Simple Not=NDS ?".$quote.$comma.$quote."Assigned Password Policy Reverse RDN".$quote."\n"; foreach $key (sort keys %RevUsers) { # print USERS "$key $RevUsers{$key} \n"; $LastChanged=substr($RevUsers{$key},0,19); $UPEnabled=substr($RevUsers{$key},19,1); $UPSet=substr($RevUsers{$key},20,1); $UPNEQ=substr($RevUsers{$key},21,1); $DistPassStatus=substr($RevUsers{$key},22,1); $SimplePassStatus=substr($RevUsers{$key},23,1); $SimpleNE=substr($RevUsers{$key},24,1); $RevPolRDN=substr($RevUsers{$key},25); if ($UPEnabled eq "0") { print UPNE "$key\n"; $UPNENcount++; } else { print UPE "$key\n"; $UPENcount++; } if ($UPSet eq "0") { print UPNS "$key\n"; $UPNS++; } else { print UPS "$key\n"; $UPS++; } if ($DistPassStatus eq "0") { print DPNS "$key\n"; $DPNS++; } else { print DPS "$key\n"; $DPS++; } if ($SimplePassStatus eq "0") { print SPNS "$key\n"; $SPNS++; } else { print SPS "$key\n"; $SPS++; } if (($UPNEQ eq "1")&&($SimpleNE eq "1")) { print BADPASS "$key Universal and Simple Password not equal to NDS password !\n"; $UPScount++; goto NL15; } if ($UPNEQ eq "1") { print BADPASS "$key Universal Password not equal to NDS password !\n"; $UPNEQcount++; goto NL15; } if ($SimpleNE eq "1") { print BADPASS "$key Simple Password not equal to NDS password !\n"; $SNEcount++; } NL15: print PASS $quote.$key.$quote.$comma.$quote.$LastChanged.$quote.$comma.$quote; print PASS $UPEnabled.$quote.$comma.$quote.$UPSet.$quote.$comma.$quote; print PASS $UPNEQ.$quote.$comma.$quote.$DistPassStatus.$quote.$comma.$quote; print PASS $SimplePassStatus.$quote.$comma.$quote.$SimpleNE.$quote.$comma.$quote; print PASS $RevPolRDN.$quote."\n"; } # close (USERS); print UPNE "\nTotal number of objects with Universal Password NOT enabled: $UPNENcount\n\n"; print UPE "\nTotal number of objects with Universal Password Enabled: $UPENcount\n\n"; close (UPNE); close (UPE); print UPS "\nTotal number of objects with Universal Password Set: $UPS\n\n"; print UPNS "\nTotal number of objects with Universal Password NOT Set: $UPNS\n\n"; close (UPS); close (UPNS); print DPS "\nTotal number of objects with Distribution Password Set: $DPS\n\n"; print DPNS "\nTotal number of objects with Distribution Password NOT Set: $DPNS\n\n"; close (DPS); close (DPNS); print SPS "\nTotal number of objects with Simple Password Set: $SPS\n\n"; print SPNS "\nTotal number of objects with Simple Password NOT Set: $SPNS\n\n"; close (SPS); close (SPNS); close (PASS); $TotBad=$UPNEQcount+$SNEcount+$UPScount; print BADPASS "\nNumber of objects with bad Universal and Simple Passwords: $UPScount"; print BADPASS "\nNumber of objects with bad Universal Password Only : $UPNEQcount"; print BADPASS "\nNumber of objects with bad Simple Password Only : $SNEcount\n"; print BADPASS "\nTotal number of objects with bad passwords : $TotBad\n\n"; close (BADPASS); open (CHRON,">Users_By_Last_Password_Change.txt"); print "Creating output file Users_By_Last_Password_Change.txt !\n\n"; foreach $key (sort keys %DateChanged) { print CHRON "$key $DateChanged{$key} \n"; } print CHRON "\nNumber Users without a Password Last Changed Date: $NoLastChanged \n"; print CHRON "\nNumber of Users with a Password Last Changed Date: $NumLastChanged \n\n"; close (CHRON); open (BYPOL,">Users_By_Password_Policies.txt"); print "Creating output file Users_By_Password_Policies.txt !\n\n"; foreach $key (sort keys %UsersByPolicies) { print BYPOL "$key \n"; $totusers++; } print BYPOL "\nNumber of Users assigned to each password policy:\n\n"; $totpols=0; foreach $key (sort keys %PassPolicies) { if ($key ne "[NONE]") { print BYPOL " $key: $PassPolicies{$key} \n"; $totpols=$totpols+$PassPolicies{$key}; } } print BYPOL "\n Objects not assigned to a policy: ".$PassPolicies{"[NONE]"}."\n"; print BYPOL "\n Total number of objects assigned: $totpols\n\n"; close (BYPOL); open (PASSTOT,">Password_Totals.txt"); print "Creating output file Password_Totals.txt !\n\n"; print PASSTOT "\nNumber of objects with bad Universal and Simple Passwords : $UPScount\n"; print PASSTOT "Number of objects with bad Universal Password Only : $UPNEQcount\n"; print PASSTOT "Number of objects with bad Simple Password Only : $SNEcount\n"; print PASSTOT " ========\n"; print PASSTOT "Total number of objects with bad passwords : $TotBad\n\n"; print PASSTOT "Total number of objects with Universal Password NOT enabled: $UPNENcount\n"; print PASSTOT "Total number of objects with Universal Password Enabled : $UPENcount\n"; print PASSTOT " ========\n"; $UPtot=$UPNENcount+$UPENcount; print PASSTOT " $UPtot\n\n"; print PASSTOT "Total number of objects with Universal Password Set : $UPS\n"; print PASSTOT "Total number of objects with Universal Password NOT Set : $UPNS\n"; print PASSTOT " ========\n"; $UPtot=$UPS+$UPNS; print PASSTOT " $UPtot\n\n"; print PASSTOT "Total number of objects with Distribution Password Set : $DPS\n"; print PASSTOT "Total number of objects with Distribution Password NOT Set : $DPNS\n"; print PASSTOT " ========\n"; $UPtot=$DPS+$DPNS; print PASSTOT " $UPtot\n\n"; print PASSTOT "Total number of objects with Simple Password Set : $SPS\n"; print PASSTOT "Total number of objects with Simple Password NOT Set : $SPNS\n"; print PASSTOT " ========\n"; $UPtot=$SPS+$SPNS; print PASSTOT " $UPtot\n\n"; print PASSTOT "Number Users without a Password Last Changed Date : $NoLastChanged\n"; print PASSTOT "Number of Users with a Password Last Changed Date : $NumLastChanged\n"; print PASSTOT " ========\n"; $UPtot=$NoLastChanged+$NumLastChanged; print PASSTOT " $UPtot\n\n"; print PASSTOT "Total number of objects : $totusers\n"; print PASSTOT "Total number of objects Excluded : $excluded\n"; print PASSTOT " ========\n"; $UPtot=$totusers+$excluded; print PASSTOT " $UPtot\n\n"; print PASSTOT "Number of Users assigned to each password policy:\n\n"; $totpols=0; foreach $key (sort keys %PassPolicies) { if ($key ne "[NONE]") { print PASSTOT " $key: $PassPolicies{$key} \n"; $totpols=$totpols+$PassPolicies{$key}; } } print PASSTOT "\n Objects not assigned to a policy: ".$PassPolicies{"[NONE]"}."\n"; print PASSTOT "\n Total number of objects assigned: $totpols\n\n"; close (PASSTOT); print "Done !\n\n"; exit; sub strip { $bit=index($linein,$carreturn,0); if ($bit ne -1) {$linein=substr($linein,0,$bit)}; $bit=index($linein,$newline,0); if ($bit ne -1) {$linein=substr($linein,0,$bit)}; $bit=index($linein,$tab,0); if ($bit ne -1) {$linein=substr($linein,0,$bit)." ".substr($linein,$bit+1)}; } # < < < Reverse RDN Section > > > # Also replaces first tab with 8 spaces sub ReverseRDN { sw00: $count1=length($getfield); $context3=""; $context=$getfield; $swcontext=$getfield; sw13: $bit=substr($swcontext,$count1-1,1); if ($bit ne " ") {goto sw14}; $count1--; goto sw13; sw14: $count2=$count1; sw15: $count2--; if ($count2 eq 0) { goto sw16 }; $bit=substr($swcontext,$count2-1,1); if ($bit ne $RDNsep) {goto sw15}; sw16: $context2=substr($swcontext,$count2,$count1-$count2); $context2=$context2.$RDNsep; $count3=$count1-$count2+1; sw17: $count1=$count2-1; sw18: $count2--; if ($count2 le 1) { $count2=0; goto sw19; } $bit=substr($swcontext,$count2-1,1); if ($bit ne $RDNsep) { goto sw18 }; sw19: $context3=substr($swcontext,$count2,$count1+1-$count2); substr($context2,$count3,$count1-$count2)=$context3; if ($count2 eq 0) { goto sw20 }; $count3=$count3+$count1-$count2+1; goto sw17; sw20: $context2=substr($context2,0,length($context2)-1); } # > > > END - Reverse RDN Section < < < #------------------------------------------------------------------------------- #Array: %RevUsers #------------------------------------------------------------------------------- # #KEY FIELDS # 1:$RevRDN :alpha : :Reverse RDN of object # #DATA FIELDS # 2:$LastChanged :a19 : :Password last changed date and time # 3:$UPEnabled :n1 :0 or 1 :Is Universal Password Enabled ? # 4:$UPSet :n1 :0 or 1 :Is Universal Password Set ? # 5:$UPNE :n1 :0 or 1 :Is Universal Password not equal to NDS ? # 6:$DistPassStatus :n1 :0 or 1 :Is Distribution Password Set ? # 7:$SimplePassStatus :n1 :0 or 1 :Is Simple Password set ? # 8:$SimpleNE :n1 :0 or 1 :Is Simple Password not equal to NDS ? # 9:$RevPolRDN :alpha : :Reverse RDN of Assigned Password Policy #------------------------------------------------------------------------------- #Array: %DateChanged #------------------------------------------------------------------------------- # #KEY FIELDS # 1:$LastChanged :a19 : :Password last changed date and time # 2: :a1 : : # 3:$RevRDN :alpha : :Reverse RDN of object # #DATA FIELDS #------------------------------------------------------------------------------- #Array: %UsersByPolicies #------------------------------------------------------------------------------- # #KEY FIELDS # 1:$RevPolRDN :alpha : :Reverse RDN of Assigned Password Policy # 2: :a1 : : # 3:$RevRDN :alpha : :Reverse RDN of object # #DATA FIELDS #------------------------------------------------------------------------------- #Array: %PassPolicies #------------------------------------------------------------------------------- # #KEY FIELDS # 1:$RevPolRDN :alpha : :Reverse RDN of Assigned Password Policy #DATA FIELDS # 2:$val1 :number : :Number of objects assigned to this policy