1#! /usr/bin/env python3 

2 

3""" Imports a keyring into the database """ 

4# Copyright (C) 2007 Anthony Towns <aj@erisian.com.au> 

5# Copyright (C) 2009 Mark Hymers <mhy@debian.org> 

6 

7# This program is free software; you can redistribute it and/or modify 

8# it under the terms of the GNU General Public License as published by 

9# the Free Software Foundation; either version 2 of the License, or 

10# (at your option) any later version. 

11 

12# This program is distributed in the hope that it will be useful, 

13# but WITHOUT ANY WARRANTY; without even the implied warranty of 

14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

15# GNU General Public License for more details. 

16 

17# You should have received a copy of the GNU General Public License 

18# along with this program; if not, write to the Free Software 

19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

20 

21################################################################################ 

22 

23import sys 

24import apt_pkg 

25 

26from daklib.config import Config 

27from daklib.dbconn import * 

28 

29# Globals 

30Options = None 

31 

32################################################################################ 

33 

34 

35def get_uid_info(session): 

36 byname = {} 

37 byid = {} 

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) 

42 

43 return (byname, byid) 

44 

45 

46def get_fingerprint_info(session): 

47 fins = {} 

48 q = session.execute("SELECT f.fingerprint, f.id, f.uid, f.keyring FROM fingerprint f") 

49 for (fingerprint, fingerprint_id, uid, keyring) in q.fetchall(): 

50 fins[fingerprint] = (uid, fingerprint_id, keyring) 

51 return fins 

52 

53 

54def list_uids(session, pattern): 

55 sql_pattern = f"%{pattern}%" 

56 message = "List UIDs matching pattern %s" % sql_pattern 

57 message += "\n" + ("=" * len(message)) 

58 print(message) 

59 uid_query = session.query(Uid).filter(Uid.uid.ilike(sql_pattern)) 

60 for uid in uid_query.all(): 

61 print("\nuid %s" % uid.uid) 

62 for fp in uid.fingerprint: 

63 print(" fingerprint %s" % fp.fingerprint) 

64 keyring = "unknown" 

65 if fp.keyring: 

66 keyring = fp.keyring.keyring_name 

67 print(" keyring %s" % keyring) 

68 

69################################################################################ 

70 

71 

72def usage(exit_code=0): 

73 print("""Usage: dak import-keyring [OPTION]... [KEYRING] 

74 -h, --help show this help and exit. 

75 -L, --import-ldap-users generate uid entries for keyring from LDAP 

76 -U, --generate-users FMT generate uid entries from keyring as FMT 

77 -l, --list-uids STRING list all uids matching *STRING* 

78 -n, --no-action don't change database""") 

79 sys.exit(exit_code) 

80 

81 

82################################################################################ 

83 

84def main(): 

85 global Options 

86 

87 cnf = Config() 

88 Arguments = [('h', "help", "Import-Keyring::Options::Help"), 

89 ('L', "import-ldap-users", "Import-Keyring::Options::Import-Ldap-Users"), 

90 ('U', "generate-users", "Import-Keyring::Options::Generate-Users", "HasArg"), 

91 ('l', "list-uids", "Import-Keyring::Options::List-UIDs", "HasArg"), 

92 ('n', "no-action", "Import-Keyring::Options::No-Action"), 

93 ] 

94 

95 for i in ["help", "report-changes", "generate-users", 

96 "import-ldap-users", "list-uids", "no-action"]: 

97 key = "Import-Keyring::Options::%s" % i 

98 if key not in cnf: 98 ↛ 95line 98 didn't jump to line 95, because the condition on line 98 was never false

99 cnf[key] = "" 

100 

101 keyring_names = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv) 

102 

103 ### Parse options 

104 

105 Options = cnf.subtree("Import-Keyring::Options") 

106 

107 if Options["Help"]: 

108 usage() 

109 

110 ### Initialise 

111 session = DBConn().session() 

112 

113 if Options["List-UIDs"]: 113 ↛ 114line 113 didn't jump to line 114, because the condition on line 113 was never true

114 list_uids(session, Options["List-UIDs"]) 

115 sys.exit(0) 

116 

117 if len(keyring_names) != 1: 117 ↛ 118line 117 didn't jump to line 118, because the condition on line 117 was never true

118 usage(1) 

119 

120 ### Keep track of changes made 

121 changes = [] # (uid, changes strings) 

122 

123 ### Cache all the existing fingerprint entries 

124 db_fin_info = get_fingerprint_info(session) 

125 

126 ### Parse the keyring 

127 

128 keyringname = keyring_names[0] 

129 keyring = get_keyring(keyringname, session) 

130 if not keyring: 130 ↛ 131line 130 didn't jump to line 131, because the condition on line 130 was never true

131 print("E: Can't load keyring %s from database" % keyringname) 

132 sys.exit(1) 

133 

134 keyring.load_keys(keyringname) 

135 

136 ### Generate new uid entries if they're needed (from LDAP or the keyring) 

137 if Options["Generate-Users"]: 137 ↛ 140line 137 didn't jump to line 140, because the condition on line 137 was never false

138 format = Options["Generate-Users"] 

139 (desuid_byname, desuid_byid) = keyring.generate_users_from_keyring(Options["Generate-Users"], session) 

140 elif Options["Import-Ldap-Users"]: 

141 (desuid_byname, desuid_byid) = keyring.import_users_from_ldap(session) 

142 else: 

143 (desuid_byname, desuid_byid) = ({}, {}) 

144 

