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):