SMTP through Exchange using Integrated Windows Authentication (NTLM) using Python

后端 未结 3 1131
没有蜡笔的小新
没有蜡笔的小新 2020-12-24 09:18

I want to use the credentials of the logged-in Windows user to authenticate an SMTP connection to an Exchange server using NTLM.

I\'m aware of the python-ntlm module

相关标签:
3条回答
  • 2020-12-24 09:38

    Great answer but as an update for python 3

    def asbase64(msg):
        # encoding the message then convert to string
        return base64.b64encode(msg).decode("utf-8")
    
    0 讨论(0)
  • 2020-12-24 09:43

    Although the solution below only uses the Python Win32 extensions (the sspi example code included with the Python Win32 extensions was very helpful), the python-ntlm IMAP & SMTP patches mentioned in the question also served as useful guides.

    from smtplib import SMTPException, SMTPAuthenticationError
    import string
    import base64
    import sspi
    
    # NTLM Guide -- http://curl.haxx.se/rfc/ntlm.html
    
    SMTP_EHLO_OKAY = 250
    SMTP_AUTH_CHALLENGE = 334
    SMTP_AUTH_OKAY = 235
    
    def asbase64(msg):
        # encoding the message then convert to string
        return base64.b64encode(msg).decode("utf-8")
    
    def connect_to_exchange_as_current_user(smtp):
        """Example:
        >>> import smtplib
        >>> smtp = smtplib.SMTP("my.smtp.server")
        >>> connect_to_exchange_as_current_user(smtp)
        """
    
        # Send the SMTP EHLO command
        code, response = smtp.ehlo()
        if code != SMTP_EHLO_OKAY:
            raise SMTPException("Server did not respond as expected to EHLO command")
    
        sspiclient = sspi.ClientAuth('NTLM')
    
        # Generate the NTLM Type 1 message
        sec_buffer=None
        err, sec_buffer = sspiclient.authorize(sec_buffer)
        ntlm_message = asbase64(sec_buffer[0].Buffer)
    
        # Send the NTLM Type 1 message -- Authentication Request
        code, response = smtp.docmd("AUTH", "NTLM " + ntlm_message)
    
        # Verify the NTLM Type 2 response -- Challenge Message
        if code != SMTP_AUTH_CHALLENGE:
            raise SMTPException("Server did not respond as expected to NTLM negotiate message")
    
        # Generate the NTLM Type 3 message
        err, sec_buffer = sspiclient.authorize(base64.decodebytes(response))
        ntlm_message = asbase64(sec_buffer[0].Buffer)
    
        # Send the NTLM Type 3 message -- Response Message
        code, response = smtp.docmd(ntlm_message)
        if code != SMTP_AUTH_OKAY:
            raise SMTPAuthenticationError(code, response)
    
    0 讨论(0)
  • 2020-12-24 09:54

    Python 2.7.x will fail on sending the NTLM Type 3 message due to the blank cmd specified:

    code, response = smtp.docmd("", ntlm_message)

    This ends up sending the correct response back to the server, however it pre-pends a space due to the nature of docmd() calling putcmd().

    smtplib.py:

    def putcmd(self, cmd, args=""):
        """Send a command to the server."""
        if args == "":
            str = '%s%s' % (cmd, CRLF)
        else:
            str = '%s %s%s' % (cmd, args, CRLF)
        self.send(str)
    
    # ...
    
    def docmd(self, cmd, args=""):
        """Send a command, and return its response code."""
        self.putcmd(cmd, args)
        return self.getreply()
    

    which as a result takes the path of the else condition, thereby sending str(' ' + ntlm_message + CRLF) which results in (501, 'Syntax error in parameters or arguments').

    As such the fix is simply to send the NTLM message as the cmd.

    code, response = smtp.docmd(ntlm_message)

    A fix to the above answer was submitted, though who knows when it will be reviewed/accepted.

    0 讨论(0)
提交回复
热议问题