Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/jarabe/desktop/keydialog.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/jarabe/desktop/keydialog.py')
-rw-r--r--src/jarabe/desktop/keydialog.py317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/jarabe/desktop/keydialog.py b/src/jarabe/desktop/keydialog.py
new file mode 100644
index 0000000..41c2a51
--- /dev/null
+++ b/src/jarabe/desktop/keydialog.py
@@ -0,0 +1,317 @@
+# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2009 One Laptop per Child
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import hashlib
+from gettext import gettext as _
+
+import gtk
+import dbus
+
+from jarabe.model import network
+
+
+IW_AUTH_ALG_OPEN_SYSTEM = 'open'
+IW_AUTH_ALG_SHARED_KEY = 'shared'
+
+WEP_PASSPHRASE = 1
+WEP_HEX = 2
+WEP_ASCII = 3
+
+
+def string_is_hex(key):
+ is_hex = True
+ for c in key:
+ if not 'a' <= c.lower() <= 'f' and not '0' <= c <= '9':
+ is_hex = False
+ return is_hex
+
+
+def string_is_ascii(string):
+ try:
+ string.encode('ascii')
+ return True
+ except UnicodeEncodeError:
+ return False
+
+
+def string_to_hex(passphrase):
+ key = ''
+ for c in passphrase:
+ key += '%02x' % ord(c)
+ return key
+
+
+def hash_passphrase(passphrase):
+ # passphrase must have a length of 64
+ if len(passphrase) > 64:
+ passphrase = passphrase[:64]
+ elif len(passphrase) < 64:
+ while len(passphrase) < 64:
+ passphrase += passphrase[:64 - len(passphrase)]
+ passphrase = hashlib.md5(passphrase).digest()
+ return string_to_hex(passphrase)[:26]
+
+
+class CanceledKeyRequestError(dbus.DBusException):
+ def __init__(self):
+ dbus.DBusException.__init__(self)
+ self._dbus_error_name = network.NM_SETTINGS_IFACE + '.CanceledError'
+
+
+class KeyDialog(gtk.Dialog):
+ def __init__(self, ssid, flags, wpa_flags, rsn_flags, dev_caps, response):
+ gtk.Dialog.__init__(self, flags=gtk.DIALOG_MODAL)
+ self.set_title('Wireless Key Required')
+
+ self._response = response
+ self._entry = None
+ self._ssid = ssid
+ self._flags = flags
+ self._wpa_flags = wpa_flags
+ self._rsn_flags = rsn_flags
+ self._dev_caps = dev_caps
+
+ self.set_has_separator(False)
+
+ display_name = network.ssid_to_display_name(ssid)
+ label = gtk.Label(_("A wireless encryption key is required for\n"
+ " the wireless network '%s'.") % (display_name, ))
+ self.vbox.pack_start(label)
+
+ self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OK, gtk.RESPONSE_OK)
+ self.set_default_response(gtk.RESPONSE_OK)
+ self.set_has_separator(True)
+
+ def add_key_entry(self):
+ self._entry = gtk.Entry()
+ self._entry.connect('changed', self._update_response_sensitivity)
+ self._entry.connect('activate', self._entry_activate_cb)
+ self.vbox.pack_start(self._entry)
+ self.vbox.set_spacing(6)
+ self.vbox.show_all()
+
+ self._update_response_sensitivity()
+ self._entry.grab_focus()
+
+ def _entry_activate_cb(self, entry):
+ self.response(gtk.RESPONSE_OK)
+
+ def create_security(self):
+ raise NotImplementedError
+
+ def get_response_object(self):
+ return self._response
+
+
+class WEPKeyDialog(KeyDialog):
+ def __init__(self, ssid, flags, wpa_flags, rsn_flags, dev_caps, response):
+ KeyDialog.__init__(self, ssid, flags, wpa_flags, rsn_flags,
+ dev_caps, response)
+
+ # WEP key type
+ self.key_store = gtk.ListStore(str, int)
+ self.key_store.append(['Passphrase (128-bit)', WEP_PASSPHRASE])
+ self.key_store.append(['Hex (40/128-bit)', WEP_HEX])
+ self.key_store.append(['ASCII (40/128-bit)', WEP_ASCII])
+
+ self.key_combo = gtk.ComboBox(self.key_store)
+ cell = gtk.CellRendererText()
+ self.key_combo.pack_start(cell, True)
+ self.key_combo.add_attribute(cell, 'text', 0)
+ self.key_combo.set_active(0)
+ self.key_combo.connect('changed', self._key_combo_changed_cb)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(gtk.Label(_('Key Type:')))
+ hbox.pack_start(self.key_combo)
+ hbox.show_all()
+ self.vbox.pack_start(hbox)
+
+ # Key entry field
+ self.add_key_entry()
+
+ # WEP authentication mode
+ self.auth_store = gtk.ListStore(str, str)
+ self.auth_store.append(['Open System', IW_AUTH_ALG_OPEN_SYSTEM])
+ self.auth_store.append(['Shared Key', IW_AUTH_ALG_SHARED_KEY])
+
+ self.auth_combo = gtk.ComboBox(self.auth_store)
+ cell = gtk.CellRendererText()
+ self.auth_combo.pack_start(cell, True)
+ self.auth_combo.add_attribute(cell, 'text', 0)
+ self.auth_combo.set_active(0)
+
+ hbox = gtk.HBox()
+ hbox.pack_start(gtk.Label(_('Authentication Type:')))
+ hbox.pack_start(self.auth_combo)
+ hbox.show_all()
+
+ self.vbox.pack_start(hbox)
+
+ def _key_combo_changed_cb(self, widget):
+ self._update_response_sensitivity()
+
+ def _get_security(self):
+ key = self._entry.get_text()
+
+ it = self.key_combo.get_active_iter()
+ (key_type, ) = self.key_store.get(it, 1)
+
+ if key_type == WEP_PASSPHRASE:
+ key = hash_passphrase(key)
+ elif key_type == WEP_ASCII:
+ key = string_to_hex(key)
+
+ it = self.auth_combo.get_active_iter()
+ (auth_alg, ) = self.auth_store.get(it, 1)
+
+ return (key, auth_alg)
+
+ def print_security(self):
+ (key, auth_alg) = self._get_security()
+ print 'Key: %s' % key
+ print 'Auth: %d' % auth_alg
+
+ def create_security(self):
+ (key, auth_alg) = self._get_security()
+ wsec = {'wep-key0': key, 'auth-alg': auth_alg}
+ return {'802-11-wireless-security': wsec}
+
+ def _update_response_sensitivity(self, ignored=None):
+ key = self._entry.get_text()
+ it = self.key_combo.get_active_iter()
+ (key_type, ) = self.key_store.get(it, 1)
+
+ valid = False
+ if key_type == WEP_PASSPHRASE:
+ # As the md5 passphrase can be of any length and has no indicator,
+ # we cannot check for the validity of the input.
+ if len(key) > 0:
+ valid = True
+ elif key_type == WEP_ASCII:
+ if len(key) == 5 or len(key) == 13:
+ valid = string_is_ascii(key)
+ elif key_type == WEP_HEX:
+ if len(key) == 10 or len(key) == 26:
+ valid = string_is_hex(key)
+
+ self.set_response_sensitive(gtk.RESPONSE_OK, valid)
+
+
+class WPAKeyDialog(KeyDialog):
+ def __init__(self, ssid, flags, wpa_flags, rsn_flags, dev_caps, response):
+ KeyDialog.__init__(self, ssid, flags, wpa_flags, rsn_flags,
+ dev_caps, response)
+ self.add_key_entry()
+
+ self.store = gtk.ListStore(str)
+ self.store.append([_('WPA & WPA2 Personal')])
+
+ self.combo = gtk.ComboBox(self.store)
+ cell = gtk.CellRendererText()
+ self.combo.pack_start(cell, True)
+ self.combo.add_attribute(cell, 'text', 0)
+ self.combo.set_active(0)
+
+ self.hbox = gtk.HBox()
+ self.hbox.pack_start(gtk.Label(_('Wireless Security:')))
+ self.hbox.pack_start(self.combo)
+ self.hbox.show_all()
+
+ self.vbox.pack_start(self.hbox)
+
+ def _get_security(self):
+ ssid = self._ssid
+ key = self._entry.get_text()
+ is_hex = string_is_hex(key)
+
+ real_key = None
+ if len(key) == 64 and is_hex:
+ # Hex key
+ real_key = key
+ elif len(key) >= 8 and len(key) <= 63:
+ # passphrase
+ from subprocess import Popen, PIPE
+ p = Popen(['wpa_passphrase', ssid, key], stdout=PIPE)
+ for line in p.stdout:
+ if line.strip().startswith('psk='):
+ real_key = line.strip()[4:]
+ if p.wait() != 0:
+ raise RuntimeError('Error hashing passphrase')
+ if real_key and len(real_key) != 64:
+ real_key = None
+
+ if not real_key:
+ raise RuntimeError('Invalid key')
+
+ return real_key
+
+ def print_security(self):
+ key = self._get_security()
+ print 'Key: %s' % key
+
+ def create_security(self):
+ wsec = {'psk': self._get_security()}
+ return {'802-11-wireless-security': wsec}
+
+ def _update_response_sensitivity(self, ignored=None):
+ key = self._entry.get_text()
+ is_hex = string_is_hex(key)
+
+ valid = False
+ if len(key) == 64 and is_hex:
+ # hex key
+ valid = True
+ elif len(key) >= 8 and len(key) <= 63:
+ # passphrase
+ valid = True
+ self.set_response_sensitive(gtk.RESPONSE_OK, valid)
+ return False
+
+
+def create(ssid, flags, wpa_flags, rsn_flags, dev_caps, response):
+ if wpa_flags == network.NM_802_11_AP_SEC_NONE and \
+ rsn_flags == network.NM_802_11_AP_SEC_NONE:
+ key_dialog = WEPKeyDialog(ssid, flags, wpa_flags, rsn_flags,
+ dev_caps, response)
+ else:
+ key_dialog = WPAKeyDialog(ssid, flags, wpa_flags, rsn_flags,
+ dev_caps, response)
+
+ key_dialog.connect('response', _key_dialog_response_cb)
+ key_dialog.show_all()
+
+
+def _key_dialog_response_cb(key_dialog, response_id):
+ response = key_dialog.get_response_object()
+ secrets = None
+ if response_id == gtk.RESPONSE_OK:
+ secrets = key_dialog.create_security()
+
+ if response_id in [gtk.RESPONSE_CANCEL, gtk.RESPONSE_NONE,
+ gtk.RESPONSE_DELETE_EVENT]:
+ # key dialog dialog was canceled; send the error back to NM
+ response.set_error(CanceledKeyRequestError())
+ elif response_id == gtk.RESPONSE_OK:
+ if not secrets:
+ raise RuntimeError('Invalid security arguments.')
+ response.set_secrets(secrets)
+ else:
+ raise RuntimeError('Unhandled key dialog response %d' % response_id)
+
+ key_dialog.destroy()