A set of copyright headers
[mirror/userdir-ldap.git] / ud-gpgimport
1 #!/usr/bin/env python
2 # -*- mode: python -*-
3
4 #   Copyright (c) 1999-2000  Jason Gunthorpe <jgg@debian.org>
5 #   Copyright (c) 2004       Joey Schulze <joey@debian.org>
6 #   Copyright (c) 2008, 2009 Peter Palfrader <peter@palfrader.org>
7 #
8 #   This program is free software; you can redistribute it and/or modify
9 #   it under the terms of the GNU General Public License as published by
10 #   the Free Software Foundation; either version 2 of the License, or
11 #   (at your option) any later version.
12 #
13 #   This program is distributed in the hope that it will be useful,
14 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #   GNU General Public License for more details.
17 #
18 #   You should have received a copy of the GNU General Public License
19 #   along with this program; if not, write to the Free Software
20 #   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 # This script tries to match key fingerprints from a keyring with user
23 # name in a directory. When an unassigned key is found a heuristic match
24 # against the keys given cn/sn and the directory is performed to try to get
25 # a matching. Generally this works about 90% of the time, matching is fairly
26 # strict. In the event a non-match a fuzzy sounds-alike search is performed
27 # and the results printed to aide the user.
28 #
29 # GPG is automatically invoked with the correct magic special options,
30 # pass the names of all the valid key rings on the command line.
31 #
32 # The output report will list what actions were taken. Keys that are present
33 # in the directory but not in the key ring will be removed from the 
34 # directory. 
35
36 import re, time, ldap, getopt, sys, pwd, os;
37 from userdir_ldap import *;
38 from userdir_gpg import *;
39
40 # This map deals with people who put the wrong sort of stuff in their pgp
41 # key entries
42 UnknownMap = {};
43 NoAct = 1;
44
45 # Read the override file into the unknown map. The override file is a list
46 # of colon delimited entires mapping PGP email addresess to local users
47 def LoadOverride(File):
48    List = open(File,"r");
49    while(1):
50       Line = List.readline();
51       if Line == "":
52          break;
53       Split = re.split("[:\n]",Line);
54       UnknownMap[Split[0]] = Split[1].strip()
55
56 # Process options
57 AdminUser = pwd.getpwuid(os.getuid())[0];
58 (options, arguments) = getopt.getopt(sys.argv[1:], "au:m:n")
59 for (switch, val) in options:
60    if (switch == '-u'):
61       AdminUser = val
62    elif (switch == '-m'):
63        LoadOverride(val);
64    elif (switch == '-a'):
65        NoAct = 0;
66
67
68 # Main program starts here
69
70 # Connect to the ldap server
71 if NoAct == 0:
72    l = passwdAccessLDAP(BaseDn, AdminUser)
73 else:
74    l = connectLDAP()
75    l.simple_bind_s("","");
76
77 # Download the existing key list and put it into a map
78 print "Fetching key list..",
79 sys.stdout.flush();
80 Attrs = l.search_s(BaseDn,ldap.SCOPE_ONELEVEL,"keyFingerPrint=*",["keyFingerPrint","uid"]);
81 KeyMap = {};
82 KeyCount = {};
83 for x in Attrs:
84   try:
85      # Sense a bad fingerprint.. Slapd has problems, it will store a null
86      # value that ldapsearch doesn't show up.. detect and remove
87      if len(x[1]["keyFingerPrint"]) == 0 or x[1]["keyFingerPrint"][0] == "":
88        print;
89        print "Fixing bad fingerprint for",x[1]["uid"][0],
90        sys.stdout.flush();
91        if NoAct == 0:
92          l.modify_s("uid="+x[1]["uid"][0]+","+BaseDn,\
93                      [(ldap.MOD_DELETE,"keyFingerPrint",None)]);
94      else:
95        for I in x[1]["keyFingerPrint"]:
96          KeyMap[I] = [x[1]["uid"][0],0];
97          if KeyCount.has_key(x[1]["uid"][0]):
98             KeyCount[x[1]["uid"][0]] = KeyCount[x[1]["uid"][0]] + 1;
99          else:
100             KeyCount[x[1]["uid"][0]] = 1;
101   except:
102      continue;
103 Attrs = None;
104 print;
105
106 # Popen GPG with the correct magic special options
107 ClearKeyrings()
108 if len(arguments) == 0:
109    print "Using default keyrings: %s"%ConfModule.add_keyrings;
110    SetKeyrings(ConfModule.add_keyrings.split(":"))
111 for x in arguments:
112    if x.find("/") == -1:
113       x= "./"+x
114    SetKeyrings( [x] )
115
116 Args = [GPGPath] + GPGBasicOptions + GPGKeyRings + GPGSearchOptions + [" 2> /dev/null"]
117 Keys = os.popen(" ".join(Args),"r");
118
119 # Loop over the GPG key file
120 Outstanding = 0;
121 Ignored = 0;
122 SeenKeys = {};
123 while(1):
124    Line = Keys.readline();
125    if Line == "":
126       break;
127    
128    Split = Line.split(":")
129    if len(Split) < 8 or Split[0] != "pub":
130       continue;
131
132    while (1):
133        Line2 = Keys.readline();
134        if Line2 == "":
135           break;
136        Split2 = Line2.split(":");
137        if len(Split2) < 11 or Split2[0] != "fpr":
138           continue;
139        break;
140    if Line2 == "":
141       break;
142
143    if SeenKeys.has_key(Split2[9]):
144       print "Dup key 0x",Split2[9],"belonging to",KeyMap[Split2[9]][0];
145       continue;
146    SeenKeys[Split2[9]] = None;
147
148    if KeyMap.has_key(Split2[9]):
149       Ignored = Ignored + 1;
150       # print "Ignoring keyID",Split2[9],"belonging to",KeyMap[Split2[9]][0];
151       KeyMap[Split2[9]][1] = 1;
152       continue;
153       
154    UID = GetUID(l,SplitEmail(Split[9]),UnknownMap);
155    if UID[0] == None:
156       print "None for",SplitEmail(Split[9]),"'%s'"%(Split[9]);
157       if UID[1] != None: 
158          for x in UID[1]: print x;
159       print "MISSING 0x" + Split2[9];
160       continue;
161
162    UID = UID[0]
163    Rec = [(ldap.MOD_ADD,"keyFingerPrint",Split2[9])];
164    Dn = "uid=" + UID + "," + BaseDn;
165    print "Adding key 0x"+Split2[9],"to",UID;
166    if KeyCount.has_key(UID):
167       KeyCount[UID] = KeyCount[UID] + 1;
168    else:
169       KeyCount[UID] = 1;
170    
171    if NoAct == 1:
172       continue;
173
174    # Send the modify request
175    l.modify(Dn,Rec);
176    Outstanding = Outstanding + 1;
177    Outstanding = FlushOutstanding(l,Outstanding,1);
178    sys.stdout.flush();
179
180 if NoAct == 0:
181    FlushOutstanding(l,Outstanding);
182
183 if Keys.close() != None:
184    raise "Error","GPG failed"
185
186 print Ignored,"keys already in the directory (ignored)";
187
188 # Look for unmatched keys
189 for x in KeyMap.keys():
190    if KeyMap[x][1] == 0:
191       print "key 0x%s belonging to %s removed"%(x,KeyMap[x][0]);
192       if KeyCount.has_key(KeyMap[x][0]) :
193          KeyCount[KeyMap[x][0]] = KeyCount[KeyMap[x][0]] - 1
194          if KeyCount[KeyMap[x][0]] <= 0:
195             print "**",KeyMap[x][0],"no longer has any keys";
196       if NoAct == 0:
197          l.modify_s("uid="+KeyMap[x][0]+","+BaseDn,\
198                      [(ldap.MOD_DELETE,"keyFingerPrint",x)]);
199