ldap登录的用户修改密码时由于系统默认passwd命令加密方式太简单(修改/etc/pam_ldap.conf的pam_password为crypt),所以用python脚本实现了passwd命令

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import re
import signal
import ldap
import ldap.modlist as modlist
import getpass
import getch
import random
import string
from passlib.hash import sha512_crypt


def pwd_input(msg = ''):
    if msg != '':
        sys.stdout.write(msg)
    chars = []  
    while True:  
        newChar = getch.getch()  
        if newChar in '\3\r\n': 
            print ''  
            if newChar in '\3':
                chars = []  
            break  
        elif newChar == '\b':
            if chars:  
                del chars[-1]  
                sys.stdout.write('\b \b')
        else:  
            chars.append(newChar)  
            sys.stdout.write('*')
    return ''.join(chars)

def judgePasswordStrength(password):
    strengthLength = max(0, len(password) - 5)
    from string import ascii_lowercase, ascii_uppercase, digits, punctuation
    flags = [bool(set(password) & set(s)) \
            for s in [ascii_lowercase, ascii_uppercase, digits, punctuation]]
    return min(flags.count(True), strengthLength)

def randomized_string(size=16, chars=string.letters + string.digits + string.punctuation):
    return ''.join(random.choice(chars) for x in range(size))

def make_pass(passwd=None):
    if passwd is None:
        passwd = randomized_string(32)
    salt = randomized_string(16, ( './' + string.letters + string.digits))
    iterations = random.randint(40000, 80000)
    return '{CRYPT}' + sha512_crypt.encrypt(passwd, salt=salt,rounds=iterations)

def check_pass(passwd):
    try:
        ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERTFILE)
        l = ldap.initialize(URI)
        l.simple_bind_s(DN, passwd)
        l.unbind_s()
        return True
    except ldap.LDAPError, e:
        return False

def change_password(oldpwd, newpwd):
    try:
        ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERTFILE)
        l = ldap.initialize(URI)
        l.simple_bind_s(DN, oldpwd)
        old = {'userPassword':oldpwd}
        new = {'userPassword':make_pass(newpwd)}
        ldif = modlist.modifyModlist(old,new)
        l.modify_s(DN,ldif)
        l.unbind_s()
        print('==================== [         Success         ] ======================')
        return "Jumpserver password information changed for %s" % UID
    except ldap.LDAPError, e:
        print('==================== [          Failed         ] ======================')
        return "Something wrong: %s" % e

def exit_gracefully(signum, frame):
    print('\nCancel~')
    signal.signal(signal.SIGINT, original_sigint)
    sys.exit(1)

def main():
    try:
        print('==================== [ Changing User Passwords ] ======================')
        print("Changing password for user %s." % UID)
        oldpwd = pwd_input('Enter login password: ')
        n = 1
        while check_pass(oldpwd)==False:
            if n > 2:
                print('passwd: Authentication token manipulation error')
                exit(1)
            else:
                n += 1
            print("Login password incorrect: try again")
            oldpwd = pwd_input('Enter login password: ')
        
        newpwd = pwd_input('New password: ')

        while oldpwd==newpwd:
            print("Password unchanged")
            newpwd = pwd_input('New password: ')

        while judgePasswordStrength(newpwd)<3:
            print('BAD PASSWORD: is too simple')
            newpwd = pwd_input('New password: ')

        newpwd2 = pwd_input('Retype new password: ')
        if newpwd<>newpwd2:
            print('Sorry, passwords do not match.')
            exit(1)
        else:
            print change_password(oldpwd, newpwd)
            print("passwd: all authentication tokens updated successfully.")
    except KeyboardInterrupt:
        sys.exit(1)

if __name__ == '__main__':
    global CACERTFILE,URI,BASEDN,UID,DN
    CACERTFILE = "/etc/ssl/certs/ca.crt"
    URI = "ldaps://master.ldap.xxx.com/"
    BASEDN = "ou=People,dc=xxx,dc=com"
    UID = getpass.getuser()
    DN = 'uid=%s,%s' % (UID, BASEDN)
    if len(sys.argv)==2:
        if re.compile('^\w+$').match(sys.argv[1]):
            UID=sys.argv[1]
        else:
            UID = getpass.getuser()
    DN = 'uid=%s,%s' % (UID, BASEDN)
    original_sigint = signal.getsignal(signal.SIGINT)
    signal.signal(signal.SIGINT, exit_gracefully)
    main()