Discussion:
[Twisted-Python] SMTP: Authenticating on Outbound emails only
Anthony Lukach
2017-02-09 17:29:21 UTC
Permalink
I am working on putting together an SMTP server implemented within
Twisted. This will act as a conduit to my API, where there are two basic
flows:

1) A device that supports sending email can "send" an email through the
SMTP server. This is, in effect, the SMTP server handling the message as an
outbound request. The device would authenticate with the server and then
provide it the message to be sent (which in reality will be uploaded to my
API).

2) A device can send an email to my SMTP server via their own SMTP server.
My server would receive the incoming message, parse its contents, and then
upload the data to my API. Naturally, these incoming messages would not be
required to authenticate with the server.

I'm having trouble constructing the server in a way that outbound messages
require authentication but incoming messages do not.

My service currently looks more or less like below:

from twisted.cred.portal import Portal

from twisted.mail import smtpfrom twisted.mail.imap4 import
LOGINCredentials, PLAINCredentialsclass SMTPFactory(smtp.SMTPFactory):
protocol = smtp.ESMTP def buildProtocol(self, addr): #
Add authentication to SMTP server p =
smtp.SMTPFactory.buildProtocol(self, addr) p.challengers = {
"LOGIN": LOGINCredentials, "PLAIN":
PLAINCredentials } # Set a timeout for the connection
message to be processed p.timeout = 200 return p

provider = Provider('localhost', settings)

# Multiple checkers that for backwards compatibilitycascade =
CascadeChecker()cascade.registerChecker(FilePasswordDB(pw_file))

# ... some other checks


portal = Portal(SimpleRealm(options, provider))

portal.registerChecker(cascade) internet.TCPServer(2500, SMTPFactory(portal
))



The problem is that, as currently written, I need to authenticate with the
server upon connection. Naturally, this doesn't make sense for the inbound
emails. It seems that a common pattern is to analyze the "to" address of
the emails and to allow any incoming emails (without authentication) that
have a matching domain, however this seems difficult to configure with
Twisted as I don't have the context of the email at the time the
authentication check is done.

Can anyone direct me to an example of an SMTP server that is both accepting
inbound and outbound emails and authenticates only on outbound emails?

PS, I'm not super familiar with the inner workings of email or the SMTP
protocol, please let me know if I'm heading in the wrong direction

Best,

Anthony
Jean-Paul Calderone
2017-02-09 18:36:10 UTC
Permalink
Post by Anthony Lukach
I am working on putting together an SMTP server implemented within
Twisted. This will act as a conduit to my API, where there are two basic
1) A device that supports sending email can "send" an email through the
SMTP server. This is, in effect, the SMTP server handling the message as an
outbound request. The device would authenticate with the server and then
provide it the message to be sent (which in reality will be uploaded to my
API).
2) A device can send an email to my SMTP server via their own SMTP server.
My server would receive the incoming message, parse its contents, and then
upload the data to my API. Naturally, these incoming messages would not be
required to authenticate with the server.
I'm having trouble constructing the server in a way that outbound messages
require authentication but incoming messages do not.
twisted.mail and cred support anonymous access. I think that what you want
is to implement an avatar that can only accept messages for local delivery
and use that for anonymous users. If an anonymous user tries to send mail
to a non-local user, they get back an error. Either they made a mistake
and they should try again after authenticating or they're trying to abuse
the service and that's what you want. That's case (2). Then implement the
relay logic in another avatar and use that for authenticated users. That's
case (1).

This relies on the fact that the realm you supply is responsible for
creating avatars and the realm gets told the avatarId for which it should
create an avatar - and the anonymous user can be differentiated from other
users by the avatarId.

Does that help?
Post by Anthony Lukach
Can anyone direct me to an example of an SMTP server that is both
accepting inbound and outbound emails and authenticates only on outbound
emails?
As it happens, yes...

This avatar supports local-only delivery:
https://github.com/twisted/quotient/blob/master/xquotient/mail.py#L51
This one supports relaying:
https://github.com/twisted/quotient/blob/master/xquotient/mail.py#L528
Here's the factory and portal setup code:
https://github.com/twisted/quotient/blob/master/xquotient/mail.py#L302

The realm is a bit spread out and involves some Axiom-specific concepts
that you probably don't need to know... But let me know if the above three
links don't make things clear.

Jean-Paul
Anthony Lukach
2017-02-12 16:50:32 UTC
Permalink
Thanks Jean-Paul, that indeed did help! To restate what you said, we're
now allowing both authenticated and unauthenticated (anonymous)
connections, each returning a different type of Avatar. We then take a
look at what the connection is trying to do (handle outbound email vs.
handle inbound email) and either permit/deny the action based on what
avatar is being used.

I appreciate the help!

Anthony

On Thu, Feb 9, 2017 at 11:39 AM Jean-Paul Calderone <
Post by Anthony Lukach
I am working on putting together an SMTP server implemented within
Twisted. This will act as a conduit to my API, where there are two basic
1) A device that supports sending email can "send" an email through the
SMTP server. This is, in effect, the SMTP server handling the message as an
outbound request. The device would authenticate with the server and then
provide it the message to be sent (which in reality will be uploaded to my
API).
2) A device can send an email to my SMTP server via their own SMTP server.
My server would receive the incoming message, parse its contents, and then
upload the data to my API. Naturally, these incoming messages would not be
required to authenticate with the server.
I'm having trouble constructing the server in a way that outbound messages
require authentication but incoming messages do not.
twisted.mail and cred support anonymous access. I think that what you
want is to implement an avatar that can only accept messages for local
delivery and use that for anonymous users. If an anonymous user tries to
send mail to a non-local user, they get back an error. Either they made a
mistake and they should try again after authenticating or they're trying to
abuse the service and that's what you want. That's case (2). Then
implement the relay logic in another avatar and use that for authenticated
users. That's case (1).
This relies on the fact that the realm you supply is responsible for
creating avatars and the realm gets told the avatarId for which it should
create an avatar - and the anonymous user can be differentiated from other
users by the avatarId.
Does that help?
Can anyone direct me to an example of an SMTP server that is both
accepting inbound and outbound emails and authenticates only on outbound
emails?
As it happens, yes...
https://github.com/twisted/quotient/blob/master/xquotient/mail.py#L51
https://github.com/twisted/quotient/blob/master/xquotient/mail.py#L528
https://github.com/twisted/quotient/blob/master/xquotient/mail.py#L302
The realm is a bit spread out and involves some Axiom-specific concepts
that you probably don't need to know... But let me know if the above three
links don't make things clear.
Jean-Paul
_______________________________________________
Twisted-Python mailing list
http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
Loading...