Support for sha256 fingerprint.

Fixes #7628
This commit is contained in:
Fedor Brunner 2014-01-21 15:27:41 +01:00
parent 9110ad90d5
commit 6588636aa5
6 changed files with 68 additions and 27 deletions

View file

@ -354,6 +354,7 @@ class Config:
'warn_when_insecure_ssl_connection': [ opt_bool, True, _('Show a warning dialog before using standard SSL library.') ], 'warn_when_insecure_ssl_connection': [ opt_bool, True, _('Show a warning dialog before using standard SSL library.') ],
'warn_when_insecure_password': [ opt_bool, True, _('Show a warning dialog before sending PLAIN password over a plain connection.') ], 'warn_when_insecure_password': [ opt_bool, True, _('Show a warning dialog before sending PLAIN password over a plain connection.') ],
'ssl_fingerprint_sha1': [ opt_str, '', '', True ], 'ssl_fingerprint_sha1': [ opt_str, '', '', True ],
'ssl_fingerprint_sha256': [ opt_str, '', '', True ],
'ignore_ssl_errors': [ opt_str, '', _('Space separated list of ssl errors to ignore.') ], 'ignore_ssl_errors': [ opt_str, '', _('Space separated list of ssl errors to ignore.') ],
'use_srv': [ opt_bool, True, '', True ], 'use_srv': [ opt_bool, True, '', True ],
'use_custom_host': [ opt_bool, False, '', True ], 'use_custom_host': [ opt_bool, False, '', True ],

View file

@ -1408,27 +1408,46 @@ class Connection(CommonConnection, ConnectionHandlers):
text += _('\nSSL Error: <b>%s</b>') % ssl_error[errnum] text += _('\nSSL Error: <b>%s</b>') % ssl_error[errnum]
else: else:
text += _('\nUnknown SSL error: %d') % errnum text += _('\nUnknown SSL error: %d') % errnum
fingerprint = cert.digest('sha1').decode('utf-8') fingerprint_sha1 = cert.digest('sha1').decode('utf-8')
fingerprint_sha256 = cert.digest('sha256').decode('utf-8')
pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, pem = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
cert).decode('utf-8') cert).decode('utf-8')
gajim.nec.push_incoming_event(SSLErrorEvent(None, conn=self, gajim.nec.push_incoming_event(SSLErrorEvent(None, conn=self,
error_text=text, error_num=errnum, cert=pem, error_text=text, error_num=errnum, cert=pem,
fingerprint=fingerprint, certificate=cert)) fingerprint_sha1=fingerprint_sha1,
fingerprint_sha256=fingerprint_sha256, certificate=cert))
return True return True
if cert: if cert:
fingerprint = cert.digest('sha1').decode('utf-8') fingerprint_sha1 = cert.digest('sha1').decode('utf-8')
saved_fingerprint = gajim.config.get_per('accounts', self.name, fingerprint_sha256 = cert.digest('sha256').decode('utf-8')
saved_fingerprint_sha1 = gajim.config.get_per('accounts', self.name,
'ssl_fingerprint_sha1') 'ssl_fingerprint_sha1')
if saved_fingerprint: if saved_fingerprint_sha1:
# Check sha1 fingerprint # Check sha1 fingerprint
if fingerprint != saved_fingerprint: if fingerprint_sha1 != saved_fingerprint_sha1:
gajim.nec.push_incoming_event(FingerprintErrorEvent(None, gajim.nec.push_incoming_event(FingerprintErrorEvent(None,
conn=self, certificate=cert, conn=self, certificate=cert,
new_fingerprint=fingerprint)) new_fingerprint_sha1=fingerprint_sha1,
new_fingerprint_sha256=fingerprint_sha256))
return True return True
else: else:
gajim.config.set_per('accounts', self.name, gajim.config.set_per('accounts', self.name,
'ssl_fingerprint_sha1', fingerprint) 'ssl_fingerprint_sha1', fingerprint_sha1)
saved_fingerprint_sha256 = gajim.config.get_per('accounts', self.name,
'ssl_fingerprint_sha256')
if saved_fingerprint_sha256:
# Check sha256 fingerprint
if fingerprint_sha256 != saved_fingerprint_sha256:
gajim.nec.push_incoming_event(FingerprintErrorEvent(None,
conn=self, certificate=con.Connection.ssl_certificate,
new_fingerprint_sha1=fingerprint_sha1,
new_fingerprint_sha256=fingerprint_sha256))
return True
else:
gajim.config.set_per('accounts', self.name,
'ssl_fingerprint_sha256', fingerprint_sha256)
if not check_X509.check_certificate(cert, hostname) and \ if not check_X509.check_certificate(cert, hostname) and \
'100' not in gajim.config.get_per('accounts', self.name, '100' not in gajim.config.get_per('accounts', self.name,
'ignore_ssl_errors').split(): 'ignore_ssl_errors').split():
@ -1439,7 +1458,8 @@ class Connection(CommonConnection, ConnectionHandlers):
hostname hostname
gajim.nec.push_incoming_event(SSLErrorEvent(None, conn=self, gajim.nec.push_incoming_event(SSLErrorEvent(None, conn=self,
error_text=txt, error_num=100, cert=pem, error_text=txt, error_num=100, cert=pem,
fingerprint=fingerprint, certificate=cert)) fingerprint_sha1=fingerprint_sha1,
fingerprint_sha256=fingerprint_sha256, certificate=cert))
return True return True
self._register_handlers(con, con_type) self._register_handlers(con, con_type)

