def conversation(self, auth, query_list, data): try: return list(self._conversation(auth, query_list, data)) except: AUTH.error('Unexpected error during PAM conversation: %s' % (traceback.format_exc(), )) raise
def authenticate(self, username, password, **answers): answers.update({ PAM_TEXT_INFO: '', PAM_ERROR_MSG: '', PAM_PROMPT_ECHO_ON: username, PAM_PROMPT_ECHO_OFF: password, }) missing = [] self.start(username, (answers, [], missing)) try: self.pam.authenticate() self.pam.acct_mgmt() except PAMError as pam_err: AUTH.error("PAM: authentication error: %s" % (pam_err, )) if pam_err.args[ 1] == PAM_NEW_AUTHTOK_REQD: # error: ('Authentication token is no longer valid; new one required', 12) raise PasswordExpired(self.error_message(pam_err.args)) if pam_err.args[ 1] == PAM_ACCT_EXPIRED: # error: ('User account has expired', 13) raise AccountExpired(self.error_message(pam_err.args)) if missing: message = self._('Please insert your one time password (OTP).') raise AuthenticationInformationMissing(message, missing) raise AuthenticationFailed(self.error_message(pam_err.args))
def change_password(self, username, old_password, new_password): # type: (str, str, str) -> None answers = { PAM_TEXT_INFO: '', PAM_ERROR_MSG: '', PAM_PROMPT_ECHO_ON: username, PAM_PROMPT_ECHO_OFF: [old_password, new_password, new_password], # pam_kerberos asks for the old password first and then twice for the new password. # 'Current Kerberos password: '******'New password: '******'Retype new password: '******'LC_ALL=en_US.UTF-8') self.pam.putenv('LC_MESSAGES=en_US.UTF-8') self.pam.putenv('LANG=en_US.UTF-8') try: self.pam.chauthtok() except PAMError as pam_err: AUTH.warn('Changing password failed (%s). Prompts: %r' % (pam_err, prompts)) message = self._parse_error_message_from(pam_err.args, prompts) raise PasswordChangeFailed( '%s %s' % (self._('Changing password failed.'), message))
def conversation(self, auth, query_list, data): # type: (Any, Any, Any) -> List try: return list(self._conversation(auth, query_list, data)) except BaseException: AUTH.error('Unexpected error during PAM conversation: %s' % (traceback.format_exc(), )) raise
def __authentication_result(self, thread, result, request): if isinstance(result, BaseException) and not isinstance(result, (AuthenticationFailed, AuthenticationInformationMissing, PasswordExpired, PasswordChangeFailed, AccountExpired)): msg = ''.join(thread.trace + traceback.format_exception_only(*thread.exc_info[:2])) AUTH.error(msg) if isinstance(result, tuple): username, password = result result = {'username': username, 'password': password, 'auth_type': request.body.get('auth_type')} auth_result = AuthenticationResult(result) self.signal_emit('authenticated', auth_result, request)
def _conversation( self, auth, query_list, data ): # type: (Any, List[Tuple[Any, Any]], Any) -> Iterator[Tuple[str, int]] answers, prompts, missing = data prompts.extend(query_list) for query, qt in query_list: prompt = qt if qt == PAM_PROMPT_ECHO_OFF and query.strip( ':\t ') in self.custom_prompts: prompt = query response = '' try: response = answers[prompt] if isinstance(response, list): response = response.pop(0) except KeyError as exc: AUTH.error('Missing answer for prompt: %r' % (str(exc), )) missing.append(query) except IndexError: AUTH.error('Unexpected prompt: %r' % (query, )) if qt in (PAM_TEXT_INFO, PAM_ERROR_MSG): AUTH.info('PAM says: %r' % (query, )) # AUTH.error('# PAM(%d) %s: answer=%r' % (qt, repr(query).strip("':\" "), response)) yield (response, 0)
def __canonicalize_username(self, username): try: lo, po = get_machine_connection(write=False) result = None if lo: attr = 'mailPrimaryAddress' if '@' in username else 'uid' result = lo.search(filter_format('(&(%s=%s)(objectClass=person))', (attr, username)), attr=['uid'], unique=True) if result and result[0][1].get('uid'): username = result[0][1]['uid'][0] AUTH.info('Canonicalized username: %r' % (username,)) except (ldap.LDAPError, udm_errors.ldapError) as exc: # /etc/machine.secret missing or LDAP server not reachable AUTH.warn('Canonicalization of username was not possible: %s' % (exc,)) reset_cache() except: AUTH.error('Canonicalization of username failed: %s' % (traceback.format_exc(),)) finally: # ignore all exceptions, even in except blocks return username
def __authenticate_thread(self, username, password, new_password, **custom_prompts): AUTH.info('Trying to authenticate user %r' % (username,)) username = self.__canonicalize_username(username) try: self.pam.authenticate(username, password, **custom_prompts) except AuthenticationFailed as auth_failed: AUTH.error(str(auth_failed)) raise except PasswordExpired as pass_expired: AUTH.info(str(pass_expired)) if new_password is None: raise try: self.pam.change_password(username, password, new_password) except PasswordChangeFailed as change_failed: AUTH.error(str(change_failed)) raise else: AUTH.info('Password change for %r was successful' % (username,)) return (username, new_password) else: AUTH.info('Authentication for %r was successful' % (username,)) return (username, password)