Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: can this library be used with smptlib? #29

Closed
wxd opened this issue Jan 19, 2022 · 4 comments
Closed

Question: can this library be used with smptlib? #29

wxd opened this issue Jan 19, 2022 · 4 comments

Comments

@wxd
Copy link

wxd commented Jan 19, 2022

(I suppose this is a very basic question...)

I've got a trivial piece of Python code that uses smtplib from the standard library to connect to an SMTP server and send e-mails. The server didn't use to require any authentication; but now it got replaced by an Exchange server that does require NTLM authentication, which is not supported by smptlib out of the box.

Googling returned an appealing snippet, alas from an unmaintained library, some more unmaintained libraries, and this one. Hence the question: can I use combine the code from this library with smtplib to get through NTLM authentication?

I don't know anything about NTLM to figure it all out myself. Thanks in advance!

@jborean93
Copy link
Owner

Most likely you can use this library for adding auth with SMTP. I have no idea how smtplib works but going by your example snippet you could do something like:

import base64
import spnego

from smtplib import SMTPException, SMTPAuthenticationError


def ntlm_authenticate(smtp, username, password):
    auth = spnego.client(username, password, service="SMTP", protocol="ntlm")
    ntlm_negotiate = auth.step()

    code, response = smtp.docmd("AUTH", "NTLM " + base64.b64encode(ntlm_negotiate).decode())
    if code != 334:
        raise SMTPException("Server did not respond as expected to NTLM negotiate message")

    # I'm unsure of the format that is returned but if it's like the above this should work
    # This is essentially getting the base64 decoded value of challenge response
    ntlm_challenge = base64.b64decode(response)
    ntlm_auth = auth.step(ntlm_challenge)
    code, response = smtp.docmd("", base64.b64encode(ntlm_auth).decode())
    if code != 235:
        raise SMTPAuthenticationError(code, response)

You could even use this to work with Kerberos (or Negotiate auth that wraps both Kerberos and NTLM) if you wished.

@wxd
Copy link
Author

wxd commented Jan 20, 2022

Thanks a lot, @jborean93! Your snippet just worked for me.

Closing the issue.

@wxd wxd closed this as completed Jan 20, 2022
@alex-pobeditel-2004
Copy link

@wxd
I found this issue yesterday while setting up integration with Exchange and proposal of @jborean93 worked for me excellently, too :)
But later to my surprise I realized that async SMTP library which I use can login itself to this server just as easy: https://github.com/cole/aiosmtplib
It is almost in-place replacement for standard smtplib, but async.

So all this code can be written as simple as
await smtp_client.login(username=my_username, password=my_password)
What was really surprisingly because there is no mention of NTLM login in documentation and my server returned
250-AUTH GSSAPI NTLM
so "usual" authentication methods seemed to be unavailable there.

Anyway, I will keep this external depencency method as a fallback - they both work perfectly :)

@Lamber-maybe
Copy link

Most likely you can use this library for adding auth with SMTP. I have no idea how smtplib works but going by your example snippet you could do something like:

import base64
import spnego

from smtplib import SMTPException, SMTPAuthenticationError


def ntlm_authenticate(smtp, username, password):
    auth = spnego.client(username, password, service="SMTP", protocol="ntlm")
    ntlm_negotiate = auth.step()

    code, response = smtp.docmd("AUTH", "NTLM " + base64.b64encode(ntlm_negotiate).decode())
    if code != 334:
        raise SMTPException("Server did not respond as expected to NTLM negotiate message")

    # I'm unsure of the format that is returned but if it's like the above this should work
    # This is essentially getting the base64 decoded value of challenge response
    ntlm_challenge = base64.b64decode(response)
    ntlm_auth = auth.step(ntlm_challenge)
    code, response = smtp.docmd("", base64.b64encode(ntlm_auth).decode())
    if code != 235:
        raise SMTPAuthenticationError(code, response)

You could even use this to work with Kerberos (or Negotiate auth that wraps both Kerberos and NTLM) if you wished.

Here is a complete example of authenticating SMTP using NTLM via smtplib, thanks to @jborean93 for providing the demo

import base64
import spnego
import smtplib
from smtplib import SMTPException, SMTPAuthenticationError

# SMTP server info
smtp_server = 'mail.example.com'
smtp_port = 25
username = 'your_username'
password = 'your_password'

# Define ntlm authentication function
def ntlm_authenticate(smtp, username, password):
    auth = spnego.client(username, password, service="SMTP", protocol="ntlm")
    ntlm_negotiate = auth.step()

    code, response = smtp.docmd("AUTH", "NTLM " + base64.b64encode(ntlm_negotiate).decode())
    if code != 334:
        raise SMTPException("Server did not respond as expected to NTLM negotiate message")

    # I'm unsure of the format that is returned but if it's like the above this should work
    # This is essentially getting the base64 decoded value of challenge response
    ntlm_challenge = base64.b64decode(response)
    ntlm_auth = auth.step(ntlm_challenge)
    code, response = smtp.docmd("", base64.b64encode(ntlm_auth).decode())
    if code != 235:
        raise SMTPAuthenticationError(code, response)

# Connect to smtp
smtp = smtplib.SMTP(smtp_server, smtp_port)
ehlo_response = smtp.ehlo()
ntlm_authenticate(smtp, username, password)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants