3 """ Imports a keyring into the database """
4 # Copyright (C) 2007 Anthony Towns <aj@erisian.com.au>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 ################################################################################
23 import apt_pkg, ldap, email.Utils
25 from daklib.config import Config
26 from daklib.dbconn import *
27 from daklib import utils
33 ################################################################################
35 def get_uid_info(session):
38 q = session.execute("SELECT id, uid, name FROM uid")
39 for (keyid, uid, name) in q.fetchall():
40 byname[uid] = (keyid, name)
41 byid[keyid] = (uid, name)
44 def get_fingerprint_info(session):
46 q = session.execute("SELECT f.fingerprint, f.id, f.uid, f.keyring FROM fingerprint f")
47 for (fingerprint, fingerprint_id, uid, keyring) in q.fetchall():
48 fins[fingerprint] = (uid, fingerprint_id, keyring)
51 ################################################################################
53 def get_ldap_name(entry):
55 for k in ["cn", "mn", "sn"]:
57 if ret and ret[0] != "" and ret[0] != "-":
61 ################################################################################
63 class Keyring(object):
64 gpg_invocation = "gpg --no-default-keyring --keyring %s" +\
65 " --with-colons --fingerprint --fingerprint"
69 def de_escape_gpg_str(self, str):
70 esclist = re.split(r'(\\x..)', str)
71 for x in range(1,len(esclist),2):
72 esclist[x] = "%c" % (int(esclist[x][2:],16))
73 return "".join(esclist)
75 def __init__(self, keyring):
77 k = os.popen(self.gpg_invocation % keyring, "r")
80 fpr_lookup = self.fpr_lookup
82 for line in k.xreadlines():
83 field = line.split(":")
86 (name, addr) = email.Utils.parseaddr(field[9])
87 name = re.sub(r"\s*[(].*[)]", "", name)
88 if name == "" or addr == "" or "@" not in addr:
91 name = self.de_escape_gpg_str(name)
92 keys[key] = {"email": addr}
93 if name != "": keys[key]["name"] = name
94 keys[key]["aliases"] = [name]
95 keys[key]["fingerprints"] = []
97 elif key and field[0] == "sub" and len(field) >= 12:
98 signingkey = ("s" in field[11])
99 elif key and field[0] == "uid":
100 (name, addr) = email.Utils.parseaddr(field[9])
101 if name and name not in keys[key]["aliases"]:
102 keys[key]["aliases"].append(name)
103 elif signingkey and field[0] == "fpr":
104 keys[key]["fingerprints"].append(field[9])
105 fpr_lookup[field[9]] = key
107 def generate_desired_users(self):
108 if Options["Generate-Users"]:
109 format = Options["Generate-Users"]
110 return self.generate_users_from_keyring(format)
111 if Options["Import-Ldap-Users"]:
112 return self.import_users_from_ldap()
115 def import_users_from_ldap(self):
116 LDAPDn = self.cnf["Import-LDAP-Fingerprints::LDAPDn"]
117 LDAPServer = self.cnf["Import-LDAP-Fingerprints::LDAPServer"]
118 l = ldap.open(LDAPServer)
119 l.simple_bind_s("","")
120 Attrs = l.search_s(LDAPDn, ldap.SCOPE_ONELEVEL,
121 "(&(keyfingerprint=*)(gidnumber=%s))" % (self.cnf["Import-Users-From-Passwd::ValidGID"]),
122 ["uid", "keyfingerprint", "cn", "mn", "sn"])
129 fpr_lookup = self.fpr_lookup
133 uid = entry["uid"][0]
134 name = get_ldap_name(entry)
135 fingerprints = entry["keyFingerPrint"]
137 for f in fingerprints:
138 key = fpr_lookup.get(f, None)
139 if key not in keys: continue
140 keys[key]["uid"] = uid
142 if keyid != None: continue
143 keyid = database.get_or_set_uid_id(uid)
144 byuid[keyid] = (uid, name)
145 byname[uid] = (keyid, name)
147 return (byname, byuid)
149 def generate_users_from_keyring(self, format):
154 for x in keys.keys():
155 if keys[x]["email"] == "invalid-uid":
157 keys[x]["uid"] = format % "invalid-uid"
159 uid = format % keys[x]["email"]
160 keyid = database.get_or_set_uid_id(uid)
161 byuid[keyid] = (uid, keys[x]["name"])
162 byname[uid] = (keyid, keys[x]["name"])
165 uid = format % "invalid-uid"
166 keyid = database.get_or_set_uid_id(uid)
167 byuid[keyid] = (uid, "ungeneratable user id")
168 byname[uid] = (keyid, "ungeneratable user id")
169 return (byname, byuid)
171 ################################################################################
173 def usage (exit_code=0):
174 print """Usage: dak import-keyring [OPTION]... [KEYRING]
175 -h, --help show this help and exit.
176 -L, --import-ldap-users generate uid entries for keyring from LDAP
177 -U, --generate-users FMT generate uid entries from keyring as FMT"""
181 ################################################################################
187 Arguments = [('h',"help","Import-Keyring::Options::Help"),
188 ('L',"import-ldap-users","Import-Keyring::Options::Import-Ldap-Users"),
189 ('U',"generate-users","Import-Keyring::Options::Generate-Users", "HasArg"),
192 for i in [ "help", "report-changes", "generate-users", "import-ldap-users" ]:
193 if not cnf.has_key("Import-Keyring::Options::%s" % (i)):
194 cnf["Import-Keyring::Options::%s" % (i)] = ""
196 keyring_names = apt_pkg.ParseCommandLine(cnf.Cnf, Arguments, sys.argv)
200 Options = cnf.SubTree("Import-Keyring::Options")
204 if len(keyring_names) != 1:
207 ### Keep track of changes made
209 changes = [] # (uid, changes strings)
212 session = DBConn().session()
214 ### Cache all the existing fingerprint entries
215 db_fin_info = get_fingerprint_info(session)
217 ### Parse the keyring
219 keyringname = keyring_names[0]
220 keyring = Keyring(keyringname)
223 if cnf.has_key("Import-Keyring::"+keyringname+"::Debian-Maintainer"):
224 session.execute("UPDATE keyrings SET debian_maintainer = :dm WHERE name = :name",
225 {'dm': cnf["Import-Keyring::"+keyringname+"::Debian-Maintainer"],
226 'name': keyringname.split("/")[-1]})
228 is_dm = cnf["Import-Keyring::"+keyringname+"::Debian-Maintainer"]
230 keyring_id = database.get_or_set_keyring_id(
231 keyringname.split("/")[-1])
233 ### Generate new uid entries if they're needed (from LDAP or the keyring)
234 (desuid_byname, desuid_byid) = keyring.generate_desired_users()
236 ### Cache all the existing uid entries
237 (db_uid_byname, db_uid_byid) = get_uid_info(session)
239 ### Update full names of applicable users
240 for keyid in desuid_byid.keys():
241 uid = (keyid, desuid_byid[keyid][0])
242 name = desuid_byid[keyid][1]
243 oname = db_uid_byid[keyid][1]
244 if name and oname != name:
245 changes.append((uid[1], "Full name: %s" % (name)))
246 session.execute("UPDATE uid SET name = :name WHERE id = :keyid",
247 {'name': name, 'keyid': keyid})
249 # The fingerprint table (fpr) points to a uid and a keyring.
250 # If the uid is being decided here (ldap/generate) we set it to it.
251 # Otherwise, if the fingerprint table already has a uid (which we've
252 # cached earlier), we preserve it.
253 # Otherwise we leave it as None
256 for z in keyring.keys.keys():
257 keyid = db_uid_byname.get(keyring.keys[z].get("uid", None), [None])[0]
259 keyid = db_fin_info.get(keyring.keys[z]["fingerprints"][0], [None])[0]
260 for y in keyring.keys[z]["fingerprints"]:
261 fpr[y] = (keyid,keyring_id)
263 # For any keys that used to be in this keyring, disassociate them.
264 # We don't change the uid, leaving that for historical info; if
265 # the id should change, it'll be set when importing another keyring.
267 for f,(u,fid,kr) in db_fin_info.iteritems():
268 if kr != keyring_id: continue
269 if f in fpr: continue
270 changes.append((db_uid_byid.get(u, [None])[0], "Removed key: %s" % (f)))
271 session.execute("UPDATE fingerprint SET keyring = NULL WHERE id = :fprid", {'fprid': fid})
273 # For the keys in this keyring, add/update any fingerprints that've
278 newuiduid = db_uid_byid.get(newuid, [None])[0]
279 (olduid, oldfid, oldkid) = db_fin_info.get(f, [-1,-1,-1])
280 if olduid == None: olduid = -1
281 if oldkid == None: oldkid = -1
283 changes.append((newuiduid, "Added key: %s" % (f)))
285 session.execute("""INSERT INTO fingerprint (fingerprint, uid, keyring)
286 VALUES (:fpr, :uid, :keyring)""",
287 {'fpr': f, 'uid': uid, 'keyring': keyring_id})
289 session.execute("""INSERT INTO fingerprint (fingerprint, keyring)
290 VALUES (:fpr, :keyring)""",
291 {'fpr': f, 'keyring': keyring_id})
293 if newuid and olduid != newuid:
295 changes.append((newuiduid, "Linked key: %s" % f))
296 changes.append((newuiduid, " (formerly belonging to %s)" % (db_uid_byid[olduid][0])))
298 changes.append((newuiduid, "Linked key: %s" % f))
299 changes.append((newuiduid, " (formerly unowned)"))
300 session.execute("UPDATE fingerprint SET uid = :uid WHERE id = :fpr",
301 {'uid': newuid, 'fpr': oldfid})
303 if oldkid != keyring_id:
304 # Only change the keyring if it won't result in a loss of permissions
305 q = session.execute("SELECT debian_maintainer FROM keyrings WHERE id = :keyring",
306 {'keyring': keyring_id})
307 if is_dm == "false" and not q.fetchall()[0][0]:
308 session.execute("UPDATE fingerprint SET keyring = :keyring WHERE id = :fpr",
309 {'keyring': keyring_id, 'fpr': oldfid})
311 print "Key %s exists in both DM and DD keyrings. Not demoting." % (f)
317 for (k, v) in changes:
318 if k not in changesd: changesd[k] = ""
319 changesd[k] += " %s\n" % (v)
321 keys = changesd.keys()
324 print "%s\n%s\n" % (k, changesd[k])
326 ################################################################################
328 if __name__ == '__main__':