Ticket #720: ssl_cert_verif.2.diff

File ssl_cert_verif.2.diff, 11.5 KB (added by asterix, 2 years ago)
  • gajim.py

     
    21662166                instance = data[1] 
    21672167                instance.unique_room_id_error(data[0]) 
    21682168 
     2169        def handle_event_ssl_error(self, account, data): 
     2170                # ('SSL_ERROR', account, (text, cert, sha1_fingerprint)) 
     2171                server = gajim.config.get_per('accounts', account, 'hostname') 
     2172                def on_ok(is_checked): 
     2173                        if is_checked: 
     2174                                f = open(gajim.MY_CACERTS, 'a') 
     2175                                f.write(server + '\n') 
     2176                                f.write(data[1] + '\n\n') 
     2177                                f.close() 
     2178                                gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1', 
     2179                                        data[2]) 
     2180                        gajim.connections[account].ssl_certificate_accepted() 
     2181                def on_cancel(): 
     2182                        gajim.connections[account].disconnect(on_purpose=True) 
     2183                        self.handle_event_status(account, 'offline') 
     2184                pritext = _('Error verifying SSL certificate') 
     2185                sectext = _('There was an error verifying the SSL certificate of your jabber server: %(error)s\nDo you still want to connect to this server?') % {'error': data[0]} 
     2186                checktext = _('Add this certificate to the list of trusted certificates.\nSHA1 fingerprint of the certificate:\n%s') % data[2] 
     2187                dialogs.ConfirmationDialogCheck(pritext, sectext, checktext, 
     2188                        on_response_ok=on_ok, on_response_cancel=on_cancel) 
     2189 
     2190        def handle_event_fingerprint_error(self, account, data): 
     2191                # ('FINGERPRINT_ERROR', account, (fingerprint,)) 
     2192                def on_yes(widget): 
     2193                        dialog.destroy() 
     2194                        gajim.config.set_per('accounts', account, 'ssl_fingerprint_sha1', 
     2195                                data[0]) 
     2196                        gajim.connections[account].ssl_certificate_accepted() 
     2197                def on_no(widget): 
     2198                        dialog.destroy() 
     2199                        gajim.connections[account].disconnect(on_purpose=True) 
     2200                        self.handle_event_status(account, 'offline') 
     2201                pritext = _('SSL certificate error') 
     2202                sectext = _('It seems SSL certificate has changed or your connection is ' 
     2203                        'being hacked. Do you still want to connect and update the fingerprint' 
     2204                        'of the certificate?') 
     2205                dialog = dialogs.YesNoDialog(pritext, sectext, on_response_yes=on_yes, 
     2206                        on_response_no=on_no) 
     2207 
    21692208        def read_sleepy(self): 
    21702209                '''Check idle status and change that status if needed''' 
    21712210                if not self.sleeper.poll(): 
     
    25022541                        'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported, 
    25032542                        'SESSION_NEG': self.handle_session_negotiation, 
    25042543                        'GPG_PASSWORD_REQUIRED': self.handle_event_gpg_password_required, 
     2544                        'SSL_ERROR': self.handle_event_ssl_error, 
     2545                        'FINGERPRINT_ERROR': self.handle_event_fingerprint_error, 
    25052546                } 
    25062547                gajim.handlers = self.handlers 
    25072548 
  • common/xmpp/transports_nb.py

     
    745745                #tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD) 
    746746                tcpsock.ssl_errnum = 0 
    747747                tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER, self._ssl_verify_callback) 
     748                cacerts = os.path.join(gajim.DATA_DIR, 'other', 'cacerts.pem') 
    748749                try: 
    749                         tcpsock._sslContext.load_verify_locations(os.path.join(gajim.DATA_DIR, 'other', 'cacerts.pem')) 
     750                        tcpsock._sslContext.load_verify_locations(cacerts) 
    750751                except: 
    751                         log.warning(_("Unable to load SSL certificats from file %s" % os.path.abspath(os.path.join(gajim.DATA_DIR,'other','ca.crt')))) 
     752                        log.warning('Unable to load SSL certificats from file %s' % \ 
     753                                os.path.abspath(cacerts)) 
     754                # load users certs 
     755                if os.path.isfile(gajim.MY_CACERTS): 
     756                        store = tcpsock._sslContext.get_cert_store() 
     757                        f = open(gajim.MY_CACERTS) 
     758                        lines = f.readlines() 
     759                        i = 0 
     760                        begin = -1 
     761                        for line in lines: 
     762                                if 'BEGIN CERTIFICATE' in line: 
     763                                        begin = i 
     764                                        continue 
     765                                elif 'END CERTIFICATE' in line and begin > -1: 
     766                                        cert = ''.join(lines[begin:i+2]) 
     767                                        try: 
     768                                                X509cert = OpenSSL.crypto.load_certificate( 
     769                                                        OpenSSL.crypto.FILETYPE_PEM, cert) 
     770                                                store.add_cert(X509cert) 
     771                                        except: 
     772                                                log.warning('Unable to load a certificate from file %s' % \ 
     773                                                        gajim.MY_CACERTS) 
     774                                        begin = -1 
     775                                i += 1 
    752776                tcpsock._sslObj = OpenSSL.SSL.Connection(tcpsock._sslContext, tcpsock._sock) 
    753777                tcpsock._sslObj.set_connect_state() # set to client mode 
    754778 
     
    788812        def _ssl_verify_callback(self, sslconn, cert, errnum, depth, ok): 
    789813                # Exceptions can't propagate up through this callback, so print them here. 
    790814                try: 
     815                        self._owner.Connection.ssl_fingerprint_sha1 = cert.digest('sha1') 
    791816                        if errnum == 0: 
    792817                                return True 
    793818                        self._owner.Connection.ssl_errnum = errnum 
     819                        self._owner.Connection.ssl_cert_pem = OpenSSL.crypto.dump_certificate( 
     820                                OpenSSL.crypto.FILETYPE_PEM, cert) 
    794821                        return True 
    795822                except: 
    796823                        log.error("Exception caught in _ssl_info_callback:", exc_info=True) 
  • common/configpaths.py

     
    7979 
    8080                # LOG is deprecated 
    8181                k = ( 'LOG',   'LOG_DB',   'VCARD',   'AVATAR',   'MY_EMOTS', 
    82                         'MY_ICONSETS' ) 
     82                        'MY_ICONSETS', 'MY_CACERTS') 
    8383                v = (u'logs', u'logs.db', u'vcards', u'avatars', u'emoticons', 
    84                         u'iconsets') 
     84                        u'iconsets',  u'cacerts.pem') 
    8585 
    8686                if os.name == 'nt': 
    8787                        v = map(lambda x: x.capitalize(), v) 
  • common/config.py

     
    276276                        'keyid': [ opt_str, '', '', True ], 
    277277                        'keyname': [ opt_str, '', '', True ], 
    278278                        'usessl': [ opt_bool, False, '', True ], 
     279                        'ssl_fingerprint_sha1': [ opt_str, '', '', True ], 
    279280                        'use_srv': [ opt_bool, True, '', True ], 
    280281                        'use_custom_host': [ opt_bool, False, '', True ], 
    281282                        'custom_port': [ opt_int, 5222, '', True ], 
  • common/gajim.py

     
    7777AVATAR_PATH = gajimpaths['AVATAR'] 
    7878MY_EMOTS_PATH = gajimpaths['MY_EMOTS'] 
    7979MY_ICONSETS_PATH = gajimpaths['MY_ICONSETS'] 
     80MY_CACERTS =  gajimpaths['MY_CACERTS'] 
    8081TMP = gajimpaths['TMP'] 
    8182DATA_DIR = gajimpaths['DATA'] 
    8283HOME_DIR = gajimpaths['HOME'] 
  • common/connection.py

     
    5959import gtkgui_helpers 
    6060 
    6161ssl_error = {  
    62 2: "Unable to get issuer certificate", 
    63 3: "Unable to get certificate CRL", 
    64 4: "Unable to decrypt certificate's signature", 
    65 5: "Unable to decrypt CRL's signature", 
    66 6: "Unable to decode issuer public key", 
    67 7: "Certificate signature failure", 
    68 8: "CRL signature failure", 
    69 9: "Certificate is not yet valid", 
    70 10: "Certificate has expired", 
    71 11: "CRL is not yet valid", 
    72 12: "CRL has expired", 
    73 13: "Format error in certificate's notBefore field", 
    74 14: "Format error in certificate's notAfter field", 
    75 15: "Format error in CRL's lastUpdate field", 
    76 16: "Format error in CRL's nextUpdate field", 
    77 17: "Out of memory", 
    78 18: "Self signed certificate in certificate chain", 
    79 19: "Unable to get local issuer certificate", 
    80 20: "Unable to verify the first certificate", 
    81 21: "Unable to verify the first certificate", 
    82 22: "Certificate chain too long", 
    83 23: "Certificate revoked", 
    84 24: "Invalid CA certificate", 
    85 25: "Path length constraint exceeded", 
    86 26: "Unsupported certificate purpose", 
    87 27: "Certificate not trusted", 
    88 28: "Certificate rejected", 
    89 29: "Subject issuer mismatch", 
    90 30: "Authority and subject key identifier mismatch", 
    91 31: "Authority and issuer serial number mismatch", 
    92 32: "Key usage does not include certificate signing", 
    93 50: "Application verification failure" 
     622: _("Unable to get issuer certificate"), 
     633: _("Unable to get certificate CRL"), 
     644: _("Unable to decrypt certificate's signature"), 
     655: _("Unable to decrypt CRL's signature"), 
     666: _("Unable to decode issuer public key"), 
     677: _("Certificate signature failure"), 
     688: _("CRL signature failure"), 
     699: _("Certificate is not yet valid"), 
     7010: _("Certificate has expired"), 
     7111: _("CRL is not yet valid"), 
     7212: _("CRL has expired"), 
     7313: _("Format error in certificate's notBefore field"), 
     7414: _("Format error in certificate's notAfter field"), 
     7515: _("Format error in CRL's lastUpdate field"), 
     7616: _("Format error in CRL's nextUpdate field"), 
     7717: _("Out of memory"), 
     7818: _("Self signed certificate in certificate chain"), 
     7919: _("Unable to get local issuer certificate"), 
     8020: _("Unable to verify the first certificate"), 
     8121: _("Unable to verify the first certificate"), 
     8222: _("Certificate chain too long"), 
     8323: _("Certificate revoked"), 
     8424: _("Invalid CA certificate"), 
     8525: _("Path length constraint exceeded"), 
     8626: _("Unsupported certificate purpose"), 
     8727: _("Certificate not trusted"), 
     8828: _("Certificate rejected"), 
     8929: _("Subject issuer mismatch"), 
     9030: _("Authority and subject key identifier mismatch"), 
     9131: _("Authority and issuer serial number mismatch"), 
     9232: _("Key usage does not include certificate signing"), 
     9350: _("Application verification failure") 
    9494} 
    9595class Connection(ConnectionHandlers): 
    9696        '''Connection class''' 
     
    182182                        self.retrycount = 0 
    183183         
    184184        # We are doing disconnect at so many places, better use one function in all 
    185         def disconnect(self, on_purpose = False): 
     185        def disconnect(self, on_purpose=False): 
    186186                self.on_purpose = on_purpose 
    187187                self.connected = 0 
    188188                self.time_to_reconnect = None 
     
    477477                con.RegisterDisconnectHandler(self._disconnectedReconnCB) 
    478478                log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'], 
    479479                        self._current_host['port'], con_type)) 
    480                 self._register_handlers(con, con_type) 
    481480 
    482481                name = gajim.config.get_per('accounts', self.name, 'name') 
    483482                hostname = gajim.config.get_per('accounts', self.name, 'hostname') 
     
    487486                except AttributeError: 
    488487                        errnum = -1 # we don't have an errnum 
    489488                if errnum > 0: 
    490                         # FIXME: tell the user that the certificat is untrusted, and ask him what to do 
    491                         try: 
    492                                 log.warning("The authenticity of the "+hostname+" certificate could be invalid.\nSSL Error: "+ssl_error[errnum]) 
    493                         except KeyError: 
    494                                 log.warning("Unknown SSL error: %d" % errnum) 
     489                        text = _('The authenticity of the %s certificate could be invalid.') %\ 
     490                                hostname 
     491                        if errnum in ssl_error: 
     492                                text += _('\nSSL Error: %s') % ssl_error[errnum] 
     493                        else: 
     494                                text += _('\nUnknown SSL error: %d') % errnum 
     495                        self.dispatch('SSL_ERROR', (text, con.Connection.ssl_cert_pem, 
     496                                con.Connection.ssl_fingerprint_sha1)) 
     497                        return True 
     498                if hasattr(con.Connection, 'ssl_fingerprint_sha1'): 
     499                        saved_fingerprint = gajim.config.get_per('accounts', self.name, 'ssl_fingerprint_sha1') 
     500                        if saved_fingerprint: 
     501                                # Check sha1 fingerprint 
     502                                if con.Connection.ssl_fingerprint_sha1 != saved_fingerprint: 
     503                                        self.dispatch('FINGERPRINT_ERROR', 
     504                                                (con.Connection.ssl_fingerprint_sha1,)) 
     505                                        return True 
     506                self._register_handlers(con, con_type) 
    495507                con.auth(name, self.password, self.server_resource, 1, self.__on_auth) 
    496508 
    497                 return True 
    498509 
     510        def ssl_certificate_accepted(self): 
     511                name = gajim.config.get_per('accounts', self.name, 'name') 
     512                self._register_handlers(self.connection, 'ssl') 
     513                self.connection.auth(name, self.password, self.server_resource, 1, self.__on_auth) 
     514 
    499515        def _register_handlers(self, con, con_type): 
    500516                self.peerhost = con.get_peerhost() 
    501517                # notify the gui about con_type 
     
    543559 
    544560        def quit(self, kill_core): 
    545561                if kill_core and gajim.account_is_connected(self.name): 
    546                         self.disconnect(on_purpose = True) 
     562                        self.disconnect(on_purpose=True) 
    547563         
    548564        def get_privacy_lists(self): 
    549565                if not self.connection: