def __call__(self, server: SMTP, session: Session, envelope: Envelope, mechanism: str, auth_data: Tuple[bytes, bytes], ) -> AuthResult: fail_nothandled = AuthResult(success=False, handled=False) if mechanism not in ("LOGIN", "PLAIN"): return fail_nothandled if not isinstance(auth_data, LoginPassword): return fail_nothandled username = auth_data.login.decode('utf-8') password = auth_data.password.decode('utf-8') conn = sqlite3.connect(str(self.auth_db)) curs = conn.execute( "SELECT hashpass FROM userauth WHERE username=?", (username,) ) hash_db = curs.fetchone() conn.close() if not hash_db: return fail_nothandled try: if self.ph.verify(hash_db[0], password): self.logger.info(f"User {username} authenticated") return AuthResult(success=True) except VerifyMismatchError: self.logger.warning(f"User {username} failed authentication!") return fail_nothandled
async def handle_smtpd_auth(self, server: SMTP, method: str, data: Any) -> AuthResult: if method == 'login' or method == 'plain': assert isinstance(data, LoginPassword) username: bytes = data.login password: bytes = data.password username_s = username.decode( 'utf-8') # TODO (rubicon): support the other charsets result = await self.storage_hub.user_records.check_user_password( username_s, password) return AuthResult(success=result, handled=True) else: return AuthResult(success=False, handled=False)
async def auth_PLAIN(self, server: SMTP, args: List[str]) -> AuthResult: words = await server.challenge_auth("", encode_to_b64=False) if words == aiosmtpd.smtp.MISSING: return AuthResult(success=False, handled=False) decoded_words = standard_b64decode(cast(bytes, words)) parts = decoded_words.split(b"\0") if parts: if not parts[0]: parts.pop(0) if len(parts) != 2: return AuthResult(success=False, handled=True) return await self.smtp_auth_handler(server, "plain", LoginPassword(parts[0], parts[1]))
async def auth_LOGIN(self, server: SMTP, args: List[str]) -> AuthResult: username = await server.challenge_auth("Username:"******"Password:"******"login", LoginPassword(cast(bytes, username), cast(bytes, password)))
def authenticator(self, _unused_server: SMTP, _unused_session: Session, _unused_envelope: Envelope, mechanism: str, login_data: Tuple[bytes, bytes]) -> AuthResult: if mechanism not in ('LOGIN', 'PLAIN'): return AuthResult(success=False, handled=False) if not isinstance(login_data, LoginPassword): # type: ignore return AuthResult(success=False, handled=False) username = login_data.login.decode() # type: ignore password = login_data.password.decode() # type: ignore if username != self._user or password != self._password: return AuthResult(success=False, handled=False) return AuthResult(success=True)
def authenticator( server: SMTP, session: Session, envelope: Envelope, mechanism: str, auth_data: LoginPassword, ) -> AuthResult: fail_nothandled = AuthResult(success=False, handled=False) if mechanism not in ("LOGIN", "PLAIN"): return fail_nothandled if not isinstance(auth_data, LoginPassword): return fail_nothandled if auth_data.login == b"*****@*****.**" and auth_data.password == b"secret": return AuthResult(success=True) else: return fail_nothandled
def __call__(self, server: SMTP, session: Session, envelope: Envelope, mechanism: str, auth_data): fail_nothandled = AuthResult(success=False, handled=False) if mechanism not in ("LOGIN", "PLAIN"): return fail_nothandled if not isinstance(auth_data, LoginPassword): return fail_nothandled username = auth_data.login.decode() password = auth_data.password.decode() auth_client = db.clients.find_one({ "username": username, "password": password }) if auth_client: return AuthResult(success=True, auth_data={"id": str(auth_client['_id'])}) return fail_nothandled
async def auth_PLAIN(self, server, args): log.debug("AUTH PLAIN received") response = None if len(args) >= 2: response = base64.b64decode(args[1]) else: response = await server.challenge_auth("") response = response.decode() response = response.split() if len(response) < 2: return AuthResult(success=False, handled=False) if ( len(response) >= 2 and server._authenticator.validate(response[0], response[-1]) ): return AuthResult(success=True, handled=True) return AuthResult(success=False, handled=False)
def __call__(self, server, session, envelope, mechanism, auth_data): fail_nothandled = AuthResult(success=False, handled=False) if mechanism not in ("LOGIN", "PLAIN"): return fail_nothandled if not isinstance(auth_data, LoginPassword): return fail_nothandled username = auth_data.login password = auth_data.password hashpass = self.ph.hash(password) conn = sqlite3.connect(self.auth_db) curs = conn.execute("SELECT hashpass FROM userauth WHERE username=?", (username, )) hash_db = curs.fetchone() conn.close() if not hash_db: return fail_nothandled if hashpass != hash_db[0]: return fail_nothandled return AuthResult(success=True)
async def auth_PLAIN(self, server: SMTP, args: List[str]) -> AuthResult: log.debug("AUTH PLAIN received") response = b"" if len(args) >= 2: response = base64.b64decode(args[1]) else: response = await server.challenge_auth("") decoded_resp = response.decode() split_resp: List[str] = decoded_resp.split() if len(split_resp) < 2: return AuthResult(success=False, handled=False) if ( len(split_resp) >= 2 and server._authenticator.validate(split_resp[0], split_resp[-1]) ): log.debug("AUTH PLAIN succeeded") return AuthResult(success=True, handled=True) log.debug("AUTH PLAIN failed") return AuthResult(success=False, handled=False)
async def auth_CRAM_MD5(self, server: SMTP, args: List[str]) -> AuthResult: log.debug("AUTH CRAM-MD5 received") # Generate challenge secret = secrets.token_hex(8) ts = datetime.now().timestamp() hostname = server.hostname challenge = f"<{secret}{ts}@{hostname}>" response = await server.challenge_auth(challenge) user, received = response.split() password = server._authenticator.get_password(user.decode()) # Verify mac = hmac.HMAC(password.encode(), challenge.encode(), "md5") expected = mac.hexdigest().encode() if hmac.compare_digest(expected, received): log.debug("AUTH CARM-MD5 succeeded") return AuthResult(success=True, handled=True, auth_data=user) log.debug("AUTH CRAM-MD5 failed") return AuthResult(success=False, handled=False)
async def auth_LOGIN(self, server: SMTP, args: List[str]) -> AuthResult: log.info("AUTH LOGIN received") login = [] for n in range(1, len(args)): arg = base64.b64decode(args[n]).decode() login.extend(arg.split(maxsplit=1)) while len(login) < 2: prompt = "Password" if len(login) >= 1 else "" response = await server.challenge_auth(prompt) if response is MISSING: return AuthResult(success=False, handled=True) response = response.decode() login.extend(response.split(maxsplit=1 - len(login))) username = login[0] password = login[1] if server._authenticator.validate(username, password): log.info("AUTH LOGIN succeeded.") return AuthResult(success=True, handled=True, auth_data=username) log.info("AUTH LOGIN failed.") return AuthResult(success=False, handled=False)
def __call__(self, server, session, envelope, mechanism, auth_data): return AuthResult(success=True)
async def auth_PLAIN(self, _, args: list[str]) -> AuthResult: expected = 'AHVzZXJuYW1lAHBhc3N3b3Jk' if args[1] == expected and success: return AuthResult(success=True) else: return AuthResult(success=False, handled=False)
async def smtpd_auth_rejectall(*args, **kargs): return AuthResult(success=False, handled=True)