@@ -94,8 +94,13 @@ public function encode($nt_domain, $client_hostname, $negotiate_flags = null)
9494 // Get our default negotiate flags if none were supplied
9595 $ negotiate_flags = (null === $ negotiate_flags ) ? static ::getDefaultNegotiateFlags () : $ negotiate_flags ;
9696
97+ $ nt_domain_supplied = false ;
98+ $ client_hostname_supplied = false ;
99+
97100 if ((NegotiateFlag::NEGOTIATE_OEM_DOMAIN_SUPPLIED & $ negotiate_flags )
98101 === NegotiateFlag::NEGOTIATE_OEM_DOMAIN_SUPPLIED ) {
102+ $ nt_domain_supplied = true ;
103+
99104 $ nt_domain = $ this ->encoding_converter ->convert (
100105 strtoupper ($ nt_domain ),
101106 static ::OEM_ENCODING
@@ -107,6 +112,8 @@ public function encode($nt_domain, $client_hostname, $negotiate_flags = null)
107112
108113 if ((NegotiateFlag::NEGOTIATE_OEM_WORKSTATION_SUPPLIED & $ negotiate_flags )
109114 === NegotiateFlag::NEGOTIATE_OEM_WORKSTATION_SUPPLIED ) {
115+ $ client_hostname_supplied = true ;
116+
110117 $ client_hostname = $ this ->encoding_converter ->convert (
111118 strtoupper ($ client_hostname ),
112119 static ::OEM_ENCODING
@@ -121,6 +128,24 @@ public function encode($nt_domain, $client_hostname, $negotiate_flags = null)
121128 $ domain_name_length = strlen ($ nt_domain );
122129 $ hostname_length = strlen ($ client_hostname );
123130
131+ /**
132+ * Determine the payload offsets of the domain name and hostname
133+ *
134+ * The specification says that these offsets should be set to valid
135+ * locations even if the negotation flags don't contain the flags
136+ * denoting their inclusion, however some NTLM servers seem to throw a
137+ * bit of a fit if the offsets are set to non-zero values when the flags
138+ * don't denote their inclusion.
139+ *
140+ * So yea, we're breaking spec here to appease some seemingly old or
141+ * improper implementations. cURL does the same here.
142+ *
143+ * @link https://msdn.microsoft.com/en-us/library/cc236641.aspx
144+ * @link https://github.com/bagder/curl/blob/curl-7_46_0/lib/curl_ntlm_msgs.c#L364-L370
145+ */
146+ $ domain_name_offset = $ nt_domain_supplied ? $ payload_offset : 0 ;
147+ $ hostname_offset = $ client_hostname_supplied ? ($ payload_offset + $ domain_name_length ) : 0 ;
148+
124149 // Prepare a binary string to be returned
125150 $ binary_string = '' ;
126151
@@ -132,12 +157,12 @@ public function encode($nt_domain, $client_hostname, $negotiate_flags = null)
132157 // Domain name fields: length; length; offset of the domain value from the beginning of the message
133158 $ binary_string .= pack ('v ' , $ domain_name_length ); // 16-bit unsigned little-endian
134159 $ binary_string .= pack ('v ' , $ domain_name_length ); // 16-bit unsigned little-endian
135- $ binary_string .= pack ('V ' , $ payload_offset ); // 32-bit unsigned little-endian, 1st value in the payload
160+ $ binary_string .= pack ('V ' , $ domain_name_offset ); // 32-bit unsigned little-endian, 1st value in the payload
136161
137162 // Hostname fields: length; length; offset of the hostname value from the beginning of the message
138163 $ binary_string .= pack ('v ' , $ hostname_length ); // 16-bit unsigned little-endian
139164 $ binary_string .= pack ('v ' , $ hostname_length ); // 16-bit unsigned little-endian
140- $ binary_string .= pack ('V ' , $ payload_offset + $ domain_name_length ); // 32-bit unsigned little-endian, 2nd value
165+ $ binary_string .= pack ('V ' , $ hostname_offset ); // 32-bit unsigned little-endian, 2nd value
141166
142167 // NOTE: Omitting the version data here. It's unnecessary.
143168
0 commit comments