Reputation: 13
I am trying to get authentication working using aiosmtpd... I have read the documentation but still can't get it working.
I have followed the instructions on this post: python aiosmtpd server with basic authentication , but still it isn't work
I have the following as the aiosmtpd server:
import logging
from aiosmtpd.controller import Controller
from aiosmtpd.smtp import AuthResult, LoginPassword
auth_db = {
b"user1": b"password1",
b"user2": b"password2",
b"user3": b"password3",
}
# Authenticator function
def authenticator_func(server, session, envelope, mechanism, auth_data):
# For this simple example, we'll ignore other parameters
assert isinstance(auth_data, LoginPassword)
username = auth_data.login
password = auth_data.password
# I am returning always AuthResult(success=True) just for testing
if auth_db.get(username) == password:
return AuthResult(success=True)
else:
return AuthResult(success=True)
# return AuthResult(success=False, handled=False)
class CustomSMTPHandler:
async def handle_DATA(self, server, session, envelope):
print('End of message')
return '250 Message accepted for delivery'
logging.basicConfig(level=logging.DEBUG)
handler = CustomSMTPHandler()
controller = Controller(
handler,
hostname='192.168.1.1',
port=8025,
authenticator=authenticator_func, # i.e., the name of your authenticator function
auth_required=True, # Depending on your needs
)
controller.start()
input("Servidor iniciado. Presione Return para salir.")
controller.stop()
And this is the client:
import time
import yaml
import smtplib
import os
from smtplib import SMTPException
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# Variables ambiente
SMTP_SERVER = "192.168.1.1"
SMTP_PORT = 8025
def send_mail(mail_from, mail_to, mail_subject, mail_message):
try:
# Crea conexion a servidor
smtp_server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
smtp_server.set_debuglevel(True)
# Creacion de mensage
msg = MIMEMultipart()
msg['From'] = mail_from
msg['To'] = mail_to
msg['Subject'] = mail_subject
msg.attach(MIMEText(mail_message, 'html'))
smtp_server.login('user1', 'password1')
# Envio de correo
smtp_server.send_message(msg)
# Cierra conexion
smtp_server.quit()
# print("Email sent successfully!")
except SMTPException as smtp_err:
return {"Error": f"Error SMTP: {repr(smtp_err)}"}
# print("SMTP Exception...", smtp_err)
except Exception as gen_err:
return {"Error": f"Error general: {repr(gen_err)}"}
# print("Something went wrong….", gen_err)
return {"Exito": f"Correo enviado a servidor SMTP: {SMTP_SERVER}"}
def main():
st = time.time()
for _ in range(1):
resp = send_mail("[email protected]",
"[email protected]",
"Subject del correo",
"<b>Hola</b>")
print(resp)
et = time.time()
elapsed_time = et - st
print('Execution time:', elapsed_time, 'seconds')
if __name__ == "__main__":
main()
I am getting the following as the server output:
INFO:mail.log:Available AUTH mechanisms: LOGIN(builtin) PLAIN(builtin)
INFO:mail.log:Peer: ('192.168.1.X', 54186)
INFO:mail.log:('192.168.1.X', 54186) handling connection
DEBUG:mail.log:('192.168.1.X', 54186) << b'220 PC-Name Python SMTP 1.4.5'
DEBUG:mail.log:_handle_client readline: b'EHLO [192.168.1.X]\r\n'
INFO:mail.log:('192.168.1.X', 54186) >> b'EHLO [192.168.1.X]'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250-PC-Name'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250-SIZE 33554432'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250-8BITMIME'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250-SMTPUTF8'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250 HELP'
DEBUG:mail.log:_handle_client readline: b'MAIL FROM:<[email protected]> BODY=8BITMIME SIZE=890\r\n'
INFO:mail.log:('192.168.1.X', 54186) >> b'MAIL FROM:<[email protected]> BODY=8BITMIME SIZE=890'
INFO:mail.log:MAIL: Authentication required
DEBUG:mail.log:('192.168.1.X', 54186) << b'530 5.7.0 Authentication required'
INFO:mail.log:('192.168.1.X', 54186) EOF received
INFO:mail.log:('192.168.1.X', 54186) Connection lost during _handle_client()
INFO:mail.log:('192.168.1.X', 54186) connection lost
I am also not able to send emails using thunderbird.
Any ideas?
Posting client log after adding the line:
smtp_server.set_debuglevel(True)
to the send_mail function.
Log:
send: 'ehlo [192.168.1.X]\r\n'
reply: b'250-PC-Name-7010\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250-SMTPUTF8\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'PC-Name\nSIZE 33554432\n8BITMIME\nSMTPUTF8\nHELP'
{'Error': "Error SMTP: SMTPNotSupportedError('SMTP AUTH extension not supported by server.')"}
Upvotes: 0
Views: 399
Reputation: 13
Well, I've got it working... I changed code on both server (following advice from @tripleee about Authenticator instance) to handle authentication and on the client to send the "LOGIN" and/or "PLAIN" authentication method to the server (those are the methods aiosmtpd can handle by default).
Here is the server code:
import logging
import aiosmtpd.controller
from aiosmtpd.smtp import Envelope, AuthResult, LoginPassword
from email import message_from_bytes
from email.message import Message
from email.policy import default
class Authenticator:
def __call__(self, server, session, envelope, mechanism, auth_data):
test_passwd = "1234"
username = auth_data.login
password = auth_data.password
print(f'Username: {username}')
print(f'Password: {password}')
print(f'Password_test: {testpasswd}')
if password == test_passwd:
resp = AuthResult(success=True)
else:
resp = AuthResult(success=False, handled=False)
return resp
class CustomSMTPHandler:
async def handle_DATA(self, server, session, envelope):
""" Used to handle other stuff"""
print('End of message')
return '250 Message accepted for delivery'
def main():
handler = CustomSMTPHandler()
auth = Authenticator()
server = aiosmtpd.controller.Controller(handler,
hostname='192.168.1.1',
port=8025,
authenticator=auth,
auth_required=True,
auth_require_tls=False
)
server.start()
input("Servidor iniciado. Presione Return para salir.")
server.stop()
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
main()
Note: By defaul "auth_require_tls" is set to True for security reasons (as it should be!)... I changed parameter for testing.
Here it is the client code:
import time
import smtplib
import os
from smtplib import SMTPException
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
SMTP_SERVER = "192.168.1.1"
SMTP_PORT = 8025
def send_mail(mail_from, mail_to, mail_subject, mail_message):
try:
smtp_server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
smtp_server.set_debuglevel(True)
msg = MIMEMultipart()
msg['From'] = mail_from
msg['To'] = mail_to
msg['Subject'] = mail_subject
msg.attach(MIMEText(mail_message, 'html'))
smtp_server.esmtp_features['auth'] = 'PLAIN'
smtp_server.login('user1', '1234')
smtp_server.send_message(msg)
smtp_server.quit()
except SMTPException as smtp_err:
return {"Error": f"Error SMTP: {repr(smtp_err)}"}
except Exception as gen_err:
return {"Error": f"Error general: {repr(gen_err)}"}
return {"Exito": f"Correo enviado a servidor SMTP: {SMTP_SERVER}"}
def main():
st = time.time()
for _ in range(1):
resp = send_mail("[email protected]",
"[email protected]",
"Subject del correo",
"<b>Hola</b>")
print(resp)
et = time.time()
elapsed_time = et - st
print('Execution time:', elapsed_time, 'seconds')
if __name__ == "__main__":
main()
The change on the client is the "smtp_server.esmtp_features['auth'] = 'PLAIN'" line who tells the authentication is going to be "PLAIN"... You can specify many types like for example: "smtp_server.esmtp_features['auth'] = 'LOGIN PLAIN'".
Upvotes: 1
Reputation: 189628
The debug error log indirectly tells you what's wrong: The list of supported ESMTP extensions advertised by the server in its EHLO
response does not mention AUTH
, so the client code (correctly) refuses to try to use this optional protocol extension, and therefore your login
call fails.
Your code doesn't show some parts, so this is hard to detect from looking at the code alone; thanks for providing the debug log, and I hope you'll turn to it more in the future to figure out what's wrong when something isn't working.
In some more detail, the base SMTP protocol only supports the basic transport protocol, with no provisions for e.g. 8-bit DATA
. The ESMTP specification extended the protocol so that if the client greets the server with EHLO
instead of HELO
, this enables a mechanism for the server to offer additional facilities to the client (IIRC the SIZE
extension is mandatory, maybe 8BITMIME
too? But other than that, what exactly to offer is up to the server's capabilities and configuration).
The de facto standard these days is to offer the AUTH
extension (with a potentially growing palette of implementation options, starting from very basic cleartext to various challenge/response etc authentication mechanisms) and STARTTLS
to pivot from an unencrypted connection to an encrypted one (ideally before transmitting any credentials!)
I'm not familiar with aiosmtpd
, but the documentation seems to be implying that the authenticator
keyword argument needs to be an Authenticator
instance.
Upvotes: 0