#!/usr/bin/perl use Net::LDAP; use Term::ReadPassword; use Digest::SHA1; use MIME::Base64; use Data::UUID; use Crypt::Cracklib; my $PASSWD_MIN_LEN = 8; my $password; # parse RC file # $ENV{GL_RC} = "/home/gitolite/.gitolite.rc"; die "parse $ENV{GL_RC} failed: " . ($! or $@) unless do $ENV{GL_RC}; # These come from .gitolite.rc file our ($GL_LDAP_HOST, $GL_LDAP_BIND_DN, $GL_LDAP_BIND_PASSWORD, $GL_LDAP_USER_DN); $Term::ReadPassword::ALLOW_STDIN = 1; # NOTICE: For some reason Perl fails to disable terminal echo # so following warning about ECHO must be given to the user # Warn about password echo because of bugs in Perl ReadPasword print "\nNOTE THAT THE PASSWORD WILL BE ECHOED TO THE SCREEN!\n" . "Please make sure no one is shoulder-surfing, and make sure\n" . "you clear your screen and scrollback history after you are done\n" . "(or close your terminal session).\n\n"; print "Please type in your new password at the prompt.\n\n" . "Following special keys are available while typing:\n" . " key to remove the last character\n" . " to remove all characters\n" . " to terminate password change operation\n" . " to end password typing\n"; while ( 1 ) { print "\n"; # Start reading with new line $password = read_password("Enter new password: ", 0, 1); # Check the validity of new password if ( length( $password ) >= $PASSWD_MIN_LEN # require minimum length && $password =~ /([\x20-\x7E])/ # require printable characters && $password =~ /[a-z]/ # require lower case letter && $password =~ /[A-Z]/ # require upper case letter && $password =~ /[0-9]/ # require number && check( $password ) ) # require other than dictionary words { # Re-enter new password to check possible typos if ( $password ne read_password("Enter password again: ") ) { print "Passwords do not match!\n"; redo; } else { last; # Password is valid and there are no typos, so break out } } else { # Given password is not valid print "Password must contain at least $PASSWD_MIN_LEN characters and numbers,\n" . "must have both upper and lower case characters,\n" . "can have special characters like !,",#,...\n" . "but cannot be any valid dictionary word.\n"; redo; } } # Create hash from the password to be stored to the LDAP my $ctx = Digest::SHA1->new(); my $ug = new Data::UUID; my $salt = $ug->create_b64(); $ctx->add( $password ); $ctx->add( $salt ); $password = '{SSHA}' . encode_base64( $ctx->digest . $salt, '' ); # Create communication structure for LDAP connection my $ldap = Net::LDAP->new( $GL_LDAP_HOST ) or die "$@"; my $r = $ldap->start_tls( verify => 'none', sslversion => 'tlsv1' ); if ( $r->code ) { print "Password handling failed with $r->code return code!\n"; log_it( "Password change, LDAP connection failed for $ENV{GL_USER}" ); exit 1; } # Bind to LDAP with proper user $r = $ldap->bind( $GL_LDAP_BIND_DN, password => $GL_LDAP_BIND_PASSWORD ); if ( $r->code ) { print "Password update failed with $r->code return code!\n"; log_it( "Password change, LDAP bind failed for $ENV{GL_USER}" ); exit 1; } # Update new password to the LDAP $r = $ldap->modify( "uid=$ENV{GL_USER}, $GL_LDAP_USER_DN", replace => { 'userPassword', $password } ); if ( $r->code ) { print "Password change failed!\n" . "Please contact administrator to change password.\n"; # log_it( "Password change, LDAP modify failed for $ENV{GL_USER}" ); } else { print "Password changed succesfully.\n"; # log_it( "Password change, LDAP modify done for $ENV{GL_USER}" ); } $r = $ldap->unbind();