Created
May 15, 2019 17:45
-
-
Save nxadm/84ff14675e4dbb2b97b3354e627cf5eb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env perl | |
# Update pwdChangedTime to match sambaPwdLastSet which is provided by by IAM. | |
# Bugs to [email protected]. | |
# CentOS 7 dependencies: | |
# - perl-LDAP | |
# - perl-TermReadKey | |
# Debian/Ubuntu dependencies: | |
# - libnet-ldap-perl | |
# - libterm-readkey-perl | |
our $VERSION = '0.2.0'; | |
use strict; | |
use warnings; | |
use feature 'say'; | |
use Getopt::Long; | |
use POSIX qw/strftime/; | |
use Net::LDAP; | |
use Net::LDAP::Control::Relax; | |
use Term::ReadKey; | |
### Variables ### | |
my ($uri, $binddn, $password, $prompt, $dryrun, $help, $ldif_in); | |
### CLI interface ### | |
GetOptions( 'H=s' => \$uri, 'D=s' => \$binddn, 'w=s' => \$password, | |
'W' => \$prompt, 'dryrun' => \$dryrun, 'help' => \$help) | |
or exit_with_msgs('Error in command line arguments (try "--help").'); | |
if ($help) { help() and exit 0 } | |
my @missing; | |
push @missing, 'Missing uri' unless defined $uri; | |
push @missing, 'Missing binddn' unless defined $binddn; | |
if (scalar @missing) { exit_with_msgs(@missing) } | |
if (scalar @ARGV > 1) { exit_with_msgs("Too many parameters") } | |
if (scalar @ARGV == 0) { exit_with_msgs("No LDIF file supplied") } | |
$ldif_in = $ARGV[0]; | |
if (!defined $password || $prompt) { | |
$password = prompt_for_password() unless $dryrun; | |
} | |
### Main ### | |
my $changes = find_changes(); | |
exit 0 if $dryrun; | |
ldap_modify($changes); | |
say "Done."; | |
exit 0; | |
### Subroutines ### | |
sub check_ldap_error { | |
my ($msg, $action) = @_; | |
if ($msg->code()) { | |
say STDERR "LDAP ERROR ($action): " . $msg->error(); | |
exit 2; | |
} | |
} | |
sub exit_with_msgs { | |
say STDERR "ERROR: $_" for @_ ; | |
exit 1; | |
} | |
sub find_changes { | |
local $/ = ""; | |
open(my $fh_in, '<', $ldif_in) or die($!); | |
my @changes; | |
my $counter = 0; | |
while(my $record = <$fh_in>) { | |
next if $record =~ /^\s*$/; | |
if ($record =~ /^dn::/) { | |
say STDERR 'base64 DN found (to be implemented)'; | |
exit 1; | |
} | |
my ($dn, $pwdChangedTime, $sambaPwdLastSet); | |
$dn = $1 if $record =~ /^dn:\s+(.+?)\n/; | |
next unless defined $dn; | |
$pwdChangedTime = $1 if $record =~ /\bpwdChangedTime:\s+(.+?)\n/; | |
$sambaPwdLastSet = $1 if $record =~ /\bsambaPwdLastSet:\s+(.+?)\n/; | |
if (defined $pwdChangedTime && defined $sambaPwdLastSet) { | |
my $sambachangedtime = strftime "%Y%m%d%H%M%SZ", gmtime($sambaPwdLastSet); | |
if ($sambachangedtime ne $pwdChangedTime) { | |
$counter++; | |
say "Mismatch: $dn, converted sambaPwdLastSet $sambachangedtime, pwdChangedTime $pwdChangedTime"; | |
push @changes, [ $dn, $sambachangedtime ] | |
} | |
} | |
} | |
say "Number of entries to be updated: $counter" if $dryrun; | |
return \@changes; | |
} | |
sub help { | |
say 'sync-pwdChangedTime-sambaPwdLastSet: ' . | |
'Update pwdChangedTime to match sambaPwdLastSet (changed by IAM),' . | |
"version $VERSION."; | |
say 'Usage:'; | |
say "sync-pwdChangedTime-sambaPwdLastSet <ldif backup file> [options]"; | |
say "sync-pwdChangedTime-sambaPwdLastSet -h"; | |
say 'Options:'; | |
say "\t-H URI LDAP Uniform Resource Identifier"; | |
say "\t-D bindnd bind DN"; | |
say "\t-w passwd bind password"; | |
say "\t-W passwd prompt for bind password"; | |
say "\t--help this help info"; | |
} | |
sub ldap_modify { | |
my $changes = shift; | |
my $ldap = Net::LDAP->new($uri); | |
say "LDAP ERROR (URI): $!" and exit 2 unless defined $ldap; | |
my $msg = $ldap->bind($binddn, password => $password); | |
check_ldap_error($msg, 'BIND'); | |
my $relax = Net::LDAP::Control::Relax->new(); | |
for my $data ( @{ $changes } ) { | |
my $dn = $data->[0]; | |
my $time = $data->[1]; | |
say "LDAP: working on $dn..."; | |
$msg = $ldap->modify( | |
$dn, | |
replace => { pwdChangedTime => $time }, | |
control => $relax, | |
); | |
check_ldap_error($msg, 'MODIFY'); | |
} | |
$ldap->unbind() or warn($!); | |
} | |
sub prompt_for_password { | |
Term::ReadKey::ReadMode('noecho'); | |
print "Password: "; | |
my $input = Term::ReadKey::ReadLine(0); | |
Term::ReadKey::ReadMode('restore'); | |
print "\n"; | |
$input =~ s/\R\z//; # remove EOL | |
return $input; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think you can write it with cro-ldap using:
I have nothing to run it against, but such syntax should work, at the very least. I don't recommend to run it against production server ( ;) ), but hope it helps.