Commit 9a9b95c2 authored by jan.koester's avatar jan.koester
Browse files

netlogon: NETLOGON secure-channel client, verified against Samba AD

samLogonNetwork() runs the full flow against a domain controller and is
confirmed end to end against a real Samba 4.22 AD DC (strict schannel):
DCE/RPC bind, netr_ServerReqChallenge + netr_ServerAuthenticate3 (AES
session key, server credential checked), a netsec schannel alter_context,
then a sealed netr_LogonSamLogonEx with a NetlogonNetworkInformation NTLMv2
logon -> NT_STATUS 0 and the user session key. The NetworkInfo NDR is built
field by field (byte-identical to a captured reference), the netsec AES
sign+seal follows samba's schannel_sign.c, and the request carries the
DCE/RPC SEC_VT verification trailer. Built on libnetplus.

Still to wire: endpoint-mapper lookup of the NETLOGON port for real AD
(the test pins Samba's rpc port), the NTLM command on adconnectd's socket,
and the sharedd side for the kernel's KSMBD_EVENT_NTLM_AUTHEN_REQUEST.

Co-Authored-By: default avatarClaude Opus 4.8 <noreply@anthropic.com>
parent 3a903ac5
Loading
Loading
Loading
Loading
+1 −0
Changes for CMakeLists.txt: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ add_library(adconnect_core STATIC
    src/join.cpp
    src/acceptor.cpp
    src/home.cpp
    src/netlogon.cpp
)
target_link_libraries(adconnect_core PUBLIC adconnect_proto confplus::confplus netplus::netplus Threads::Threads)

+16 −13
Changes for src/netlogon.cpp: 16 added lines, 13 removed lines.
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "netlogon.h"

#include <cstdio>
#include <cstring>
#include <stdexcept>
#include <vector>
@@ -187,22 +188,25 @@ Bytes credential(const Bytes &sk, const Bytes &chal8) {
 * the netr_NetworkInfo pointee is inline after its referent, with its own
 * member string/response pointees deferred to the end of the struct; then
 * validation_level + flags; then the DCE/RPC verification trailer. */
void ndrUniqStr(Bytes &s, uint32_t &ref, const std::string &w) {
    p32(s, ref); ref += 4;
void ndrInlineStr(Bytes &s, const std::string &w) {
    align4(s); /* conformant array: 4-align the max_count; no trailing pad */
    uint32_t n = (uint32_t)w.size() + 1; /* incl NUL */
    p32(s, n); p32(s, 0); p32(s, n);
    s += utf16(w); s += (char)0; s += (char)0;
    align4(s);
}
void ndrUniqStr(Bytes &s, uint32_t &ref, const std::string &w) {
    p32(s, ref); ref += 4;
    ndrInlineStr(s, w);
}
void lsaFixed(Bytes &s, uint32_t &ref, const std::string &w) {
    uint16_t lb = (uint16_t)(w.size() * 2); /* bytes, no NUL */
    p16(s, lb); p16(s, lb); p32(s, ref); ref += 4;
}
void lsaData(Bytes &s, const std::string &w) {
    align4(s);
    uint32_t u = (uint32_t)w.size(); /* units */
    p32(s, u); p32(s, 0); p32(s, u);
    s += utf16(w);
    align4(s);
}
void crFixed(Bytes &s, uint32_t &ref, const Bytes &r) {
    uint16_t l = (uint16_t)r.size();
@@ -211,9 +215,10 @@ void crFixed(Bytes &s, uint32_t &ref, const Bytes &r) {
}
void crData(Bytes &s, const Bytes &r) {
    if (r.empty()) return;
    p32(s, (uint32_t)r.size());
    s += r;
    align4(s);
    uint32_t u = (uint32_t)r.size();
    p32(s, u); p32(s, 0); p32(s, u); /* conformant-varying: max, offset, actual */
    s += r;
}
void secVt(Bytes &s) {
    align4(s);
@@ -346,7 +351,7 @@ struct Rpc {
    /* a schannel-sealed request, returns the decrypted response stub */
    Bytes callSealed(uint16_t opnum, Bytes stub, const Bytes &sk, uint64_t &seq) {
        Bytes data = stub;
        align4(data);
        while (data.size() % 16) data += (char)0; /* 16-align the sec_trailer (auth pad) */
        uint8_t padlen = (uint8_t)(data.size() - stub.size());
        Bytes sig = netsecSealOut(sk, seq++, (uint8_t *)data.data(), data.size());
        Bytes sect;
@@ -395,8 +400,7 @@ NtlmResult samLogonNetwork(const NetlogonConfig &cfg, const NtlmPassthrough &in)
    uint32_t ref = 0x00020000;
    { Bytes dummy; (void)dummy; }
    p32(reqStub, 0);                       /* PrimaryName = NULL */
    { uint32_t n = (uint32_t)cfg.computer.size() + 1; p32(reqStub, n); p32(reqStub, 0); p32(reqStub, n);
      reqStub += utf16(cfg.computer); reqStub += (char)0; reqStub += (char)0; align4(reqStub); }
    ndrInlineStr(reqStub, cfg.computer);   /* computer_name [ref,string] */
    reqStub += cc;
    Bytes o = r.call(4, reqStub);
    Bytes sc = o.substr(0, 8);
@@ -407,12 +411,11 @@ NtlmResult samLogonNetwork(const NetlogonConfig &cfg, const NtlmPassthrough &in)
    /* ServerAuthenticate3 (opnum 26) */
    Bytes a;
    p32(a, 0);                             /* PrimaryName = NULL */
    { uint32_t n = (uint32_t)cfg.machineAccount.size() + 1; p32(a, n); p32(a, 0); p32(a, n);
      a += utf16(cfg.machineAccount); a += (char)0; a += (char)0; align4(a); }
    ndrInlineStr(a, cfg.machineAccount);   /* account_name [ref,string] */
    p16(a, 2);                             /* secure_channel_type = WorkstationSecureChannel (enum16) */
    { uint32_t n = (uint32_t)cfg.computer.size() + 1; p32(a, n); p32(a, 0); p32(a, n);
      a += utf16(cfg.computer); a += (char)0; a += (char)0; align4(a); }
    ndrInlineStr(a, cfg.computer);         /* computer_name [ref,string] */
    a += credential(sk, cc);
    align4(a);
    uint32_t neg = 0x610fffff | 0x01000000 | 0x00004000;
    p32(a, neg);
    Bytes ao = r.call(26, a);