Skip to content

Commit fde4878

Browse files
committed
Always include a version field in NTLMSSP packets
It turns out that the version field structure must always be included in packets, the flag only controls whether the structure is filled or all zeros. Add it directly to the wire structures, and change the encoding functions accordingly. Signed-off-by: Simo Sorce <[email protected]>
1 parent 8911176 commit fde4878

File tree

3 files changed

+86
-40
lines changed

3 files changed

+86
-40
lines changed

src/ntlm.c

+59-24
Original file line numberDiff line numberDiff line change
@@ -349,15 +349,14 @@ void ntlm_internal_set_version(uint8_t major, uint8_t minor,
349349

350350
static int ntlm_encode_version(struct ntlm_ctx *ctx,
351351
struct ntlm_buffer *buffer,
352-
size_t *data_offs)
352+
size_t data_offs)
353353
{
354-
if (*data_offs + sizeof(struct wire_version) > buffer->length) {
354+
if (data_offs + sizeof(struct wire_version) > buffer->length) {
355355
return ERR_ENCODE;
356356
}
357357

358-
memcpy(&buffer->data[*data_offs], &ntlmssp_version,
358+
memcpy(&buffer->data[data_offs], &ntlmssp_version,
359359
sizeof(struct wire_version));
360-
*data_offs += sizeof(struct wire_version);
361360
return 0;
362361
}
363362

@@ -932,6 +931,12 @@ int ntlm_encode_neg_msg(struct ntlm_ctx *ctx, uint32_t flags,
932931
if (ret) goto done;
933932
}
934933

934+
if (flags & NTLMSSP_NEGOTIATE_VERSION) {
935+
ret = ntlm_encode_version(ctx, &buffer,
936+
(char *)&msg->version - (char *)msg);
937+
if (ret) goto done;
938+
}
939+
935940
done:
936941
if (ret) {
937942
safefree(buffer.data);
@@ -959,6 +964,14 @@ int ntlm_decode_neg_msg(struct ntlm_ctx *ctx,
959964

960965
neg_flags = le32toh(msg->neg_flags);
961966

967+
if ((neg_flags & NTLMSSP_NEGOTIATE_VERSION) == 0) {
968+
/* adjust the payload offset to point to the
969+
* version field, for compatibility with older
970+
* clients that completely omitted the structure
971+
* on the wire */
972+
payload_offs -= sizeof(struct wire_version);
973+
}
974+
962975
if (domain &&
963976
(neg_flags & NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED)) {
964977
ret = ntlm_decode_oem_str(&msg->domain_name, buffer,
@@ -1003,10 +1016,6 @@ int ntlm_encode_chal_msg(struct ntlm_ctx *ctx,
10031016

10041017
buffer.length = sizeof(struct wire_chal_msg);
10051018

1006-
if (flags & NTLMSSP_NEGOTIATE_VERSION) {
1007-
buffer.length += sizeof(struct wire_version);
1008-
}
1009-
10101019
if ((flags & NTLMSSP_TARGET_TYPE_SERVER)
10111020
|| (flags & NTLMSSP_TARGET_TYPE_DOMAIN)) {
10121021
if (!target_name) return EINVAL;
@@ -1035,7 +1044,8 @@ int ntlm_encode_chal_msg(struct ntlm_ctx *ctx,
10351044

10361045
/* this must be first as it pushes the payload further down */
10371046
if (flags & NTLMSSP_NEGOTIATE_VERSION) {
1038-
ret = ntlm_encode_version(ctx, &buffer, &data_offs);
1047+
ret = ntlm_encode_version(ctx, &buffer,
1048+
(char *)&msg->version - (char *)msg);
10391049
if (ret) goto done;
10401050
}
10411051

@@ -1077,6 +1087,7 @@ int ntlm_decode_chal_msg(struct ntlm_ctx *ctx,
10771087
{
10781088
struct wire_chal_msg *msg;
10791089
size_t payload_offs;
1090+
size_t base_chal_size;
10801091
uint32_t flags;
10811092
char *trg = NULL;
10821093
int ret = 0;
@@ -1089,6 +1100,16 @@ int ntlm_decode_chal_msg(struct ntlm_ctx *ctx,
10891100
payload_offs = (char *)msg->payload - (char *)msg;
10901101

10911102
flags = le32toh(msg->neg_flags);
1103+
base_chal_size = sizeof(struct wire_chal_msg);
1104+
1105+
if ((flags & NTLMSSP_NEGOTIATE_VERSION) == 0) {
1106+
/* adjust the payload offset to point to the
1107+
* version field, for compatibility with older
1108+
* clients that completely omitted the structure
1109+
* on the wire */
1110+
payload_offs -= sizeof(struct wire_version);
1111+
base_chal_size -= sizeof(struct wire_version);
1112+
}
10921113

10931114
if ((flags & NTLMSSP_TARGET_TYPE_SERVER)
10941115
|| (flags & NTLMSSP_TARGET_TYPE_DOMAIN)) {
@@ -1107,7 +1128,7 @@ int ntlm_decode_chal_msg(struct ntlm_ctx *ctx,
11071128

11081129
/* if we allowed a broken short challenge message from an old
11091130
* server we must stop here */
1110-
if (buffer->length < sizeof(struct wire_chal_msg)) {
1131+
if (buffer->length < base_chal_size) {
11111132
if (flags & NTLMSSP_NEGOTIATE_TARGET_INFO) {
11121133
ret = ERR_DECODE;
11131134
}
@@ -1190,9 +1211,6 @@ int ntlm_encode_auth_msg(struct ntlm_ctx *ctx,
11901211
if (enc_sess_key) {
11911212
buffer.length += enc_sess_key->length;
11921213
}
1193-
if (flags & NTLMSSP_NEGOTIATE_VERSION) {
1194-
buffer.length += sizeof(struct wire_version);
1195-
}
11961214
if (mic) {
11971215
buffer.length += 16;
11981216
}
@@ -1207,11 +1225,12 @@ int ntlm_encode_auth_msg(struct ntlm_ctx *ctx,
12071225

12081226
/* this must be first as it pushes the payload further down */
12091227
if (flags & NTLMSSP_NEGOTIATE_VERSION) {
1210-
ret = ntlm_encode_version(ctx, &buffer, &data_offs);
1228+
ret = ntlm_encode_version(ctx, &buffer,
1229+
(char *)&msg->version - (char *)msg);
12111230
if (ret) goto done;
12121231
}
12131232

1214-
/* this must be second as it pushes the payload further down */
1233+
/* this pushes the payload further down */
12151234
if (mic) {
12161235
memset(&buffer.data[data_offs], 0, mic->length);
12171236
/* return the actual pointer back in the mic, as it will
@@ -1293,6 +1312,7 @@ int ntlm_decode_auth_msg(struct ntlm_ctx *ctx,
12931312
struct ntlm_buffer *mic)
12941313
{
12951314
struct wire_auth_msg *msg;
1315+
uint32_t neg_flags;
12961316
size_t payload_offs;
12971317
char *dom = NULL;
12981318
char *usr = NULL;
@@ -1308,23 +1328,38 @@ int ntlm_decode_auth_msg(struct ntlm_ctx *ctx,
13081328
msg = (struct wire_auth_msg *)buffer->data;
13091329
payload_offs = (char *)msg->payload - (char *)msg;
13101330

1311-
/* this must be first as it pushes the payload further down */
1312-
if (flags & NTLMSSP_NEGOTIATE_VERSION) {
1313-
/* skip version for now */
1314-
payload_offs += sizeof(struct wire_version);
1331+
neg_flags = le32toh(msg->neg_flags);
1332+
if ((neg_flags & NTLMSSP_NEGOTIATE_VERSION) == 0) {
1333+
/* adjust the payload offset to point to the
1334+
* version field, for compatibility with older
1335+
* clients that completely omitted the structure
1336+
* on the wire */
1337+
payload_offs -= sizeof(struct wire_version);
13151338
}
13161339

13171340
/* Unconditionally copy 16 bytes for the MIC, if it was really
13181341
* added by the client it will be flagged in the AV_PAIR contained
13191342
* in the NT Response, that will be fully decoded later by the caller
13201343
* and the MIC checked otherwise these 16 bytes will just be ignored */
13211344
if (mic) {
1345+
size_t mic_offs = payload_offs;
1346+
13221347
if (mic->length < 16) return ERR_DECODE;
1323-
/* mic is at payload_offs right now */
1324-
if (buffer->length - payload_offs < 16) return ERR_DECODE;
1325-
memcpy(mic->data, &buffer->data[payload_offs], 16);
1326-
/* NOTE: we do not push down the payload because we do not know that
1327-
* the MIC is actually present yet for real */
1348+
1349+
if ((neg_flags & NTLMSSP_NEGOTIATE_VERSION) == 0) {
1350+
struct wire_version zver = {0};
1351+
/* mic is at payload_offs right now, but this offset may be
1352+
* wrongly reduced for compatibility with older clients,
1353+
* if all bytes are zeroed, then it means there was an actual
1354+
* empty version struct */
1355+
if (memcmp(&msg->version, &zver,
1356+
sizeof(struct wire_version)) == 0) {
1357+
mic_offs += sizeof(struct wire_version);
1358+
}
1359+
}
1360+
1361+
if (buffer->length - mic_offs < 16) return ERR_DECODE;
1362+
memcpy(mic->data, &buffer->data[mic_offs], 16);
13281363
}
13291364

13301365
if (msg->lm_chalresp.len != 0 && lm_chalresp) {

src/ntlm_common.h

+16-13
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,26 @@ struct wire_field_hdr {
8888
};
8989
#pragma pack(pop)
9090

91+
/* Version information.
92+
* Used only for debugging and usually placed as the head of the payload when
93+
* used */
94+
#pragma pack(push, 1)
95+
struct wire_version {
96+
uint8_t major;
97+
uint8_t minor;
98+
uint16_t build;
99+
uint8_t reserved[3];
100+
uint8_t revision;
101+
};
102+
#pragma pack(pop)
103+
91104
#pragma pack(push, 1)
92105
struct wire_neg_msg {
93106
struct wire_msg_hdr header;
94107
uint32_t neg_flags;
95108
struct wire_field_hdr domain_name;
96109
struct wire_field_hdr workstation_name;
110+
struct wire_version version;
97111
uint8_t payload[]; /* variable */
98112
};
99113
#pragma pack(pop)
@@ -106,6 +120,7 @@ struct wire_chal_msg {
106120
uint8_t server_challenge[8];
107121
uint8_t reserved[8];
108122
struct wire_field_hdr target_info;
123+
struct wire_version version;
109124
uint8_t payload[]; /* variable */
110125
};
111126
#pragma pack(pop)
@@ -131,23 +146,11 @@ struct wire_auth_msg {
131146
struct wire_field_hdr workstation;
132147
struct wire_field_hdr enc_sess_key;
133148
uint32_t neg_flags;
149+
struct wire_version version;
134150
uint8_t payload[]; /* variable */
135151
};
136152
#pragma pack(pop)
137153

138-
/* Version information.
139-
* Used only for debugging and usually placed as the head of the payload when
140-
* used */
141-
#pragma pack(push, 1)
142-
struct wire_version {
143-
uint8_t major;
144-
uint8_t minor;
145-
uint16_t build;
146-
uint8_t reserved[3];
147-
uint8_t revision;
148-
};
149-
#pragma pack(pop)
150-
151154
/* ln/ntlm response, v1 or v2 */
152155
#pragma pack(push, 1)
153156
union wire_ntlm_response {

src/ntlm_crypto.c

+11-3
Original file line numberDiff line numberDiff line change
@@ -972,9 +972,17 @@ int ntlm_verify_mic(struct ntlm_key *key,
972972

973973
/* flags must be checked as they may push the payload further down */
974974
flags = le32toh(msg->neg_flags);
975-
if (flags & NTLMSSP_NEGOTIATE_VERSION) {
976-
/* skip version for now */
977-
payload_offs += sizeof(struct wire_version);
975+
if ((flags & NTLMSSP_NEGOTIATE_VERSION) == 0) {
976+
struct wire_version zver = {0};
977+
/* mic is at payload_offs right now, but this offset may
978+
* need to be reduced if the sender completely omitted
979+
* the version struct, as some older clients do */
980+
if (memcmp(&msg->version, &zver,
981+
sizeof(struct wire_version)) != 0) {
982+
/* version struct is not all zeros, this indicates the actual
983+
* struct was omitted and payload is shifted down */
984+
payload_offs -= sizeof(struct wire_version);
985+
}
978986
}
979987

980988
if (payload_offs + NTLM_SIGNATURE_SIZE > authenticate_message->length) {

0 commit comments

Comments
 (0)