![]() |
![]() |
![]() |
Evolution Connector for Microsoft Exchange Programmer’s Reference Manual | ![]() |
---|
NTLM auth takes three steps:
Client → Server: |
Negotiate message |
Server → Client: |
Challenge message |
Client → Server: |
Authenticate message |
The message header is the same in each step. The packet starts with
"NTLMSSP\0"
, followed by the 4-byte message type
(of which only the first byte is significant: 1 for Negotiate, 2 for
Challenge, 3 for Authenticate).
The structure definitions below assume x86 conventions: all fields are
little-endian. Also, the "max_len
" fields
always contain the same values as their
corresponding "len
" fields. The distinction
is presumably historical.
In Samba, this looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
struct { char protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' guint32 type; // 0x00000001 guint32 flags; // 0x0000b203 guint16 dom_len; // NT domain name length guint16 dom_max_len; // NT domain name max length guint32 dom_off; // NT domain name offset guint16 host_len; // local workstation name length guint16 host_max_len; // local workstation name max length guint32 host_off; // local workstation offset char host[]; // local workstation name (ASCII) char domain[]; // NT domain name (ASCII) }; |
1 2 3 4 5 6 7 8 9 |
#define NTLMSSP_NEGOTIATE_UNICODE 0x0001 // Text strings are in unicode #define NTLMSSP_NEGOTIATE_OEM 0x0002 // Text strings are in OEM #define NTLMSSP_REQUEST_TARGET 0x0004 // Server return its auth realm #define NTLMSSP_NEGOTIATE_SIGN 0x0010 // Request signature capability #define NTLMSSP_NEGOTIATE_SEAL 0x0020 // Request confidentiality #define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 // Generate session key #define NTLMSSP_NEGOTIATE_NTLM 0x0200 // NTLM authentication #define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 // client/server on same machine #define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 // Sign for all security levels |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
struct { char protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' guint32 type; // 0x00000002 guint16 dom_len; // NT domain name length guint16 dom_max_len; // NT domain name max length guint32 dom_off; // NT domain name offset (always 0x0030) guint32 flags; char nonce[8]; // nonce char zero[8]; guint16 data_len; // length of data following domain guint16 data_max_len; // length of data following domain guint32 data_off; // offset of data following domain char domain[]; // NT domain name // The following piece occurs multiple times guint16 type; // Type of this data item guint16 length; // Length in bytes of this data item char data[]; // Data ... }; |
1 2 |
#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000 // Server is a DC/AD #define NTLMSSP_TARGET_TYPE_SERVER 0x20000 // Server is just a server |
|
WINS name of server (eg, |
|
NT domain name (eg, |
|
DNS name of server (eg, |
|
Windows 2000 domain name (eg, |
However, they may occur in any order. Note that they're returned in
Unicode (UTF-16LE) even though we said we don't speak Unicode. The
packet is terminated by an item with type and length
0
.
The final message, generated by
xntlm_authenticate()
looks like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
struct { char protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' guint32 type; // 0x00000003 guint16 lm_resp_len; // LanManager response length (always 0x18) guint16 lm_resp_max_len; // LanManager response max length guint32 lm_resp_off; // LanManager response offset guint16 nt_resp_len; // NT response length (always 0x18) guint16 nt_resp_max_len; // NT response max length guint32 nt_resp_off; // NT response offset guint16 dom_len; // NT domain name length guint16 dom_max_len; // NT domain name max length guint32 dom_off; // NT domain name offset (always 0x0040) guint16 user_len; // username length guint16 user_max_len; // username max length guint32 user_off; // username offset guint16 host_len; // local workstation name length guint16 host_max_len; // local workstation name max length guint32 host_off; // local workstation name offset guint16 session_len; // session key length guint16 session_max_len; // session key max length guint32 session_off; // session key offset guint32 flags; // 0x00008201 char domain[]; // NT domain name (UCS-16LE) char user[]; // username (UCS-16LE) char host[]; // local workstation name (UCS-16LE) char lm_resp[]; // LanManager response char nt_resp[]; // NT response }; |
POLL |
207 Multi-Status with a 409 Conflict inside |
BPROPPATCH |
207 Multi-Status with a 401 Unauthorized inside |
PUT to |
404 Not Found |
Meanwhile, Active Directory (and presumably Exchange 5.5 as well) abuses LDAP auth somewhat to support NTLM. RFC 2251 says:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
BindRequest ::= [APPLICATION 0] SEQUENCE { version INTEGER (1 .. 127), name LDAPDN, authentication AuthenticationChoice } AuthenticationChoice ::= CHOICE { simple [0] OCTET STRING, -- 1 and 2 reserved sasl [3] SaslCredentials } BindResponse ::= [APPLICATION 1] SEQUENCE { COMPONENTS OF LDAPResult, serverSaslCreds [7] OCTET STRING OPTIONAL } LDAPResult ::= SEQUENCE { resultCode ENUMERATED { success (0), ... other (80) }, matchedDN LDAPDN, errorMessage LDAPString, referral [3] Referral OPTIONAL } |