def _do_login(self, user, password, load_index=False, redirect=False): session, config = self.session, self.session.config session_id = self.session.ui.html_variables.get('http_session') # This prevents folks from sending us a DEFAULT user (upper case), # which is an internal security bypass below. user = user and user.lower() if not user: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(password) password = '' try: # Verify the passphrase gpg = GnuPG(use_agent=False) if gpg.is_available(): gpg.passphrase = sps.get_reader() assert(gpg.sign('Sign This!')[0] == 0) # Store the varified passphrase config.gnupg_passphrase.data = sps.data # Load the config and index, if necessary if not config.loaded_config: self._config() if load_index: self._idx() else: pass # FIXME: Start load in background session.ui.debug('Good passphrase for %s' % session_id) return self._logged_in(redirect=redirect) else: session.ui.debug('No GnuPG, checking DEFAULT user') # No GnuPG, see if there is a DEFAULT user in the config user = '******' except (AssertionError, IOError): session.ui.debug('Bad passphrase for %s' % session_id) return self._error(_('Invalid passphrase, please try again')) if user in config.logins or user == 'DEFAULT': # FIXME: Salt and hash the password, check if it matches # the entry in our user/password list (TODO). # NOTE: This hack effectively disables auth without GnUPG if user == 'DEFAULT': session.ui.debug('FIXME: Unauthorized login allowed') return self._logged_in(redirect=redirect) raise Exception('FIXME') self._error(_('Incorrect username or password'))
def VerifyAndStorePassphrase(config, passphrase=None, sps=None): if passphrase and not sps: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(passphrase) passphrase = 'this probably does not really overwrite :-( ' assert(sps is not None) gpg = GnuPG(use_agent=False) if gpg.is_available(): gpg.passphrase = sps.get_reader() gpgr = config.prefs.gpg_recipient gpgr = gpgr if (gpgr not in (None, '', '!CREATE')) else None assert(gpg.sign('Sign This!', fromkey=gpgr)[0] == 0) return sps
def VerifyAndStorePassphrase(config, passphrase=None, sps=None, key=None): if passphrase and not sps: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(passphrase) passphrase = 'this probably does not really overwrite :-( ' assert (sps is not None) gpg = GnuPG(use_agent=False) if gpg.is_available(): gpg.passphrase = sps.get_reader() gpgr = config.prefs.gpg_recipient gpgr = key or (gpgr if (gpgr not in (None, '', '!CREATE')) else None) assert (gpg.sign('Sign This!', fromkey=gpgr)[0] == 0) return sps
def VerifyAndStorePassphrase(config, passphrase=None, sps=None, key=None): if passphrase and not sps: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(passphrase) passphrase = 'this probably does not really overwrite :-( ' assert (sps is not None) gpg = GnuPG(None, use_agent=False) if gpg.is_available(): gpg.passphrase = sps.get_reader() gpgr = config.prefs.gpg_recipient gpgr = key or (gpgr if (gpgr not in (None, '', '!CREATE')) else None) assert (gpg.sign('Sign This!', fromkey=gpgr)[0] == 0) # Fun side effect: changing the passphrase invalidates the message cache import mailpile.mailutils mailpile.mailutils.ClearParseCache(full=True) return sps
def VerifyAndStorePassphrase(config, passphrase=None, sps=None, key=None): if passphrase and not sps: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(passphrase) passphrase = 'this probably does not really overwrite :-( ' assert(sps is not None) gpg = GnuPG(None, use_agent=False) if gpg.is_available(): gpg.passphrase = sps.get_reader() gpgr = config.prefs.gpg_recipient gpgr = key or (gpgr if (gpgr not in (None, '', '!CREATE')) else None) assert(gpg.sign('Sign This!', fromkey=gpgr)[0] == 0) # Fun side effect: changing the passphrase invalidates the message cache import mailpile.mailutils mailpile.mailutils.ClearParseCache(full=True) return sps
def VerifyAndStorePassphrase(config, passphrase=None, sps=None, key=None): if passphrase and not sps: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(passphrase) passphrase = 'this probably does not really overwrite :-( ' assert (sps is not None) assert (config.load_master_key(sps)) # Fun side effect: changing the passphrase invalidates the message cache import mailpile.mailutils mailpile.mailutils.ClearParseCache(full=True) return sps
def setup_command(self, session): changed = False if self.data.get('_method') == 'POST' or self._testing(): name, email, note, pwd = (self.data.get(k, [None])[0] for k in ('name', 'email', 'note', 'pass')) if email: rv = AddProfile(session, data=self.data).run() if rv.status == 'success': # # FIXME: We need to fire off a background process to # try and auto-discover routes and sources. # if not session.config.prefs.default_email: session.config.prefs.default_email = email changed = True self.save_profiles_to_key() else: return self._error(_('Failed to add profile'), info=rv.error_info, result=self._result()) if email and pwd: sps = SecurePassphraseStorage(pwd) SetupProfiles.PASSWORD_CACHE[email] = sps result = self._result() if not result['default_email']: profiles = result['profiles'].values() profiles.sort( key=lambda p: (len(p['pgp_keys']), len(p['name']))) e = result['default_email'] = profiles[-1]['email'] session.config.prefs.default_email = e changed = True else: result = self._result() if changed: self._background_save(config=True) return self._success(_('Your profiles'), result)
def command(self): config = self.session.config policyttl = self.args[1] if (len(self.args) > 1) else 'cache-only:-1' if 'policy-ttl' in self.data: policyttl = self.data['policy-ttl'][0] if ':' in policyttl: policy, ttl = policyttl.split(':') else: policy, ttl = policyttl, -1 if 'policy' in self.data: policy = self.data['policy'][0] if 'ttl' in self.data: ttl = self.data['policy'][0] fingerprint = info = None keyid = self.args[0] if self.args else self.data.get('id', [None])[0] if keyid: fingerprint, info = self._lookup_key(keyid) result = {'key': info} else: result = {'keylist': self._gnupg().list_secret_keys()} if self.data.get('_method', None) == 'GET': password = False return self._success(_('Enter your password'), result) assert(keyid is not None and fingerprint is not None) def happy(msg): # Fun side effect: changing the passphrase invalidates the message cache import mailpile.mailutils mailpile.mailutils.ClearParseCache(full=True) redirect = self.data.get('redirect', [None])[0] if redirect: raise UrlRedirectException(redirect) return self._success(msg, result) if policy == 'forget': if fingerprint in config.passphrases: del config.passphrases[fingerprint] if fingerprint in config.secrets: config.secrets[fingerprint].password = '' return happy(_('Password forgotten!')) if policy == 'fail': if fingerprint in config.passphrases: del config.passphrases[fingerprint] config.secrets[fingerprint] = { 'policy': policy } return happy(_('Password will never be stored')) if self.data.get('_method', None) == 'POST': password = self.data.get('password', [None])[0] else: password = self.session.ui.get_password(_('Enter your password:'******' ') if policy == 'store': if fingerprint in config.passphrases: del config.passphrases[fingerprint] config.secrets[fingerprint] = { 'password': password, 'policy': policy } return happy(_('Password stored permanently')) elif policy == 'cache-only' and password: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(password) sps.expiration = time.time() + float(ttl) config.passphrases[fingerprint] = sps if fingerprint.lower() in config.secrets: del config.secrets[fingerprint.lower()] return happy(_('Password stored temporarily')) else: return self._error(_('Invalid password policy!'), result)
def command(self): config = self.session.config policyttl = self.args[1] if (len(self.args) > 1) else 'cache-only:-1' if 'policy-ttl' in self.data: policyttl = self.data['policy-ttl'][0] if ':' in policyttl: policy, ttl = policyttl.split(':') else: policy, ttl = policyttl, -1 if 'policy' in self.data: policy = self.data['policy'][0] if 'ttl' in self.data: ttl = self.data['policy'][0] fingerprint = info = None keyid = self.args[0] if self.args else self.data.get('id', [None])[0] if keyid: fingerprint, info = self._lookup_key(keyid) result = {'key': info} else: result = {'keylist': self._gnupg().list_secret_keys()} if self.data.get('_method', None) == 'GET': password = False return self._success(_('Enter your password'), result) assert (keyid is not None and fingerprint is not None) def happy(msg): # Fun side effect: changing the passphrase invalidates the message cache import mailpile.mailutils mailpile.mailutils.ClearParseCache(full=True) redirect = self.data.get('redirect', [None])[0] if redirect: raise UrlRedirectException(redirect) return self._success(msg, result) if policy == 'forget': if fingerprint in config.passphrases: del config.passphrases[fingerprint] if fingerprint in config.secrets: config.secrets[fingerprint].password = '' return happy(_('Password forgotten!')) if policy == 'fail': if fingerprint in config.passphrases: del config.passphrases[fingerprint] config.secrets[fingerprint] = {'policy': policy} return happy(_('Password will never be stored')) if self.data.get('_method', None) == 'POST': password = self.data.get('password', [None])[0] else: password = self.session.ui.get_password( _('Enter your password:'******' ') if policy == 'store': if fingerprint in config.passphrases: del config.passphrases[fingerprint] config.secrets[fingerprint] = { 'password': password, 'policy': policy } return happy(_('Password stored permanently')) elif policy == 'cache-only' and password: from mailpile.config import SecurePassphraseStorage sps = SecurePassphraseStorage(password) sps.expiration = time.time() + float(ttl) config.passphrases[fingerprint] = sps if fingerprint.lower() in config.secrets: del config.secrets[fingerprint.lower()] return happy(_('Password stored temporarily')) else: return self._error(_('Invalid password policy!'), result)
def setup_command(self, session): changed = authed = False results = { 'secret_keys': self.list_secret_keys(), } error_info = None if self.data.get('_method') == 'POST' or self._testing(): # 1st, are we choosing or creating a new key? choose_key = self.data.get('choose_key', [''])[0] if choose_key and not error_info: if (choose_key not in results['secret_keys'] and choose_key != '!CREATE'): error_info = (_('Invalid key'), { 'invalid_key': True, 'chosen_key': choose_key }) # 2nd, check authentication... # # FIXME: Creating a new key will allow a malicious actor to # bypass authentication and change settings. # try: passphrase = self.data.get('passphrase', [''])[0] passphrase2 = self.data.get('passphrase_confirm', [''])[0] chosen_key = ((not error_info) and choose_key ) or session.config.prefs.gpg_recipient if not error_info: assert(passphrase == passphrase2) if chosen_key == '!CREATE': assert(passphrase != '') sps = SecurePassphraseStorage(passphrase) elif chosen_key: sps = mailpile.auth.VerifyAndStorePassphrase( session.config, passphrase=passphrase, key=chosen_key) else: sps = mailpile.auth.VerifyAndStorePassphrase( session.config, passphrase=passphrase) if not chosen_key: choose_key = '!CREATE' results['updated_passphrase'] = True session.config.gnupg_passphrase.data = sps.data mailpile.auth.SetLoggedIn(self) except AssertionError: error_info = (_('Invalid passphrase'), { 'invalid_passphrase': True, 'chosen_key': session.config.prefs.gpg_recipient }) # 3rd, if necessary master key and/or GPG key with BLOCK_HTTPD_LOCK, Idle_HTTPD(): if choose_key and not error_info: session.config.prefs.gpg_recipient = choose_key # FIXME: This should probably only happen if the GPG # key was successfully created. self.make_master_key() changed = True with Setup.KEY_WORKER_LOCK: if ((not error_info) and (session.config.prefs.gpg_recipient == '!CREATE') and (Setup.KEY_CREATING_THREAD is None or Setup.KEY_CREATING_THREAD.failed)): gk = GnuPGKeyGenerator( sps=session.config.gnupg_passphrase, on_complete=('notify', lambda: self.gpg_key_ready(gk))) Setup.KEY_CREATING_THREAD = gk Setup.KEY_CREATING_THREAD.start() # Finally we update misc. settings for key in self.HTTP_POST_VARS.keys(): # FIXME: This should probably only happen if the GPG # key was successfully created. # Continue iff all is well... if error_info: break if key in (['choose_key', 'passphrase', 'passphrase_confirm'] + TestableWebbable.HTTP_POST_VARS.keys()): continue try: val = self.data.get(key, [''])[0] if val: session.config.prefs[key] = self.TRUTHY[val.lower()] changed = True except (ValueError, KeyError): error_info = (_('Invalid preference'), { 'invalid_setting': True, 'variable': key }) results.update({ 'creating_key': (Setup.KEY_CREATING_THREAD is not None and Setup.KEY_CREATING_THREAD.running), 'creating_failed': (Setup.KEY_CREATING_THREAD is not None and Setup.KEY_CREATING_THREAD.failed), 'chosen_key': session.config.prefs.gpg_recipient, 'prefs': { 'index_encrypted': session.config.prefs.index_encrypted, 'obfuscate_index': session.config.prefs.obfuscate_index, 'encrypt_mail': session.config.prefs.encrypt_mail, 'encrypt_index': session.config.prefs.encrypt_index, 'encrypt_vcards': session.config.prefs.encrypt_vcards, 'encrypt_events': session.config.prefs.encrypt_events, 'encrypt_misc': session.config.prefs.encrypt_misc } }) if changed: self._background_save(config=True) if error_info: return self._error(error_info[0], info=error_info[1], result=results) elif changed: return self._success(_('Updated crypto preferences'), results) else: return self._success(_('Configure crypto preferences'), results)