1 """
2 A dialog box for confirming GPG keys.
3 """
4
5
6
7
8 from zeroinstall import _, translation
9
10 import gtk
11 from zeroinstall.injector.model import SafeException
12 from zeroinstall.injector import gpg, trust
13 from zeroinstall.support import tasks, unicode
14 from zeroinstall.gtkui import help_box, gtkutils
15
16 -def frame(page, title, content, expand = False):
17 frame = gtk.Frame()
18 label = gtk.Label()
19 label.set_markup('<b>%s</b>' % title)
20 frame.set_label_widget(label)
21 frame.set_shadow_type(gtk.SHADOW_NONE)
22 if type(content) in (str, unicode):
23 content = gtk.Label(content)
24 content.set_alignment(0, 0.5)
25 content.set_selectable(True)
26 frame.add(content)
27 if hasattr(content, 'set_padding'):
28 content.set_padding(8, 4)
29 else:
30 content.set_border_width(8)
31 page.pack_start(frame, expand, True, 0)
32
34 s = fp[0:4]
35 for x in range(4, len(fp), 4):
36 s += ' ' + fp[x:x + 4]
37 return s
38
40 label = gtk.Label(text)
41 label.set_alignment(0, 0.5)
42 label.set_selectable(True)
43 return label
44
46 def text(parent):
47 text = ""
48 for node in parent.childNodes:
49 if node.nodeType == node.TEXT_NODE:
50 text = text + node.data
51 return text
52
53 hints = gtk.VBox(False, 4)
54
55 shown = set()
56 def add_hints():
57 infos = set(key_info_fetcher.info) - shown
58 for info in infos:
59 hints.add(make_hint(info.getAttribute("vote"), text(info)))
60 shown.add(info)
61
62 if not(key_info_fetcher.blocker or shown):
63 hints.add(make_hint("bad", _('Warning: Nothing known about this key!')))
64
65 if key_info_fetcher.blocker:
66 status = left(key_info_fetcher.status)
67 hints.add(status)
68
69 @tasks.async
70 def update_when_ready():
71 while key_info_fetcher.blocker:
72 yield key_info_fetcher.blocker, closed
73 if closed.happened:
74
75 return
76 add_hints()
77 status.destroy()
78 update_when_ready()
79 else:
80 add_hints()
81
82 hints.show()
83 return hints
84
86 hint_icon = gtk.Image()
87 if vote == "good":
88 hint_icon.set_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_BUTTON)
89 else:
90 hint_icon.set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_BUTTON)
91 hint = left(hint_text)
92 hint.set_line_wrap(True)
93 hint_hbox = gtk.HBox(False, 4)
94 hint_hbox.pack_start(hint_icon, False, True, 0)
95 hint_hbox.pack_start(hint, True, True, 0)
96 hint_icon.set_alignment(0, 0)
97 hint_hbox.show_all()
98 return hint_hbox
99
101 """Display a dialog box asking the user to confirm that one of the
102 keys is trusted for this domain.
103 """
104 parent = None
105 closed = None
106
107 - def __init__(self, pending, valid_sigs, parent):
108 """@since: 0.42"""
109 assert valid_sigs
110
111 gtk.Dialog.__init__(self)
112 if hasattr(self, 'set_has_separator'):
113 self.set_has_separator(False)
114 self.set_position(gtk.WIN_POS_CENTER)
115 self.set_transient_for(parent)
116
117 self.closed = tasks.Blocker(_("confirming keys with user"))
118
119 domain = trust.domain_from_url(pending.url)
120 assert domain
121
122 def destroy(box):
123 self.closed.trigger()
124
125 self.connect('destroy', destroy)
126
127 self.set_title(_('Confirm trust'))
128
129 vbox = gtk.VBox(False, 4)
130 vbox.set_border_width(4)
131 self.vbox.pack_start(vbox, True, True, 0)
132
133 notebook = gtk.Notebook()
134
135 if len(valid_sigs) == 1:
136 notebook.set_show_tabs(False)
137
138 label = left(_('Checking: %s') % pending.url)
139 label.set_padding(4, 4)
140 vbox.pack_start(label, False, True, 0)
141
142 currently_trusted_keys = trust.trust_db.get_keys_for_domain(domain)
143 if currently_trusted_keys:
144 keys = [gpg.load_key(fingerprint) for fingerprint in currently_trusted_keys]
145 descriptions = [_("%(key_name)s\n(fingerprint: %(key_fingerprint)s)") % {'key_name': key.name, 'key_fingerprint': pretty_fp(key.fingerprint)}
146 for key in keys]
147 else:
148 descriptions = [_('None')]
149 frame(vbox, _('Keys already approved for "%s"') % domain, '\n'.join(descriptions))
150
151 label = left(translation.ngettext('This key signed the feed:', 'These keys signed the feed:', len(valid_sigs)))
152
153 label.set_padding(4, 4)
154 vbox.pack_start(label, False, True, 0)
155
156 vbox.pack_start(notebook, True, True, 0)
157
158 self.add_button(gtk.STOCK_HELP, gtk.RESPONSE_HELP)
159 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
160 self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
161 self.set_default_response(gtk.RESPONSE_OK)
162
163 trust_checkbox = {}
164 def ok_sensitive():
165 trust_any = False
166 for toggle in trust_checkbox.values():
167 if toggle.get_active():
168 trust_any = True
169 break
170 self.set_response_sensitive(gtk.RESPONSE_OK, trust_any)
171
172 first = True
173 for sig in valid_sigs:
174 if hasattr(sig, 'get_details'):
175 name = '<unknown>'
176 details = sig.get_details()
177 for item in details:
178 if item[0] == 'uid' and len(item) > 9:
179 name = item[9]
180 break
181 else:
182 name = None
183 page = gtk.VBox(False, 4)
184 page.set_border_width(8)
185
186 frame(page, _('Fingerprint'), pretty_fp(sig.fingerprint))
187
188 if name is not None:
189 frame(page, _('Claimed identity'), name)
190
191 frame(page, _('Unreliable hints database says'), make_hints_area(self.closed, valid_sigs[sig]))
192
193 already_trusted = trust.trust_db.get_trust_domains(sig.fingerprint)
194 if already_trusted:
195 frame(page, _('You already trust this key for these domains'),
196 '\n'.join(already_trusted))
197
198 trust_checkbox[sig] = gtk.CheckButton(_('_Trust this key'))
199 page.pack_start(trust_checkbox[sig], False, True, 0)
200 trust_checkbox[sig].connect('toggled', lambda t: ok_sensitive())
201
202 notebook.append_page(page, gtk.Label(name or 'Signature'))
203
204 if first:
205 trust_checkbox[sig].set_active(True)
206 first = False
207
208 ok_sensitive()
209 self.vbox.show_all()
210
211 if len(valid_sigs) == 1:
212 for box in trust_checkbox.values():
213 box.hide()
214
215 def response(box, resp):
216 if resp == gtk.RESPONSE_HELP:
217 trust_help.display()
218 return
219 if resp == gtk.RESPONSE_OK:
220 to_trust = [sig for sig in trust_checkbox if trust_checkbox[sig].get_active()]
221
222 if not self._confirm_unknown_keys(to_trust, valid_sigs):
223 return
224
225 self.trust_keys(to_trust, domain)
226 self.destroy()
227 self.connect('response', response)
228
240
242 """Check the key-info server's results for these keys. If we don't know any of them,
243 ask for extra confirmation from the user.
244 @param to_trust: the signatures the user wants to trust
245 @return: True to continue"""
246
247 def is_unknown(sig):
248 for note in valid_sigs[sig].info:
249 if note.getAttribute("vote") == "good":
250 return False
251 return True
252 unknown = [sig for sig in to_trust if is_unknown(sig)]
253
254 if unknown:
255 if len(unknown) == 1:
256 msg = _('WARNING: you are confirming a key which was not known to the key server. Are you sure?')
257 else:
258 msg = _('WARNING: you are confirming keys which were not known to the key server. Are you sure?')
259
260 box = gtk.MessageDialog(self,
261 gtk.DIALOG_DESTROY_WITH_PARENT,
262 gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL,
263 msg)
264 box.set_position(gtk.WIN_POS_CENTER)
265 response = box.run()
266 box.destroy()
267 return response == gtk.RESPONSE_OK
268
269 return True
270
271 trust_help = help_box.HelpBox(_("Trust Help"),
272 (_('Overview'), '\n' +
273 _("""When you run a program, it typically has access to all your files and can generally do \
274 anything that you're allowed to do (delete files, send emails, etc). So it's important \
275 to make sure that you don't run anything malicious.""")),
276
277 (_('Digital signatures'), '\n' +
278 _("""Each software author creates a 'key-pair'; a 'public key' and a 'private key'. Without going \
279 into the maths, only something encrypted with the private key will decrypt with the public key.
280
281 So, when a programmer releases some software, they encrypt it with their private key (which no-one \
282 else has). When you download it, the injector checks that it decrypts using their public key, thus \
283 proving that it came from them and hasn't been tampered with.""")),
284
285 (_('Trust'), '\n' +
286 _("""After the injector has checked that the software hasn't been modified since it was signed with \
287 the private key, you still have the following problems:
288
289 1. Does the public key you have really belong to the author?
290 2. Even if the software really did come from that person, do you trust them?""")),
291
292 (_('Key fingerprints'), '\n' +
293 _("""To confirm (1), you should compare the public key you have with the genuine one. To make this \
294 easier, the injector displays a 'fingerprint' for the key. Look in mailing list postings or some \
295 other source to check that the fingerprint is right (a different key will have a different \
296 fingerprint).
297
298 You're trying to protect against the situation where an attacker breaks into a web site \
299 and puts up malicious software, signed with the attacker's private key, and puts up the \
300 attacker's public key too. If you've downloaded this software before, you \
301 should be suspicious that you're being asked to confirm another key!""")),
302
303 (_('Reputation'), '\n' +
304 _("""In general, most problems seem to come from malicous and otherwise-unknown people \
305 replacing software with modified versions, or creating new programs intended only to \
306 cause damage. So, check your programs are signed by a key with a good reputation!""")))
307