#!/usr/bin/python3 # Author: Daniel Kahn Gillmor # Date: 2014-08-30 # License: GPLv3+ # For doing keyring-maint tasks with debian LDAP import ldap import getpass import sys if sys.argv.__len__() != 2 or sys.argv[1] in ['-h', '--help', 'help']: print('''Usage: update-ldap FILENAME should be a simple text file, utf8 encoded, where each line is a key replacement with three fields separated by a single space: uid oldfpr newfpr ''') exit(1) class debldap: def __init__(self): self.l = ldap.initialize("ldaps://db.debian.org") def auth(self, uid, password): self.l.simple_bind_s("uid={uid},ou=users,dc=debian,dc=org".format(uid=uid),password) def changefpr(self, uid, oldfpr, newfpr): dn = "uid={uid},ou=users,dc=debian,dc=org".format(uid=uid) objs = self.l.search_s(dn, ldap.SCOPE_SUBTREE, "objectclass=*") if not objs: raise BaseException("No objects found matching {dn}".format(dn=dn)) for o in objs: if o[0] != dn: raise BaseException("Weird/unexpected dn {new} (expected {old})".format(new=o[0], old=dn)) fprs = o[1]['keyFingerPrint'] if fprs != [oldfpr.encode('ascii')]: raise BaseException("old fingerprint was {found}, but we expected {oldfpr}".format(found=fprs, oldfpr=oldfpr)) self.l.modify_s(dn, [(ldap.MOD_REPLACE, 'keyFingerPrint', [newfpr.encode('ascii')])]) f = open(sys.argv[1]) x = debldap() username = getpass.getuser() try: passwd = getpass.getpass('Debian LDAP password for {user}: '.format(user=username)) x.auth(username, passwd) bound = True except BaseException as e: print("Failed to authenticate: {m}".format(m=e.message)) exit(1) errors = [] lineno = 0 successes = 0 for line in f: lineno += 1 user = '' try: data = line.strip('\n').split(' ') if data.__len__() != 3: raise BaseException("ignoring malformed line: {line}\n".format(lineno=lineno, line=line)) user = data[0] x.changefpr(user, data[1], data[2]) successes += 1 except BaseException as e: print("{lineno}: {user}: {message}".format(lineno=lineno, user=user,message=str(e.message).strip())) errors.append((lineno, e)) print("{errors} errors, {successes} successfully processed".format(successes=successes, errors=errors.__len__())) if errors: exit(1)