View file

@ -1639,12 +1639,14 @@ class NewAccountConnectedEvent(nec.NetworkIncomingEvent):
self.ssl_msg = ssl_error.get(er, _('Unknown SSL error: %d') % \ self.ssl_msg = ssl_error.get(er, _('Unknown SSL error: %d') % \
self.errnum) self.errnum)
self.ssl_cert = '' self.ssl_cert = ''
self.ssl_fingerprint = '' self.ssl_fingerprint_sha1 = ''
self.ssl_fingerprint_sha256 = ''
if self.conn.connection.Connection.ssl_certificate: if self.conn.connection.Connection.ssl_certificate:
cert = self.conn.connection.Connection.ssl_certificate cert = self.conn.connection.Connection.ssl_certificate
self.ssl_cert = OpenSSL.crypto.dump_certificate( self.ssl_cert = OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert).decode('utf-8') OpenSSL.crypto.FILETYPE_PEM, cert).decode('utf-8')
self.ssl_fingerprint = cert.digest('sha1').decode('utf-8') self.ssl_fingerprint_sha1 = cert.digest('sha1').decode('utf-8')
self.ssl_fingerprint_sha256 = cert.digest('sha256').decode('utf-8')
return True return True
class NewAccountNotConnectedEvent(nec.NetworkIncomingEvent): class NewAccountNotConnectedEvent(nec.NetworkIncomingEvent):

View file