145 ### Cache all the existing uid entries 

146 (db_uid_byname, db_uid_byid) = get_uid_info(session) 

147 

148 ### Update full names of applicable users 

149 for keyid in desuid_byid.keys(): 

150 uid = (keyid, desuid_byid[keyid][0]) 

151 name = desuid_byid[keyid][1] 

152 oname = db_uid_byid[keyid][1] 

153 if name and oname != name: 153 ↛ 149line 153 didn't jump to line 149, because the condition on line 153 was never false

154 changes.append((uid[1], "Full name: %s" % (name))) 

155 session.execute("UPDATE uid SET name = :name WHERE id = :keyid", 

156 {'name': name, 'keyid': keyid}) 

157 

158 # The fingerprint table (fpr) points to a uid and a keyring. 

159 # If the uid is being decided here (ldap/generate) we set it to it. 

160 # Otherwise, if the fingerprint table already has a uid (which we've 

161 # cached earlier), we preserve it. 

162 # Otherwise we leave it as None 

163 

164 fpr = {} 

165 for z in keyring.keys.keys(): 

166 keyid = db_uid_byname.get(keyring.keys[z].get("uid", None), [None])[0] 

167 if keyid is None: 167 ↛ 168line 167 didn't jump to line 168, because the condition on line 167 was never true

168 keyid = db_fin_info.get(keyring.keys[z]["fingerprints"][0], [None])[0] 

169 for y in keyring.keys[z]["fingerprints"]: 

170 fpr[y] = (keyid, keyring.keyring_id) 

171 

172 # For any keys that used to be in this keyring, disassociate them. 

173 # We don't change the uid, leaving that for historical info; if 

174 # the id should change, it'll be set when importing another keyring. 

175 

176 for f, (u, fid, kr) in db_fin_info.items(): 

177 if kr != keyring.keyring_id: 177 ↛ 180line 177 didn't jump to line 180, because the condition on line 177 was never false

178 continue 

179 

180 if f in fpr: 

181 continue 

182 

183 changes.append((db_uid_byid.get(u, [None])[0], "Removed key: %s" % (f))) 

184 session.execute("""UPDATE fingerprint 

185 SET keyring = NULL 

186 WHERE id = :fprid""", {'fprid': fid}) 

187 

188 # For the keys in this keyring, add/update any fingerprints that've 

189 # changed. 

190 

191 for f in fpr: 

192 newuid = fpr[f][0] 

193 newuiduid = db_uid_byid.get(newuid, [None])[0] 

194 

195 (olduid, oldfid, oldkid) = db_fin_info.get(f, [-1, -1, -1]) 

196 

197 if olduid is None: 197 ↛ 198line 197 didn't jump to line 198, because the condition on line 197 was never true

198 olduid = -1 

199 

200 if oldkid is None: 200 ↛ 201line 200 didn't jump to line 201, because the condition on line 200 was never true

201 oldkid = -1 

202 

203 if oldfid == -1: 203 ↛ 215line 203 didn't jump to line 215, because the condition on line 203 was never false

204 changes.append((newuiduid, "Added key: %s" % (f))) 

205 fp = Fingerprint() 

206 fp.fingerprint = f 

207 fp.keyring_id = keyring.keyring_id 

208 if newuid: 208 ↛ 211line 208 didn't jump to line 211, because the condition on line 208 was never false

209 fp.uid_id = newuid 

210 

211 session.add(fp) 

212 session.flush() 

213 

214 else: 

215 if newuid and olduid != newuid and olduid == -1: 

216 changes.append((newuiduid, "Linked key: %s" % f)) 

217 changes.append((newuiduid, " (formerly unowned)")) 

218 session.execute("UPDATE fingerprint SET uid = :uid WHERE id = :fpr", 

219 {'uid': newuid, 'fpr': oldfid}) 

220 

221 # Don't move a key from a keyring with a higher priority to a lower one 

222 if oldkid != keyring.keyring_id: 

223 movekey = False 

224 if oldkid == -1: 

225 movekey = True 

226 else: 

227 try: 

228 oldkeyring = session.query(Keyring).filter_by(keyring_id=oldkid).one() 

229 except NotFoundError: 

230 print("ERROR: Cannot find old keyring with id %s" % oldkid) 

231 sys.exit(1) 

232 

233 if oldkeyring.priority < keyring.priority: 

234 movekey = True 

235 

236 # Only change the keyring if it won't result in a loss of permissions 

237 if movekey: 

238 session.execute("""UPDATE fingerprint 

239 SET keyring = :keyring 

240 WHERE id = :fpr""", 

241 {'keyring': keyring.keyring_id, 

242 'fpr': oldfid}) 

243 

244 session.flush() 

245 

246 else: 

247 print("Key %s exists in both %s and %s keyrings. Not demoting." % (f, 

248 oldkeyring.keyring_name, 

249 keyring.keyring_name)) 

250 

251 # All done! 

252 if Options["No-Action"]: 252 ↛ 253line 252 didn't jump to line 253, because the condition on line 252 was never true

253 session.rollback() 

254 else: 

255 session.commit() 

256 

257 # Print a summary 

258 changesd = {} 

259 for (k, v) in changes: 

260 if k not in changesd: 

261 changesd[k] = "" 

262 changesd[k] += " %s\n" % (v) 

263 

264 for k in sorted(changesd): 

265 print("%s\n%s\n" % (k, changesd[k])) 

266 

267################################################################################ 

268 

269 

270if __name__ == '__main__': 

271 main()