From 94644a2ee3e3d2ec3926a90859a57287c49a1c96 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger <asterix@lagaule.org> Date: Tue, 26 Jul 2011 22:44:11 +0200 Subject: [PATCH] ability to connect to a host that require a PCKS certificate that is encrypted. --- data/gui/accounts_window.ui | 72 ++++++++++++++++-------- src/common/config.py | 1 + src/common/connection.py | 40 ++++++++----- src/common/connection_handlers_events.py | 4 ++ src/common/xmpp/tls_nb.py | 3 +- src/config.py | 10 ++++ src/gui_interface.py | 15 +++++ 7 files changed, 106 insertions(+), 39 deletions(-) diff --git a/data/gui/accounts_window.ui b/data/gui/accounts_window.ui index 4a1b0be37c..e1e2a3dcc4 100644 --- a/data/gui/accounts_window.ui +++ b/data/gui/accounts_window.ui @@ -451,48 +451,70 @@ <property name="visible">True</property> <property name="can_focus">True</property> <child> - <object class="GtkHBox" id="hbox2"> + <object class="GtkVBox" id="vbox13"> <property name="visible">True</property> + <property name="orientation">vertical</property> <property name="spacing">6</property> <child> - <object class="GtkLabel" id="label28"> + <object class="GtkHBox" id="hbox2"> <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">_Client Cert File:</property> - <property name="use_underline">True</property> - <property name="mnemonic_widget">cert_entry1</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="label28"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">_Client Cert File:</property> + <property name="use_underline">True</property> + <property name="mnemonic_widget">cert_entry1</property> + </object> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="cert_entry1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">●</property> + </object> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="browse_for_client_cert_button"> + <property name="label" translatable="yes">Browse...</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <signal name="clicked" handler="on_browse_for_client_cert_button_clicked"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> </object> <packing> - <property name="expand">False</property> <property name="position">0</property> </packing> </child> <child> - <object class="GtkEntry" id="cert_entry1"> + <object class="GtkCheckButton" id="client_cert_encrypted_checkbutton1"> + <property name="label" translatable="yes">Certificate is e_ncrypted</property> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="tooltip_text" translatable="yes">The path to the client certificate and key in PKCS#12 format</property> - <property name="invisible_char">●</property> + <property name="receives_default">False</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_client_cert_encrypted_checkbutton1_toggled"/> </object> <packing> <property name="position">1</property> </packing> </child> - <child> - <object class="GtkButton" id="browse_for_client_cert_button"> - <property name="label" translatable="yes">Browse...</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="tooltip_text" translatable="yes">Choose Client Cert</property> - <signal name="clicked" handler="on_browse_for_client_cert_button_clicked"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> </object> </child> <child type="label"> diff --git a/src/common/config.py b/src/common/config.py index 3a83be6e13..84562468f1 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -298,6 +298,7 @@ class Config: 'hostname': [ opt_str, '', '', True ], 'anonymous_auth': [ opt_bool, False ], 'client_cert': [ opt_str, '', '', True ], + 'client_cert_encrypted': [ opt_bool, False, '', False ], 'savepass': [ opt_bool, False ], 'password': [ opt_str, '' ], 'resource': [ opt_str, 'gajim', '', True ], diff --git a/src/common/connection.py b/src/common/connection.py index e92dc57a0c..e5ce35fd42 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -749,6 +749,7 @@ class Connection(CommonConnection, ConnectionHandlers): self.pingalives = 0 self.client_cert = gajim.config.get_per('accounts', self.name, 'client_cert') + self.client_cert_passphrase = '' def check_jid(self, jid): return helpers.parse_jid(jid) @@ -1155,29 +1156,42 @@ class Connection(CommonConnection, ConnectionHandlers): secure_tuple = (self._current_type, cacerts, mycerts) con = common.xmpp.NonBlockingClient( - domain=self._hostname, - caller=self, - idlequeue=gajim.idlequeue) + domain=self._hostname, + caller=self, + idlequeue=gajim.idlequeue) self.last_connection = con # increase default timeout for server responses - common.xmpp.dispatcher_nb.DEFAULT_TIMEOUT_SECONDS = self.try_connecting_for_foo_secs + common.xmpp.dispatcher_nb.DEFAULT_TIMEOUT_SECONDS = \ + self.try_connecting_for_foo_secs # FIXME: this is a hack; need a better way if self.on_connect_success == self._on_new_account: con.RegisterDisconnectHandler(self._on_new_account) - self.log_hosttype_info(port) - con.connect( - hostname=self._current_host['host'], - port=port, - on_connect=self.on_connect_success, - on_proxy_failure=self.on_proxy_failure, - on_connect_failure=self.connect_to_next_type, - proxy=self._proxy, - secure_tuple = secure_tuple) + if self.client_cert and gajim.config.get_per('accounts', self.name, + 'client_cert_encrypted'): + gajim.nec.push_incoming_event(ClientCertPassphraseEvent( + None, conn=self, con=con, port=port, + secure_tuple=secure_tuple)) + return + self.on_client_cert_passphrase(None, con, port, secure_tuple) + else: self._connect_to_next_host(retry) + def on_client_cert_passphrase(self, passphrase, con, port, secure_tuple): + self.client_cert_passphrase = passphrase + + self.log_hosttype_info(port) + con.connect( + hostname=self._current_host['host'], + port=port, + on_connect=self.on_connect_success, + on_proxy_failure=self.on_proxy_failure, + on_connect_failure=self.connect_to_next_type, + proxy=self._proxy, + secure_tuple = secure_tuple) + def log_hosttype_info(self, port): msg = '>>>>>> Connecting to %s [%s:%d], type = %s' % (self.name, self._current_host['host'], port, self._current_type) diff --git a/src/common/connection_handlers_events.py b/src/common/connection_handlers_events.py index 63c7000ac3..08b4ce2e71 100644 --- a/src/common/connection_handlers_events.py +++ b/src/common/connection_handlers_events.py @@ -2069,3 +2069,7 @@ class MessageOutgoingEvent(nec.NetworkIncomingEvent): def generate(self): return True + +class ClientCertPassphraseEvent(nec.NetworkIncomingEvent): + name = 'client-cert-passphrase' + base_network_events = [] diff --git a/src/common/xmpp/tls_nb.py b/src/common/xmpp/tls_nb.py index 7a9c80f988..cc8393d529 100644 --- a/src/common/xmpp/tls_nb.py +++ b/src/common/xmpp/tls_nb.py @@ -359,7 +359,8 @@ class NonBlockingTLS(PlugIn): tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD) log.debug('Using client cert and key from %s' % conn.client_cert) try: - p12 = OpenSSL.crypto.load_pkcs12(open(conn.client_cert).read()) + p12 = OpenSSL.crypto.load_pkcs12(open(conn.client_cert).read(), + conn.client_cert_passphrase) except OpenSSL.crypto.Error, exception_obj: log.warning('Unable to load client pkcs12 certificate from ' 'file %s: %s ... Is it a valid PKCS12 cert?' % \ diff --git a/src/config.py b/src/config.py index d9610c713e..cc3de416a5 100644 --- a/src/config.py +++ b/src/config.py @@ -1851,6 +1851,10 @@ class AccountsWindow: client_cert = gajim.config.get_per('accounts', account, 'client_cert') self.xml.get_object('cert_entry1').set_text(client_cert) + client_cert_encrypted = gajim.config.get_per('accounts', account, + 'client_cert_encrypted') + self.xml.get_object('client_cert_encrypted_checkbutton1').\ + set_active(client_cert_encrypted) self.xml.get_object('adjust_priority_with_status_checkbutton1').\ set_active(gajim.config.get_per('accounts', account, @@ -2222,6 +2226,12 @@ class AccountsWindow: # if we showed ErrorDialog, there will not be dialog instance return + def on_client_cert_encrypted_checkbutton1_toggled(self, widget): + if self.ignore_events: + return + self.on_checkbutton_toggled(widget, 'client_cert_encrypted', + account=self.current_account) + def on_autoconnect_checkbutton_toggled(self, widget): if self.ignore_events: return diff --git a/src/gui_interface.py b/src/gui_interface.py index e2e4ed2557..86dd76fcdf 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -671,6 +671,19 @@ class Interface: _('You are currently connected without your OpenPGP key.')) self.forget_gpg_passphrase(obj.keyID) + def handle_event_client_cert_passphrase(self, obj): + def on_ok(passphrase, checked): + obj.conn.on_client_cert_passphrase(passphrase, obj.con, obj.port, + obj.secure_tuple) + + def on_cancel(): + obj.conn.on_client_cert_passphrase('', obj.con, obj.port, + obj.secure_tuple) + + dialogs.PassphraseDialog(_('Certificate Passphrase Required'), + _('Enter the passphrase for the certificate for account %s') % \ + obj.conn.name, ok_handler=on_ok, cancel_handler=on_cancel) + def handle_event_gpg_password_required(self, obj): #('GPG_PASSWORD_REQUIRED', account, (callback,)) if obj.keyid in self.gpg_passphrase: @@ -1401,6 +1414,8 @@ class Interface: 'atom-entry-received': [self.handle_atom_entry], 'bad-gpg-passphrase': [self.handle_event_bad_gpg_passphrase], 'bookmarks-received': [self.handle_event_bookmarks], + 'client-cert-passphrase': [ + self.handle_event_client_cert_passphrase], 'connection-lost': [self.handle_event_connection_lost], 'failed-decrypt': [(self.handle_event_failed_decrypt, ged.GUI2)], 'file-request-error': [self.handle_event_file_request_error], -- GitLab