13

我想使用登录的 Windows 用户的凭据来验证与使用 NTLM 的 Exchange 服务器的 SMTP 连接。

我知道python-ntlm模块和为 SMTP 启用 NTLM 身份验证的两个 补丁,但是我想使用当前用户的安全令牌,而不必提供用户名和密码。

与 Python 和 urllib2的 Windows 身份验证非常相似的问题。

4

3 回答 3

15

尽管下面的解决方案仅使用 Python Win32 扩展(Python Win32 扩展中包含的 sspi 示例代码非常有用),但问题中提到的 python-ntlm IMAP 和 SMTP 补丁也可以作为有用的指南。

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)
于 2010-05-26T20:16:07.943 回答
6

很好的答案,但作为 python 3 的更新

def asbase64(msg):
    # encoding the message then convert to string
    return base64.b64encode(msg).decode("utf-8")
于 2012-02-29T15:13:16.843 回答
3

由于指定的空白 cmd,Python 2.7.x 将无法发送 NTLM 类型 3 消息:

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

这最终将正确的响应发送回服务器,但是由于 docmd() 调用 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()

结果,它采用 else 条件的路径,从而发送str(' ' + ntlm_message + CRLF)which 结果(501, 'Syntax error in parameters or arguments')

因此,修复只是将 NTLM 消息作为 cmd 发送。

code, response = smtp.docmd(ntlm_message)

已提交对上述答案的修复,但谁知道它何时会被审查/接受。

于 2013-07-08T18:25:45.467 回答