DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
ntlmssp.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file ntlmssp.cpp
6 * @brief NTLMSSP message codec implementation (see ntlmssp.h). Little-endian; text UTF-16LE.
7 */
8
9#include "ntlmssp.h"
10
11#if DETWS_ENABLE_SMB
12
13#include <string.h>
14
15static const uint8_t NTLMSSP_SIG[8] = {'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
16
17static uint16_t rd16(const uint8_t *p)
18{
19 return (uint16_t)(p[0] | (p[1] << 8));
20}
21static uint32_t rd32(const uint8_t *p)
22{
23 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
24}
25static void wr16(uint8_t *p, uint16_t v)
26{
27 p[0] = (uint8_t)v;
28 p[1] = (uint8_t)(v >> 8);
29}
30static void wr32(uint8_t *p, uint32_t v)
31{
32 p[0] = (uint8_t)v;
33 p[1] = (uint8_t)(v >> 8);
34 p[2] = (uint8_t)(v >> 16);
35 p[3] = (uint8_t)(v >> 24);
36}
37
38// Write a Len/MaxLen/BufferOffset field triplet at @p f.
39static void wr_field(uint8_t *f, uint16_t len, uint32_t off)
40{
41 wr16(f + 0, len);
42 wr16(f + 2, len); // MaxLen == Len
43 wr32(f + 4, off);
44}
45
46size_t ntlmssp_build_negotiate(uint8_t *buf, size_t cap, uint32_t flags)
47{
48 if (!buf || cap < 32)
49 return 0;
50 memset(buf, 0, 32);
51 memcpy(buf + 0, NTLMSSP_SIG, 8); // Signature
52 wr32(buf + 8, 1); // MessageType = NEGOTIATE
53 wr32(buf + 12, flags); // NegotiateFlags
54 wr_field(buf + 16, 0, 32); // DomainNameFields (empty; offset = end of header)
55 wr_field(buf + 24, 0, 32); // WorkstationFields (empty)
56 return 32;
57}
58
59bool ntlmssp_parse_challenge(const uint8_t *msg, size_t len, NtlmChallenge *out)
60{
61 if (!msg || !out || len < 48) // through TargetInfoFields
62 return false;
63 if (memcmp(msg, NTLMSSP_SIG, 8) != 0 || rd32(msg + 8) != 2)
64 return false;
65 out->flags = rd32(msg + 20);
66 memcpy(out->server_challenge, msg + 24, 8);
67 uint16_t ti_len = rd16(msg + 40);
68 uint32_t ti_off = rd32(msg + 44);
69 if (ti_len == 0)
70 {
71 out->target_info = nullptr;
72 out->target_info_len = 0;
73 return true;
74 }
75 if ((size_t)ti_off + ti_len > len) // target info out of bounds -> fail closed
76 return false;
77 out->target_info = msg + ti_off;
78 out->target_info_len = ti_len;
79 return true;
80}
81
82// Append the UTF-16LE encoding of @p s to buf[at..]; returns the byte count (2 * strlen).
83static size_t put_utf16le(uint8_t *buf, const char *s)
84{
85 size_t n = 0;
86 if (s)
87 for (const char *p = s; *p; p++)
88 {
89 buf[n++] = (uint8_t)*p;
90 buf[n++] = 0;
91 }
92 return n;
93}
94static size_t utf16_len(const char *s)
95{
96 size_t n = 0;
97 if (s)
98 while (s[n])
99 n++;
100 return n * 2;
101}
102
103size_t ntlmssp_build_authenticate(uint8_t *buf, size_t cap, const uint8_t *lm_resp, size_t lm_len,
104 const uint8_t *nt_resp, size_t nt_len, const char *domain, const char *user,
105 const char *workstation, uint32_t flags)
106{
107 const size_t HDR = 64; // fixed part (no Version, no MIC)
108 size_t dlen = utf16_len(domain);
109 size_t ulen = utf16_len(user);
110 size_t wlen = utf16_len(workstation);
111 size_t total = HDR + lm_len + nt_len + dlen + ulen + wlen; // session key empty
112 if (!buf || total > cap)
113 return 0;
114
115 memset(buf, 0, HDR);
116 memcpy(buf + 0, NTLMSSP_SIG, 8); // Signature
117 wr32(buf + 8, 3); // MessageType = AUTHENTICATE
118
119 // Lay out the payload after the fixed header, then point each field at it.
120 size_t off = HDR;
121 size_t lm_off = off;
122 if (lm_resp && lm_len)
123 memcpy(buf + off, lm_resp, lm_len);
124 off += lm_len;
125 size_t nt_off = off;
126 if (nt_resp && nt_len)
127 memcpy(buf + off, nt_resp, nt_len);
128 off += nt_len;
129 size_t dom_off = off;
130 off += put_utf16le(buf + off, domain);
131 size_t usr_off = off;
132 off += put_utf16le(buf + off, user);
133 size_t wks_off = off;
134 off += put_utf16le(buf + off, workstation);
135 size_t key_off = off; // EncryptedRandomSessionKey empty
136
137 wr_field(buf + 12, (uint16_t)lm_len, (uint32_t)lm_off); // LmChallengeResponseFields
138 wr_field(buf + 20, (uint16_t)nt_len, (uint32_t)nt_off); // NtChallengeResponseFields
139 wr_field(buf + 28, (uint16_t)dlen, (uint32_t)dom_off); // DomainNameFields
140 wr_field(buf + 36, (uint16_t)ulen, (uint32_t)usr_off); // UserNameFields
141 wr_field(buf + 44, (uint16_t)wlen, (uint32_t)wks_off); // WorkstationFields
142 wr_field(buf + 52, 0, (uint32_t)key_off); // EncryptedRandomSessionKeyFields
143 wr32(buf + 60, flags); // NegotiateFlags
144 return total;
145}
146
147#endif // DETWS_ENABLE_SMB
NTLMSSP message codec (MS-NLMP §2.2.1) for the SMB2 client (DETWS_ENABLE_SMB).