@ -3784,7 +3784,9 @@ class AccountCreationWizardWindow:
f.write(self.ssl_cert + '\n\n') f.write(self.ssl_cert + '\n\n')
f.close() f.close()
gajim.connections[self.account].new_account_info[ gajim.connections[self.account].new_account_info[
'ssl_fingerprint_sha1'] = self.ssl_fingerprint 'ssl_fingerprint_sha1'] = self.ssl_fingerprint_sha1
gajim.connections[self.account].new_account_info[
'ssl_fingerprint_sha256'] = self.ssl_fingerprint_sha256
self.notebook.set_current_page(4) # show fom page self.notebook.set_current_page(4) # show fom page
elif cur_page == 4: elif cur_page == 4:
if self.is_form: if self.is_form:
@ -3859,7 +3861,8 @@ class AccountCreationWizardWindow:
self.forward_button.set_sensitive(False) self.forward_button.set_sensitive(False)
self.notebook.set_current_page(4) # show form page self.notebook.set_current_page(4) # show form page
return return
self.ssl_fingerprint = obj.ssl_fingerprint self.ssl_fingerprint_sha1 = obj.ssl_fingerprint_sha1
self.ssl_fingerprint_sha256 = obj.ssl_fingerprint_sha256
self.ssl_cert = obj.ssl_cert self.ssl_cert = obj.ssl_cert
if obj.ssl_msg: if obj.ssl_msg:
# An SSL warning occured, show it # An SSL warning occured, show it
@ -3873,8 +3876,9 @@ class AccountCreationWizardWindow:
'hostname': hostname, 'error': obj.ssl_msg}) 'hostname': hostname, 'error': obj.ssl_msg})
if obj.errnum in (18, 27): if obj.errnum in (18, 27):
text = _('Add this certificate to the list of trusted ' text = _('Add this certificate to the list of trusted '
'certificates.\nSHA1 fingerprint of the certificate:\n%s') \ 'certificates.\nSHA1 fingerprint of the certificate:\n%s'
% obj.ssl_fingerprint '\nSHA256 fingerprint of the certificate:\n%s') \
% (obj.ssl_fingerprint_sha1, obj.ssl_fingerprint_sha256)
self.xml.get_object('ssl_checkbutton').set_label(text) self.xml.get_object('ssl_checkbutton').set_label(text)
else: else:
self.xml.get_object('ssl_checkbutton').set_no_show_all(True) self.xml.get_object('ssl_checkbutton').set_no_show_all(True)

View file

@ -5448,14 +5448,18 @@ Issued on: %(io)s
Expires on: %(eo)s Expires on: %(eo)s
<b>Fingerprint</b> <b>Fingerprint</b>
SHA1 Fingerprint: %(sha1)s''') % { SHA1 Fingerprint: %(sha1)s
SHA256 Fingerprint: %(sha256)s
''') % {
'scn': subject.commonName, 'sorg': subject.organizationName, 'scn': subject.commonName, 'sorg': subject.organizationName,
'sou': subject.organizationalUnitName, 'sou': subject.organizationalUnitName,
'sn': cert.get_serial_number(), 'icn': issuer.commonName, 'sn': cert.get_serial_number(), 'icn': issuer.commonName,
'iorg': issuer.organizationName, 'iorg': issuer.organizationName,
'iou': issuer.organizationalUnitName, 'iou': issuer.organizationalUnitName,
'io': cert.get_notBefore(), 'eo': cert.get_notAfter(), 'io': cert.get_notBefore(), 'eo': cert.get_notAfter(),
'sha1': cert.digest('sha1')}) 'sha1': cert.digest('sha1'),
'sha256': cert.digest('sha256')})
self.set_transient_for(parent) self.set_transient_for(parent)
self.set_title(_('Certificate for account %s') % account) self.set_title(_('Certificate for account %s') % account)

View file

