3 # This script tries to match key fingerprints from a keyring with user
4 # name in a directory. When an unassigned key is found a heuristic match
5 # against the keys given cn/sn and the directory is performed to try to get
6 # a matching. Generally this works about 90% of the time, matching is fairly
7 # strict. In the event a non-match a fuzzy sounds-alike search is performed
8 # and the results printed to aide the user.
10 # GPG is automatically invoked with the correct magic special options,
11 # pass the names of all the valid key rings on the command line.
13 # The output report will list what actions were taken. Keys that are present
14 # in the directory but not in the key ring will be removed from the
17 import string, re, time, ldap, getopt, sys, pwd, posix;
18 from userdir_ldap import *;
19 from userdir_gpg import *;
21 # This map deals with people who put the wrong sort of stuff in their pgp
26 AddressSplit = re.compile("(.*).*<([^@]*)@([^>]*)>");
28 # Read the override file into the unknown map. The override file is a list
29 # of colon delimited entires mapping PGP email addresess to local users
30 def LoadOverride(File):
31 List = open(File,"r");
33 Line = List.readline();
36 Split = re.split("[:\n]",Line);
37 UnknownMap[Split[0]] = string.strip(Split[1]);
39 # Convert the PGP name string to a uid value
41 # Crack up the email address into a best guess first/middle/last name
42 (cn,mn,sn) = NameSplit(re.sub('["]','',Name[0]))
44 # Brackets anger the ldap searcher
45 cn = re.sub('[(")]','?',cn);
46 sn = re.sub('[(")]','?',sn);
48 # First check the unknown map for the email address
49 if UnknownMap.has_key(Name[1] + '@' + Name[2]):
50 print "unknown map hit for",Name;
51 return UnknownMap[Name[1] + '@' + Name[2]];
53 # Then the cruft component (ie there was no email address to match)
54 if UnknownMap.has_key(Name[2]):
55 print "unknown map hit for",Name;
56 return UnknownMap[Name[2]];
58 # Search for a possible first/last name hit
60 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(&(cn=%s)(sn=%s))"%(cn,sn),["uid"]);
61 except ldap.FILTER_ERROR:
62 print "Filter failure:","(&(cn=%s)(sn=%s))"%(cn,sn);
65 # Hmm, more than one/no return
67 # Key claims a local address
68 if Name[2] == EmailAppend:
70 # Pull out the record for the claimed user
71 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(uid=%s)"%(Name[1]),["uid","sn","cn"]);
73 # We require the UID surname to be someplace in the key name, this
74 # deals with special purpose keys like 'James Troup (Alternate Debian key)'
75 # Some people put their names backwards on their key too.. check that as well
76 if len(Attrs) == 1 and \
77 (string.find(string.lower(sn),string.lower(Attrs[0][1]["sn"][0])) != -1 or \
78 string.find(string.lower(cn),string.lower(Attrs[0][1]["sn"][0])) != -1):
79 print EmailAppend,"hit for",Name;
82 # Attempt to give some best guess suggestions for use in editing the
84 print "None for",Name;
85 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"(sn~=%s)"%(sn),["uid","sn","cn"]);
87 print " But might be:",x[1]["cn"][0],x[1]["sn"][0],"<" + x[1]["uid"][0] + "@debian.org>";
89 return Attrs[0][1]["uid"][0];
94 AdminUser = pwd.getpwuid(posix.getuid())[0];
95 (options, arguments) = getopt.getopt(sys.argv[1:], "au:m:n")
96 for (switch, val) in options:
99 elif (switch == '-m'):
101 elif (switch == '-a'):
103 if len(arguments) == 0:
104 print "Give some keyrings to probe";
107 # Main program starts here
109 # Connect to the ldap server
110 l = ldap.open(LDAPServer);
112 print "Accessing LDAP directory as '" + AdminUser + "'";
113 Password = getpass(AdminUser + "'s password: ");
114 UserDn = "uid=" + AdminUser + "," + BaseDn;
115 l.simple_bind_s(UserDn,Password);
117 l.simple_bind_s("","");
119 # Download the existing key list and put it into a map
120 print "Fetching key list..",
122 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyfingerprint=*",["keyfingerprint","uid"]);
127 # Sense a bad fingerprint.. Slapd has problems, it will store a null
128 # value that ldapsearch doesn't show up.. detect and remove
129 if len(x[1]["keyfingerprint"]) == 0 or x[1]["keyfingerprint"][0] == "":
131 print "Fixing bad fingerprint for",x[1]["uid"][0],
134 l.modify_s("uid="+x[1]["uid"][0]+","+BaseDn,\
135 [(ldap.MOD_DELETE,"keyfingerprint",None)]);
137 for I in x[1]["keyfingerprint"]:
138 KeyMap[I] = [x[1]["uid"][0],0];
139 if KeyCount.has_key(x[1]["uid"][0]):
140 KeyCount[x[1]["uid"][0]] = KeyCount[x[1]["uid"][0]] + 1;
142 KeyCount[x[1]["uid"][0]] = 1;
148 # Popen GPG with the correct magic special options
149 Args = [GPGPath] + GPGBasicOptions;
151 Args.append("--keyring");
152 if string.find(x,"/") == -1:
156 Args = Args + GPGSearchOptions + [" 2> /dev/null"]
157 Keys = os.popen(string.join(Args," "),"r");
159 # Loop over the GPG key file
163 Line = Keys.readline();
167 Split = string.split(Line,":");
168 if len(Split) < 8 or Split[0] != "pub":
172 Line2 = Keys.readline();
175 Split2 = string.split(Line2,":");
176 if len(Split2) < 11 or Split2[0] != "fpr":
182 if KeyMap.has_key(Split2[9]):
183 Ignored = Ignored + 1;
184 # print "Ignoring keyID",Split2[9],"belonging to",KeyMap[Split2[9]][0];
185 KeyMap[Split2[9]][1] = 1;
188 Match = AddressSplit.match(Split[9]);
190 UID = GetUID(l,("","",Split[9]));
192 UID = GetUID(l,Match.groups());
195 print "MISSING 0x" + Split2[9];
198 Rec = [(ldap.MOD_ADD,"keyfingerprint",Split2[9])];
199 Dn = "uid=" + UID + "," + BaseDn;
200 print "Adding keyID",Split2[9],"to",UID;
201 if KeyCount.has_key(UID):
202 KeyCount[UID] = KeyCount[UID] + 1;
209 # Send the modify request
211 Outstanding = Outstanding + 1;
212 Outstanding = FlushOutstanding(l,Outstanding,1);
216 FlushOutstanding(l,Outstanding);
218 if Keys.close() != None:
219 raise "Error","GPG failed"
221 print Ignored,"keys already in the directory (ignored)";
223 # Look for unmatched keys
224 for x in KeyMap.keys():
225 if KeyMap[x][1] == 0:
226 print "keyID",x,"belonging to",KeyMap[x][0],"removed";
227 if KeyCount.has_key(KeyMap[x][0]) :
228 KeyCount[KeyMap[x][0]] = KeyCount[KeyMap[x][0]] - 1
229 if KeyCount[KeyMap[x][0]] <= 0:
230 print "**",KeyMap[x][0],"no longer has any keys";
232 l.modify_s("uid="+KeyMap[x][0]+","+BaseDn,\
233 [(ldap.MOD_DELETE,"keyfingerprint",x)]);