Package zeroinstall :: Package gtkui :: Module trust_box
[frames] | no frames]

Source Code for Module zeroinstall.gtkui.trust_box

  1  """ 
  2  A dialog box for confirming GPG keys. 
  3  """ 
  4   
  5  # Copyright (C) 2009, Thomas Leonard 
  6  # -*- coding: utf-8 -*- 
  7  # See the README file for details, or visit http://0install.net. 
  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
33 -def pretty_fp(fp):
34 s = fp[0:4] 35 for x in range(4, len(fp), 4): 36 s += ' ' + fp[x:x + 4] 37 return s
38
39 -def left(text):
40 label = gtk.Label(text) 41 label.set_alignment(0, 0.5) 42 label.set_selectable(True) 43 return label
44
45 -def make_hints_area(closed, key_info_fetcher):
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 # The dialog box was closed. Stop updating. 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
85 -def make_hint(vote, hint_text):
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
100 -class TrustBox(gtk.Dialog):
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 = {} # Sig -> CheckButton 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
229 - def trust_keys(self, agreed_sigs, domain):
230 assert domain 231 try: 232 for sig in agreed_sigs: 233 trust.trust_db.trust_key(sig.fingerprint, domain) 234 235 trust.trust_db.notify() 236 except Exception as ex: 237 gtkutils.show_message_box(self, str(ex), gtk.MESSAGE_ERROR) 238 if not isinstance(ex, SafeException): 239 raise
240
241 - def _confirm_unknown_keys(self, to_trust, valid_sigs):
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