[Calamar and me]sasl-external c2s authentication. Fixes #5704
This commit is contained in:
parent
dd4af8cd6c
commit
d44c30373f
6 changed files with 182 additions and 10 deletions
|
@ -4,7 +4,7 @@
|
||||||
<!-- interface-naming-policy toplevel-contextual -->
|
<!-- interface-naming-policy toplevel-contextual -->
|
||||||
<object class="GtkListStore" id="liststore1">
|
<object class="GtkListStore" id="liststore1">
|
||||||
<columns>
|
<columns>
|
||||||
<!-- column-name item text -->
|
<!-- column-name item -->
|
||||||
<column type="gchararray"/>
|
<column type="gchararray"/>
|
||||||
</columns>
|
</columns>
|
||||||
<data>
|
<data>
|
||||||
|
@ -203,7 +203,7 @@
|
||||||
<object class="GtkTable" id="table1">
|
<object class="GtkTable" id="table1">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="border_width">6</property>
|
<property name="border_width">6</property>
|
||||||
<property name="n_rows">5</property>
|
<property name="n_rows">6</property>
|
||||||
<property name="n_columns">3</property>
|
<property name="n_columns">3</property>
|
||||||
<property name="column_spacing">6</property>
|
<property name="column_spacing">6</property>
|
||||||
<property name="row_spacing">6</property>
|
<property name="row_spacing">6</property>
|
||||||
|
@ -445,6 +445,68 @@
|
||||||
<property name="y_options"></property>
|
<property name="y_options"></property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkExpander" id="expander2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkHBox" id="hbox2">
|
||||||
|
<property name="visible">True</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="tooltip_text" translatable="yes">The path to the client certificate and key in PKCS#12 format</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>
|
||||||
|
<property name="tooltip_text" translatable="yes">Choose Client Cert</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">False</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="label">
|
||||||
|
<object class="GtkLabel" id="label2">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Client certificate</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="right_attach">3</property>
|
||||||
|
<property name="top_attach">5</property>
|
||||||
|
<property name="bottom_attach">6</property>
|
||||||
|
<property name="y_options">GTK_FILL</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child type="tab">
|
<child type="tab">
|
||||||
|
|
|
@ -289,6 +289,7 @@ class Config:
|
||||||
'name': [ opt_str, '', '', True ],
|
'name': [ opt_str, '', '', True ],
|
||||||
'hostname': [ opt_str, '', '', True ],
|
'hostname': [ opt_str, '', '', True ],
|
||||||
'anonymous_auth': [ opt_bool, False ],
|
'anonymous_auth': [ opt_bool, False ],
|
||||||
|
'client_cert': [ opt_str, '', '', True ],
|
||||||
'savepass': [ opt_bool, False ],
|
'savepass': [ opt_bool, False ],
|
||||||
'password': [ opt_str, '' ],
|
'password': [ opt_str, '' ],
|
||||||
'resource': [ opt_str, 'gajim', '', True ],
|
'resource': [ opt_str, 'gajim', '', True ],
|
||||||
|
|
|
@ -701,6 +701,8 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
'ping_alive_every_foo_secs')
|
'ping_alive_every_foo_secs')
|
||||||
else:
|
else:
|
||||||
self.pingalives = 0
|
self.pingalives = 0
|
||||||
|
self.client_cert = gajim.config.get_per('accounts', self.name,
|
||||||
|
'client_cert')
|
||||||
|
|
||||||
def check_jid(self, jid):
|
def check_jid(self, jid):
|
||||||
return helpers.parse_jid(jid)
|
return helpers.parse_jid(jid)
|
||||||
|
|
|
@ -349,10 +349,42 @@ class NonBlockingTLS(PlugIn):
|
||||||
def _startSSL_pyOpenSSL(self):
|
def _startSSL_pyOpenSSL(self):
|
||||||
log.debug("_startSSL_pyOpenSSL called")
|
log.debug("_startSSL_pyOpenSSL called")
|
||||||
tcpsock = self._owner
|
tcpsock = self._owner
|
||||||
# See http://docs.python.org/dev/library/ssl.html
|
conn = tcpsock._owner._caller
|
||||||
tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
|
if conn.client_cert and os.path.exists(conn.client_cert):
|
||||||
tcpsock._sslContext.set_options(OpenSSL.SSL.OP_NO_SSLv2 | \
|
# FIXME make a checkbox for Client Cert / SSLv23 / TLSv1
|
||||||
OpenSSL.SSL.OP_NO_TICKET)
|
# If we are going to use a client cert/key pair for authentication,
|
||||||
|
# we choose TLSv1 method.
|
||||||
|
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(client_cert_path).read())
|
||||||
|
except OpenSSL.crypto.Error, exception_obj:
|
||||||
|
log.warning('Unable to load client pkcs12 certificate from '
|
||||||
|
'file %s: %s ... Is it a valid PKCS12 cert?' % \
|
||||||
|
(conn.client_cert, exception_obj.args))
|
||||||
|
except:
|
||||||
|
log.warning('Unknown error while loading certificate from file '
|
||||||
|
'%s' % conn.client_cert)
|
||||||
|
else:
|
||||||
|
log.info('PKCS12 Client cert loaded OK')
|
||||||
|
try:
|
||||||
|
tcpsock._sslContext.use_certificate(p12.get_certificate())
|
||||||
|
tcpsock._sslContext.use_privatekey(p12.get_privatekey())
|
||||||
|
log.info('p12 cert and key loaded')
|
||||||
|
except OpenSSL.crypto.Error, exception_obj:
|
||||||
|
log.warning('Unable to extract client certificate from '
|
||||||
|
'file %s' % conn.client_cert)
|
||||||
|
except Exception, msg:
|
||||||
|
log.warning('Unknown error extracting client certificate '
|
||||||
|
'from file %s: %s' % (conn.client_cert, msg))
|
||||||
|
else:
|
||||||
|
log.info('client cert and key loaded OK')
|
||||||
|
else:
|
||||||
|
# See http://docs.python.org/dev/library/ssl.html
|
||||||
|
tcpsock._sslContext = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
|
||||||
|
tcpsock._sslContext.set_options(OpenSSL.SSL.OP_NO_SSLv2 | \
|
||||||
|
OpenSSL.SSL.OP_NO_TICKET)
|
||||||
|
|
||||||
tcpsock.ssl_errnum = 0
|
tcpsock.ssl_errnum = 0
|
||||||
tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER,
|
tcpsock._sslContext.set_verify(OpenSSL.SSL.VERIFY_PEER,
|
||||||
self._ssl_verify_callback)
|
self._ssl_verify_callback)
|
||||||
|
|
|
@ -1612,13 +1612,15 @@ class AccountsWindow:
|
||||||
focused_widget = self.window.get_focus()
|
focused_widget = self.window.get_focus()
|
||||||
focused_widget_name = focused_widget.get_name()
|
focused_widget_name = focused_widget.get_name()
|
||||||
if focused_widget_name in ('jid_entry1', 'resource_entry1',
|
if focused_widget_name in ('jid_entry1', 'resource_entry1',
|
||||||
'custom_port_entry'):
|
'custom_port_entry', 'cert_entry1'):
|
||||||
if focused_widget_name == 'jid_entry1':
|
if focused_widget_name == 'jid_entry1':
|
||||||
func = self.on_jid_entry1_focus_out_event
|
func = self.on_jid_entry1_focus_out_event
|
||||||
elif focused_widget_name == 'resource_entry1':
|
elif focused_widget_name == 'resource_entry1':
|
||||||
func = self.on_resource_entry1_focus_out_event
|
func = self.on_resource_entry1_focus_out_event
|
||||||
elif focused_widget_name == 'custom_port_entry':
|
elif focused_widget_name == 'custom_port_entry':
|
||||||
func = self.on_custom_port_entry_focus_out_event
|
func = self.on_custom_port_entry_focus_out_event
|
||||||
|
elif focused_widget_name == 'cert_entry1':
|
||||||
|
func = self.on_cert_entry1_focus_out_event
|
||||||
if func(focused_widget, None):
|
if func(focused_widget, None):
|
||||||
# Error detected in entry, don't change account, re-put cursor on
|
# Error detected in entry, don't change account, re-put cursor on
|
||||||
# previous row
|
# previous row
|
||||||
|
@ -1641,6 +1643,22 @@ class AccountsWindow:
|
||||||
self.init_account()
|
self.init_account()
|
||||||
self.update_proxy_list()
|
self.update_proxy_list()
|
||||||
|
|
||||||
|
def on_browse_for_client_cert_button_clicked(self, widget, data=None):
|
||||||
|
def on_ok(widget, path_to_clientcert_file):
|
||||||
|
self.dialog.destroy()
|
||||||
|
if not path_to_clientcert_file:
|
||||||
|
return
|
||||||
|
self.xml.get_widget('cert_entry1').set_text(path_to_clientcert_file)
|
||||||
|
gajim.config.set_per('accounts', self.current_account,
|
||||||
|
'client_cert', path_to_clientcert_file)
|
||||||
|
|
||||||
|
def on_cancel(widget):
|
||||||
|
self.dialog.destroy()
|
||||||
|
|
||||||
|
path_to_clientcert_file = self.xml.get_widget('cert_entry1').get_text()
|
||||||
|
self.dialog = dialogs.ClientCertChooserDialog(path_to_clientcert_file,
|
||||||
|
on_ok, on_cancel)
|
||||||
|
|
||||||
def update_proxy_list(self):
|
def update_proxy_list(self):
|
||||||
if self.current_account:
|
if self.current_account:
|
||||||
our_proxy = gajim.config.get_per('accounts', self.current_account,
|
our_proxy = gajim.config.get_per('accounts', self.current_account,
|
||||||
|
@ -1796,10 +1814,14 @@ class AccountsWindow:
|
||||||
# Account tab
|
# Account tab
|
||||||
self.draw_normal_jid()
|
self.draw_normal_jid()
|
||||||
self.xml.get_object('resource_entry1').set_text(gajim.config.get_per(
|
self.xml.get_object('resource_entry1').set_text(gajim.config.get_per(
|
||||||
'accounts', account, 'resource'))
|
'accounts', account, 'resource'))
|
||||||
|
|
||||||
|
client_cert = gajim.config.get_per('accounts', account, 'client_cert')
|
||||||
|
self.xml.get_widget('cert_entry1').set_text(client_cert)
|
||||||
|
|
||||||
self.xml.get_object('adjust_priority_with_status_checkbutton1').\
|
self.xml.get_object('adjust_priority_with_status_checkbutton1').\
|
||||||
set_active(gajim.config.get_per('accounts', account,
|
set_active(gajim.config.get_per('accounts', account,
|
||||||
'adjust_priority_with_status'))
|
'adjust_priority_with_status'))
|
||||||
spinbutton = self.xml.get_object('priority_spinbutton1')
|
spinbutton = self.xml.get_object('priority_spinbutton1')
|
||||||
if gajim.config.get('enable_negative_priority'):
|
if gajim.config.get('enable_negative_priority'):
|
||||||
spinbutton.set_range(-128, 127)
|
spinbutton.set_range(-128, 127)
|
||||||
|
@ -2074,6 +2096,15 @@ class AccountsWindow:
|
||||||
gajim.config.set_per('accounts', self.current_account, 'hostname',
|
gajim.config.set_per('accounts', self.current_account, 'hostname',
|
||||||
jid_splited[1])
|
jid_splited[1])
|
||||||
|
|
||||||
|
def on_cert_entry1_focus_out_event(self, widget, event):
|
||||||
|
if self.ignore_events:
|
||||||
|
return
|
||||||
|
client_cert = widget.get_text()
|
||||||
|
if self.option_changed('client_cert', client_cert):
|
||||||
|
self.need_relogin = True
|
||||||
|
gajim.config.set_per('accounts', self.current_account, 'client_cert',
|
||||||
|
client_cert)
|
||||||
|
|
||||||
def on_anonymous_checkbutton1_toggled(self, widget):
|
def on_anonymous_checkbutton1_toggled(self, widget):
|
||||||
if self.ignore_events:
|
if self.ignore_events:
|
||||||
return
|
return
|
||||||
|
|
|
@ -3900,6 +3900,50 @@ class ProgressDialog:
|
||||||
return True # WM's X button or Escape key should not destroy the window
|
return True # WM's X button or Escape key should not destroy the window
|
||||||
|
|
||||||
|
|
||||||
|
class ClientCertChooserDialog(FileChooserDialog):
|
||||||
|
def __init__(self, path_to_clientcert_file='', on_response_ok=None,
|
||||||
|
on_response_cancel=None):
|
||||||
|
'''
|
||||||
|
optionally accepts path_to_clientcert_file so it has that as selected
|
||||||
|
'''
|
||||||
|
def on_ok(widget, callback):
|
||||||
|
'''
|
||||||
|
check if file exists and call callback
|
||||||
|
'''
|
||||||
|
path_to_clientcert_file = self.get_filename()
|
||||||
|
path_to_clientcert_file = \
|
||||||
|
gtkgui_helpers.decode_filechooser_file_paths(
|
||||||
|
(path_to_clientcert_file,))[0]
|
||||||
|
if os.path.exists(path_to_clientcert_file):
|
||||||
|
callback(widget, path_to_clientcert_file)
|
||||||
|
|
||||||
|
FileChooserDialog.__init__(self,
|
||||||
|
title_text=_('Choose Client Cert #PCKS12'),
|
||||||
|
action=gtk.FILE_CHOOSER_ACTION_OPEN,
|
||||||
|
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||||
|
gtk.STOCK_OPEN, gtk.RESPONSE_OK),
|
||||||
|
current_folder='',
|
||||||
|
default_response=gtk.RESPONSE_OK,
|
||||||
|
on_response_ok=(on_ok, on_response_ok),
|
||||||
|
on_response_cancel=on_response_cancel)
|
||||||
|
|
||||||
|
filter_ = gtk.FileFilter()
|
||||||
|
filter_.set_name(_('All files'))
|
||||||
|
filter_.add_pattern('*')
|
||||||
|
self.add_filter(filter_)
|
||||||
|
|
||||||
|
filter_ = gtk.FileFilter()
|
||||||
|
filter_.set_name(_('PKCS12 Files'))
|
||||||
|
filter_.add_pattern('*.p12')
|
||||||
|
self.add_filter(filter_)
|
||||||
|
self.set_filter(filter_)
|
||||||
|
|
||||||
|
if path_to_clientcert_file:
|
||||||
|
# set_filename accept only absolute path
|
||||||
|
path_to_clientcert_file = os.path.abspath(path_to_clientcert_file)
|
||||||
|
self.set_filename(path_to_clientcert_file)
|
||||||
|
|
||||||
|
|
||||||
class SoundChooserDialog(FileChooserDialog):
|
class SoundChooserDialog(FileChooserDialog):
|
||||||
def __init__(self, path_to_snd_file='', on_response_ok=None,
|
def __init__(self, path_to_snd_file='', on_response_ok=None,
|
||||||
on_response_cancel=None):
|
on_response_cancel=None):
|
||||||
|
|
Loading…
Add table
Reference in a new issue