From 2903e2b0826911a2b311e40bab287619ec884b52 Mon Sep 17 00:00:00 2001 From: Sasha Romijn Date: Tue, 8 Oct 2024 12:10:09 +0200 Subject: [PATCH] Switch to smtplib.SMTP for more compliant SMTP handling The current "client" did not wait for the greeting to finish before sending commands, and took some other liberties with the SMTP standard. This causes issues with some servers. --- .../opportunistic_tls_helpers.py | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/sslyze/connection_helpers/opportunistic_tls_helpers.py b/sslyze/connection_helpers/opportunistic_tls_helpers.py index 1a45069f..b962668e 100644 --- a/sslyze/connection_helpers/opportunistic_tls_helpers.py +++ b/sslyze/connection_helpers/opportunistic_tls_helpers.py @@ -2,6 +2,7 @@ import struct from abc import abstractmethod, ABC from enum import Enum +from smtplib import SMTP, SMTPException from typing import ClassVar, Optional @@ -65,19 +66,37 @@ def __init__(self, smtp_ehlo_hostname: str): self._smtp_ehlo_hostname = smtp_ehlo_hostname def prepare_socket_for_tls_handshake(self, sock: socket.socket) -> None: - # Get the SMTP banner - sock.recv(2048) + # SMTP parsing has some complicated areas and some unusual but legal + # server behavior - this code uses Python's smtplib to handle the protocol. + smtp = SMTP(local_hostname=self._smtp_ehlo_hostname) + smtp.sock = sock - # Send a EHLO and wait for the 250 status - sock.send(f"EHLO {self._smtp_ehlo_hostname}\r\n".encode("ascii")) - data = sock.recv(2048) - if b"250 " not in data: - raise OpportunisticTlsError(f"SMTP EHLO was rejected: {repr(data)}") + try: + code, server_reply = smtp.getreply() + message = server_reply.decode() + except SMTPException as exc: + code, message = -1, str(exc) + if code != 220: + raise OpportunisticTlsError(f"Unable to find 220 service ready response: {message}") + + try: + code, server_reply = smtp.getreply() + message = server_reply.decode() + except SMTPException as exc: + code, message = -1, str(exc) + if code != 250: + raise OpportunisticTlsError(f"SMTP EHLO was rejected: {message}") + + if not smtp.has_extn("starttls"): + raise OpportunisticTlsError(f"Server does not support STARTTLS: {message}") - # Send a STARTTLS - sock.send(b"STARTTLS\r\n") - if b"220" not in sock.recv(2048): - raise OpportunisticTlsError("SMTP STARTTLS not supported") + try: + code, server_reply = smtp.getreply() + message = server_reply.decode() + except SMTPException as exc: + code, message = -1, str(exc) + if code != 220: + raise OpportunisticTlsError(f"SMTP STARTTLS rejected: {message}") class _XmppHelper(_OpportunisticTlsHelper):