@ -1300,7 +1300,7 @@ class Interface:
obj.exchange_items_list, obj.fjid) obj.exchange_items_list, obj.fjid)
def handle_event_ssl_error(self, obj): def handle_event_ssl_error(self, obj):
# ('SSL_ERROR', account, (text, errnum, cert, sha1_fingerprint)) # ('SSL_ERROR', account, (text, errnum, cert, sha1_fingerprint, sha256_fingerprint))
account = obj.conn.name account = obj.conn.name
server = gajim.config.get_per('accounts', account, 'hostname') server = gajim.config.get_per('accounts', account, 'hostname')
@ -1323,7 +1323,9 @@ class Interface:
f.write(obj.cert + '\n\n') f.write(obj.cert + '\n\n')
f.close() f.close()
gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1', gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1',
obj.fingerprint) obj.fingerprint_sha1)
gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha256',
obj.fingerprint_sha256)
if is_checked[1]: if is_checked[1]:
ignore_ssl_errors = gajim.config.get_per('accounts', account, ignore_ssl_errors = gajim.config.get_per('accounts', account,
'ignore_ssl_errors').split() 'ignore_ssl_errors').split()
@ -1344,8 +1346,9 @@ class Interface:
'server?') % {'error': obj.error_text} 'server?') % {'error': obj.error_text}
if obj.error_num in (18, 27): if obj.error_num in (18, 27):
checktext1 = _('Add this certificate to the list of trusted ' checktext1 = _('Add this certificate to the list of trusted '
'certificates.\nSHA1 fingerprint of the certificate:\n%s') % \ 'certificates.\nSHA1 fingerprint of the certificate:\n%s'
obj.fingerprint '\nSHA256 fingerprint of the certificate:\n%s') % \
(obj.fingerprint_sha1, obj.fingerprint_sha256)
else: else:
checktext1 = '' checktext1 = ''
checktext2 = _('Ignore this error for this certificate.') checktext2 = _('Ignore this error for this certificate.')
@ -1359,12 +1362,14 @@ class Interface:
_('SSL Certificate Verification for %s') % account) _('SSL Certificate Verification for %s') % account)
def handle_event_fingerprint_error(self, obj): def handle_event_fingerprint_error(self, obj):
# ('FINGERPRINT_ERROR', account, (new_fingerprint,)) # ('FINGERPRINT_ERROR', account, (new_fingerprint_sha1,new_fingerprint_sha256,))
account = obj.conn.name account = obj.conn.name
def on_yes(is_checked): def on_yes(is_checked):
del self.instances[account]['online_dialog']['fingerprint_error'] del self.instances[account]['online_dialog']['fingerprint_error']
gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1', gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1',
obj.new_fingerprint) obj.new_fingerprint_sha1)
gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha256',
obj.new_fingerprint_sha256)
# Reset the ignored ssl errors # Reset the ignored ssl errors
gajim.config.set_per('accounts', account, 'ignore_ssl_errors', '') gajim.config.set_per('accounts', account, 'ignore_ssl_errors', '')
obj.conn.ssl_certificate_accepted() obj.conn.ssl_certificate_accepted()
@ -1377,11 +1382,16 @@ class Interface:
pritext = _('SSL certificate error') pritext = _('SSL certificate error')
sectext = _('It seems the SSL certificate of account %(account)s has ' sectext = _('It seems the SSL certificate of account %(account)s has '
'changed or your connection is being hacked.\nOld fingerprint: ' 'changed or your connection is being hacked.\n\nOld SHA-1 fingerprint: '
'%(old)s\nNew fingerprint: %(new)s\n\nDo you still want to connect ' '%(old_sha1)s\nOld SHA-256 fingerprint: %(old_sha256)s\n\n'
'New SHA-1 fingerprint: %(new_sha1)s\nNew SHA-256 fingerprint: '
'%(new_sha256)s\n\nDo you still want to connect '
'and update the fingerprint of the certificate?') % \ 'and update the fingerprint of the certificate?') % \
{'account': account, 'old': gajim.config.get_per('accounts', {'account': account,
account, 'ssl_fingerprint_sha1'), 'new': obj.new_fingerprint} 'old_sha1': gajim.config.get_per('accounts', account, 'ssl_fingerprint_sha1'),
'old_sha256': gajim.config.get_per('accounts', account, 'ssl_fingerprint_sha256'),
'new_sha1': obj.new_fingerprint_sha1,
'new_sha256': obj.new_fingerprint_sha256}
if 'fingerprint_error' in self.instances[account]['online_dialog']: if 'fingerprint_error' in self.instances[account]['online_dialog']:
self.instances[account]['online_dialog']['fingerprint_error'].\ self.instances[account]['online_dialog']['fingerprint_error'].\
destroy() destroy()