#!/usr/bin/env python # -*- mode: python -*- # Copyright (c) 1999-2000 Jason Gunthorpe # Copyright (c) 2004 Joey Schulze # Copyright (c) 2008, 2009, 2010 Peter Palfrader # Copyright (c) 2010 Martin Zobel-Helas # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # This script tries to match key fingerprints from a keyring with user # name in a directory. When an unassigned key is found a heuristic match # against the keys given cn/sn and the directory is performed to try to get # a matching. Generally this works about 90% of the time, matching is fairly # strict. In the event a non-match a fuzzy sounds-alike search is performed # and the results printed to aide the user. # # GPG is automatically invoked with the correct magic special options, # pass the names of all the valid key rings on the command line. # # The output report will list what actions were taken. Keys that are present # in the directory but not in the key ring will be removed from the # directory. import re, time, ldap, getopt, sys, pwd, os; from userdir_ldap import *; from userdir_gpg import *; # This map deals with people who put the wrong sort of stuff in their pgp # key entries UnknownMap = {}; NoAct = 1; # Read the override file into the unknown map. The override file is a list # of colon delimited entires mapping PGP email addresess to local users def LoadOverride(File): List = open(File,"r"); while(1): Line = List.readline(); if Line == "": break; Split = re.split("[:\n]",Line); UnknownMap[Split[0]] = Split[1].strip() def load_keys_from_gpg(keyrings): keys = {} # Popen GPG with the correct magic special options ClearKeyrings() SetKeyrings(keyrings) Args = [GPGPath] + GPGBasicOptions + GPGKeyRings + GPGSearchOptions + [" 2> /dev/null"] Keys = os.popen(" ".join(Args),"r"); # Loop over the GPG key file Outstanding = 0; while(1): Line = Keys.readline(); if Line == "": break; Split = Line.split(":") if len(Split) < 8 or Split[0] != "pub": continue; while (1): Line2 = Keys.readline(); if Line2 == "": break; Split2 = Line2.split(":"); if len(Split2) < 11 or Split2[0] != "fpr": continue; break; if Line2 == "": break; pgp_uid = Split[9] fingerprint = Split2[9] if fingerprint in keys: print "Duplicate key in keyrings: %s, belonging to %s"%(fingerprint, pgp_uid) continue keys[fingerprint] = pgp_uid if Keys.close() != None: raise "Error","GPG failed" return keys # Process options AdminUser = pwd.getpwuid(os.getuid())[0]; (options, arguments) = getopt.getopt(sys.argv[1:], "ahu:m:") for (switch, val) in options: if (switch == '-u'): AdminUser = val elif (switch == '-m'): LoadOverride(val); elif (switch == '-h'): print "Usage: ud-gpgimport " print "Available options:" print " -h Show this help" print " -u= Admin user (defaults to current username)" print " -m= Override file to use" print " -a actually do changes, not dry-run" sys.exit(0) elif (switch == '-a'): NoAct = 0; # Main program starts here # Connect to the ldap server if NoAct == 0: l = passwdAccessLDAP(BaseDn, AdminUser) else: l = connectLDAP() l.simple_bind_s("",""); # Download the existing key list and put it into a map print "Fetching key list..", sys.stdout.flush(); Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=*",["keyFingerPrint","uid"]); KeyMap = {}; KeyCount = {}; for x in Attrs: try: # Sense a bad fingerprint.. Slapd has problems, it will store a null # value that ldapsearch doesn't show up.. detect and remove if len(x[1]["keyFingerPrint"]) == 0 or x[1]["keyFingerPrint"][0] == "": print; print "Fixing bad fingerprint for",x[1]["uid"][0], sys.stdout.flush(); if NoAct == 0: l.modify_s("uid="+x[1]["uid"][0]+","+BaseDn,\ [(ldap.MOD_DELETE,"keyFingerPrint",None)]); else: for I in x[1]["keyFingerPrint"]: KeyMap[I] = [x[1]["uid"][0],0]; if KeyCount.has_key(x[1]["uid"][0]): KeyCount[x[1]["uid"][0]] = KeyCount[x[1]["uid"][0]] + 1; else: KeyCount[x[1]["uid"][0]] = 1; except: continue; Attrs = None; print; pgpkeys = load_keys_from_gpg( ConfModule.add_keyrings.split(":") ) pgpkeys_extra = load_keys_from_gpg( ConfModule.add_keyrings_guest.split(":") ) Ignored = 0; for fpr in pgpkeys: pgp_uid = pgpkeys[fpr] if fpr in KeyMap: Ignored = Ignored + 1; # print "Ignoring keyID",fpr,"belonging to",KeyMap[fpr][0]; KeyMap[fpr][1] = 1; continue; UID = GetUID(l,SplitEmail(pgp_uid),UnknownMap); if UID[0] == None: print "Unassigned key in keyrings: %s, belonging to %s"%(fpr, pgp_uid) if UID[1] != None: for x in UID[1]: print x; print "MISSING " + fpr; continue; UID = UID[0] Rec = [(ldap.MOD_ADD,"keyFingerPrint",fpr)]; Dn = "uid=" + UID + "," + BaseDn; print "Adding key "+fpr,"to",UID; if KeyCount.has_key(UID): KeyCount[UID] = KeyCount[UID] + 1; else: KeyCount[UID] = 1; if NoAct == 1: continue; # Send the modify request l.modify(Dn,Rec); Outstanding = Outstanding + 1; Outstanding = FlushOutstanding(l,Outstanding,1); sys.stdout.flush(); if NoAct == 0: FlushOutstanding(l,Outstanding); print Ignored,"keys already in the directory (ignored)"; # Look for unmatched keys for x in KeyMap.keys(): if KeyMap[x][1] == 0 and not x in pgpkeys_extra: print "key %s belonging to %s removed"%(x,KeyMap[x][0]); if KeyCount.has_key(KeyMap[x][0]) : KeyCount[KeyMap[x][0]] = KeyCount[KeyMap[x][0]] - 1 if KeyCount[KeyMap[x][0]] <= 0: print "**",KeyMap[x][0],"no longer has any keys"; if NoAct == 0: l.modify_s("uid="+KeyMap[x][0]+","+BaseDn,\ [(ldap.MOD_DELETE,"keyFingerPrint",x)]); # vim:set et: # vim:set ts=3: # vim:set shiftwidth=3: