ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
ntlmssp.h
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.h
6 * @brief NTLMSSP message codec (MS-NLMP §2.2.1) for the SMB2 client (PC_ENABLE_SMB).
7 *
8 * The three-message NTLM handshake tokens that carry the NTLMv2 response (ntlm.h) inside SMB2
9 * SESSION_SETUP: the client sends a NEGOTIATE (type 1), the server replies with a CHALLENGE
10 * (type 2) carrying the 8-byte server challenge + the target-info AV_PAIRs, and the client sends
11 * an AUTHENTICATE (type 3) carrying the NtChallengeResponse and the user/domain. All fields
12 * little-endian; text is UTF-16LE. Pure, zero heap. This builds the raw NTLMSSP tokens; the
13 * SPNEGO/GSS wrapping + the SESSION_SETUP framing are the next increment.
14 *
15 * @author Douglas Quigg (dstroy0)
16 * @date 2026
17 */
18
19#ifndef PROTOCORE_NTLMSSP_H
20#define PROTOCORE_NTLMSSP_H
21
22#include "protocore_config.h"
23
24#if PC_ENABLE_SMB
25
26#include <stddef.h>
27#include <stdint.h>
28
29/** @brief NTLMSSP NegotiateFlags (MS-NLMP §2.2.2.5), the subset a basic NTLMv2 client uses.
30 *
31 * A flags word is OR'd/AND'd, so these are integer constants in a namespacing struct, not an enum
32 * class (which would force a cast at every | / &). */
33struct NtlmsspFlags
34{
35 static constexpr uint32_t NTLMSSP_NEGOTIATE_UNICODE = 0x00000001;
36 static constexpr uint32_t NTLMSSP_REQUEST_TARGET = 0x00000004;
37 static constexpr uint32_t NTLMSSP_NEGOTIATE_NTLM = 0x00000200;
38 static constexpr uint32_t NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x00008000;
39 static constexpr uint32_t NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x00080000;
40 static constexpr uint32_t NTLMSSP_NEGOTIATE_TARGET_INFO = 0x00800000;
41 static constexpr uint32_t NTLMSSP_NEGOTIATE_VERSION = 0x02000000;
42 static constexpr uint32_t NTLMSSP_NEGOTIATE_128 = 0x20000000;
43 static constexpr uint32_t NTLMSSP_NEGOTIATE_56 = 0x80000000;
44 // the default NEGOTIATE flag set for an NTLMv2 client
45 static constexpr uint32_t NTLMSSP_CLIENT_DEFAULT_FLAGS = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
46 NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
47 NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY;
48};
49
50/** @brief Parsed CHALLENGE_MESSAGE (type 2). @ref target_info points INTO the source message. */
51struct NtlmChallenge
52{
53 uint32_t flags;
54 uint8_t server_challenge[8];
55 const uint8_t *target_info; ///< the AV_PAIR blob, or nullptr if absent
56 uint16_t target_info_len;
57};
58
59/**
60 * @brief Build a NEGOTIATE_MESSAGE (type 1) with @p flags and empty domain/workstation.
61 * @return message length (32), or 0 if @p cap < 32.
62 */
63size_t pc_ntlmssp_build_negotiate(uint8_t *buf, size_t cap, uint32_t flags);
64
65/**
66 * @brief Parse a CHALLENGE_MESSAGE (type 2): extract the flags, the 8-byte server challenge, and
67 * the target-info AV_PAIRs (bounds-checked into @p msg).
68 * @return true on a valid CHALLENGE; false on a bad signature / type / truncation / out-of-bounds
69 * target info.
70 */
71bool pc_ntlmssp_parse_challenge(const uint8_t *msg, size_t len, NtlmChallenge *out);
72
73/**
74 * @brief Offset of the 16-byte MIC field within an AUTHENTICATE_MESSAGE built @p with_mic (MS-NLMP
75 * §2.2.1.3): the 64-byte fixed header + the 8-byte Version field. The caller writes the computed
76 * MIC here after the message is built and the digest taken over it with these bytes zeroed.
77 */
78#define PC_NTLMSSP_MIC_OFFSET 72
79
80/** @brief Length of the AUTHENTICATE MIC field (an HMAC-MD5 digest). */
81#define PC_NTLMSSP_MIC_LEN 16
82
83/**
84 * @brief Build an AUTHENTICATE_MESSAGE (type 3) carrying the LM + NT responses and the identity.
85 *
86 * @param lm_resp / lm_len the LM(v2) response (may be null/0).
87 * @param nt_resp / nt_len the NtChallengeResponse from pc_ntlm_v2_response.
88 * @param domain / user / workstation ASCII/UTF-8 identity strings (encoded UTF-16LE); user
89 * and domain are typically required, workstation is optional (may be null).
90 * @param flags the NegotiateFlags to echo (usually the server's from the CHALLENGE).
91 * @param with_mic when true, reserve the 8-byte Version + 16-byte MIC fields between the fixed header
92 * and the payload (set NTLMSSP_NEGOTIATE_VERSION, MIC zeroed at ::PC_NTLMSSP_MIC_OFFSET); the
93 * caller then computes the MIC and writes it there. When false the message has no Version/MIC.
94 * @return total message length, or 0 on overflow. No session-key exchange (EncryptedRandomSessionKey empty).
95 */
96size_t pc_ntlmssp_build_authenticate(uint8_t *buf, size_t cap, const uint8_t *lm_resp, size_t lm_len,
97 const uint8_t *nt_resp, size_t nt_len, const char *domain, const char *user,
98 const char *workstation, uint32_t flags, bool with_mic = false);
99
100#endif // PC_ENABLE_SMB
101
102#endif // PROTOCORE_NTLMSSP_H
User-facing configuration for ProtoCore.