summaryrefslogtreecommitdiff
path: root/src/leap/bitmask
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/bitmask')
-rw-r--r--src/leap/bitmask/bonafide/_protocol.py12
-rw-r--r--src/leap/bitmask/bonafide/_srp.py23
-rw-r--r--src/leap/bitmask/bonafide/service.py11
-rw-r--r--src/leap/bitmask/bonafide/session.py14
-rw-r--r--src/leap/bitmask/cli/user.py20
-rw-r--r--src/leap/bitmask/core/dispatcher.py6
-rw-r--r--src/leap/bitmask/core/mail_services.py11
-rw-r--r--src/leap/bitmask/core/service.py1
-rw-r--r--src/leap/bitmask/core/web/bitmask.js11
9 files changed, 105 insertions, 4 deletions
diff --git a/src/leap/bitmask/bonafide/_protocol.py b/src/leap/bitmask/bonafide/_protocol.py
index ff357a14..7917f383 100644
--- a/src/leap/bitmask/bonafide/_protocol.py
+++ b/src/leap/bitmask/bonafide/_protocol.py
@@ -147,6 +147,18 @@ class BonafideProtocol(object):
d.addCallback(lambda _: '%s logged out' % full_id)
return d
+ def do_change_password(self, full_id, current_password, new_password):
+ log.msg('Change password for %s' % full_id)
+ if (full_id not in self._sessions or
+ not self._sessions[full_id].is_authenticated):
+ return fail(RuntimeError("There is no session for such user"))
+ session = self._sessions[full_id]
+
+ if current_password != session.password:
+ return fail(RuntimeError("The current password is not valid"))
+
+ return session.change_password(new_password)
+
def do_get_provider(self, provider_id, autoconf=False):
provider = config.Provider(provider_id, autoconf=autoconf)
return provider.callWhenMainConfigReady(provider.config)
diff --git a/src/leap/bitmask/bonafide/_srp.py b/src/leap/bitmask/bonafide/_srp.py
index b0dd83ff..34a75a56 100644
--- a/src/leap/bitmask/bonafide/_srp.py
+++ b/src/leap/bitmask/bonafide/_srp.py
@@ -101,9 +101,7 @@ class SRPSignupMechanism(object):
"""
def get_signup_params(self, username, password):
- salt, verifier = srp.create_salted_verification_key(
- bytes(username), bytes(password),
- srp.SHA256, srp.NG_1024)
+ salt, verifier = _get_salt_verifier(username, password)
user_data = {
'user[login]': username,
'user[password_salt]': binascii.hexlify(salt),
@@ -121,6 +119,25 @@ class SRPSignupMechanism(object):
return username
+class SRPPasswordChangeMechanism(object):
+
+ """
+ Implement a protocol-agnostic SRP passord change mechanism.
+ """
+
+ def get_password_params(self, username, password):
+ salt, verifier = _get_salt_verifier(username, password)
+ user_data = {
+ 'user[password_salt]': binascii.hexlify(salt),
+ 'user[password_verifier]': binascii.hexlify(verifier)}
+ return user_data
+
+
+def _get_salt_verifier(username, password):
+ return srp.create_salted_verification_key(bytes(username), bytes(password),
+ srp.SHA256, srp.NG_1024)
+
+
def _safe_unhexlify(val):
return binascii.unhexlify(val) \
if (len(val) % 2 == 0) else binascii.unhexlify('0' + val)
diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py
index 5e3b0f1c..797e6065 100644
--- a/src/leap/bitmask/bonafide/service.py
+++ b/src/leap/bitmask/bonafide/service.py
@@ -103,6 +103,17 @@ class BonafideService(HookableService):
d.addCallback(lambda response: {'logout': 'ok'})
return d
+ def do_change_password(self, username, current_password, new_password):
+ def notify_passphrase_change(_):
+ data = dict(username=username, password=new_password)
+ self.trigger_hook('on_passphrase_change', **data)
+
+ d = self._bonafide.do_change_password(username, current_password,
+ new_password)
+ d.addCallback(notify_passphrase_change)
+ d.addCallback(lambda _: {'update': 'ok'})
+ return d
+
def do_provider_create(self, domain):
return self._bonafide.do_get_provider(domain, autoconf=True)
diff --git a/src/leap/bitmask/bonafide/session.py b/src/leap/bitmask/bonafide/session.py
index f25fa05e..abb697ac 100644
--- a/src/leap/bitmask/bonafide/session.py
+++ b/src/leap/bitmask/bonafide/session.py
@@ -66,6 +66,7 @@ class Session(object):
password = self.password or ''
self._srp_auth = _srp.SRPAuthMechanism(username, password)
self._srp_signup = _srp.SRPSignupMechanism()
+ self._srp_password = _srp.SRPPasswordChangeMechanism()
self._token = None
self._uuid = None
@@ -123,6 +124,19 @@ class Session(object):
self._initialize_session()
defer.returnValue(OK)
+ @_auth_required
+ @defer.inlineCallbacks
+ def change_password(self, password):
+ uri = self._api.get_update_user_uri(uid=self._uuid)
+ met = self._api.get_update_user_method()
+ params = self._srp_password.get_password_params(
+ self.username, password)
+ update = yield self._request(self._agent, uri, values=params,
+ method=met)
+ self.password = password
+ self._srp_auth = _srp.SRPAuthMechanism(self.username, password)
+ defer.returnValue(OK)
+
# User certificates
def get_vpn_cert(self):
diff --git a/src/leap/bitmask/cli/user.py b/src/leap/bitmask/cli/user.py
index 9ce4dc6a..1c4757e1 100644
--- a/src/leap/bitmask/cli/user.py
+++ b/src/leap/bitmask/cli/user.py
@@ -35,6 +35,7 @@ SUBCOMMANDS:
create Registers new user, if possible
auth Logs in against the provider
logout Ends any active session with the provider
+ update Update user password
active Shows the active user, if any
'''.format(name=command.appname)
@@ -47,7 +48,7 @@ SUBCOMMANDS:
def create(self, raw_args):
username = self.username(raw_args)
- passwd = getpass.getpass()
+ passwd = self.getpass_twice()
self.data += ['create', username, passwd, 'true']
return self._send(printer=command.default_dict_printer)
@@ -62,6 +63,13 @@ SUBCOMMANDS:
self.data += ['logout', username]
return self._send(printer=command.default_dict_printer)
+ def update(self, raw_args):
+ username = self.username(raw_args)
+ current_passwd = getpass.getpass('Current password: ')
+ new_passwd = self.getpass_twice('New password: ')
+ self.data += ['update', username, current_passwd, new_passwd]
+ return self._send(printer=command.default_dict_printer)
+
def username(self, raw_args):
args = tuple([command.appname] + sys.argv[1:3])
parser = argparse.ArgumentParser(
@@ -78,3 +86,13 @@ SUBCOMMANDS:
self._error("Username ID must be in the form <user@example.org>")
return username
+
+ def getpass_twice(self, prompt='Password: '):
+ while True:
+ passwd1 = getpass.getpass(prompt)
+ passwd2 = getpass.getpass('Retype the password: ')
+ if passwd1 == passwd2:
+ return passwd1
+ else:
+ print "The passwords do not match, try again."
+ print ""
diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py
index 4c885ce7..2860c88d 100644
--- a/src/leap/bitmask/core/dispatcher.py
+++ b/src/leap/bitmask/core/dispatcher.py
@@ -119,6 +119,12 @@ class UserCmd(SubCommand):
user = parts[2]
return bonafide.do_logout(user)
+ @register_method("{'update': 'ok'}")
+ def do_UPDATE(self, bonafide, *parts):
+ user, current_password, new_password = parts[2], parts[3], parts[4]
+ return bonafide.do_change_password(
+ user, current_password, new_password)
+
@register_method('str')
def do_ACTIVE(self, bonafide, *parts):
return bonafide.do_get_active_user()
diff --git a/src/leap/bitmask/core/mail_services.py b/src/leap/bitmask/core/mail_services.py
index 3de4df2e..e19b7192 100644
--- a/src/leap/bitmask/core/mail_services.py
+++ b/src/leap/bitmask/core/mail_services.py
@@ -210,6 +210,17 @@ class SoledadService(HookableService):
container.add_instance(
userid, password, uuid=uuid, token=token)
+ def hook_on_passphrase_change(self, **kw):
+ # TODO: if bitmask stops before this hook being executed bonafide and
+ # soledad will end up with different passwords
+ # https://leap.se/code/issues/8489
+ userid = kw['username']
+ password = kw['password']
+ soledad = self._container.get_instance(userid)
+ if soledad is not None:
+ log.msg("Change soledad passphrase for %s" % userid)
+ soledad.change_passphrase(unicode(password))
+
class KeymanagerContainer(Container):
diff --git a/src/leap/bitmask/core/service.py b/src/leap/bitmask/core/service.py
index 27853c09..e449a8cd 100644
--- a/src/leap/bitmask/core/service.py
+++ b/src/leap/bitmask/core/service.py
@@ -89,6 +89,7 @@ class BitmaskBackend(configurable.ConfigurableService):
# (2) provider offers this service
bf.register_hook('on_passphrase_entry', listener='soledad')
bf.register_hook('on_bonafide_auth', listener='soledad')
+ bf.register_hook('on_passphrase_change', listener='soledad')
bf.register_hook('on_bonafide_auth', listener='keymanager')
bf.register_hook('on_bonafide_auth', listener='mail')
diff --git a/src/leap/bitmask/core/web/bitmask.js b/src/leap/bitmask/core/web/bitmask.js
index fedd5fcd..71a34f4a 100644
--- a/src/leap/bitmask/core/web/bitmask.js
+++ b/src/leap/bitmask/core/web/bitmask.js
@@ -177,6 +177,17 @@ var bitmask = function(){
}
return call(['bonafide', 'user', 'logout', uid]);
}
+
+ /**
+ * Change password
+ *
+ * @param {string} uid The uid to log in
+ * @param {string} current_password The current user password
+ * @param {string} new_password The new user password
+ */
+ update: function(uid, current_password, new_password) {
+ return call(['bonafide', 'user', 'update', uid, current_password, new_password]);
+ },
}
},