Loading src/config.cpp +2 −0 Changes for src/config.cpp: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -187,6 +187,8 @@ void readDomain(const Scope &s, Config &cfg, bool multi, bool &idBaseSet) { if (cfg.servers.empty()) cfg.servers.push_back(cfg.domain); readInt(s, "port", cfg.port, 1, 65535); readInt(s, "netlogon_port", cfg.netlogonPort, 1, 65535); readString(s, "netbios_domain", cfg.netbiosDomain); for (char &c : cfg.netbiosDomain) c = (char)toupper((unsigned char)c); std::string tls = "ldaps"; readString(s, "tls", tls); Loading src/config.h +1 −0 Changes for src/config.h: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ struct Config { int pageSize = 500; int netlogonPort = 0; /* NETLOGON RPC port; 0 = ask the endpoint mapper */ std::string netbiosDomain; /* the short domain name (EXAMPLE); default: from the directory */ int ldapPort() const { return port ? port : tls == ldap::Tls::Ldaps ? 636 : 389; } }; Loading src/daemon.cpp +34 −7 Changes for src/daemon.cpp: 34 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -86,6 +86,8 @@ struct Domain { /* offline: none of its domain controllers is asked before this */ std::atomic<long long> retryAt{0}; std::atomic<bool> offline{false}; /* the short (NetBIOS) domain name the directory gave, "" until asked */ std::string netbios; explicit Domain(const Config &c) : cfg(c), src(c), dir(c, src) {} }; Loading Loading @@ -130,6 +132,36 @@ std::string acceptKrb5(Server &s, const std::string &arg) { } } /* the short domain name (EXAMPLE): netbios_domain, else the nETBIOSName * of the domain's crossRef in the configuration partition -- it need not * be the DNS name's first label (intranet.example.com may be SAMBA) -- * and, while the directory can't be asked, that first label */ std::string netbiosName(Domain &d) { if (!d.cfg.netbiosDomain.empty()) return d.cfg.netbiosDomain; std::lock_guard<std::mutex> l(d.lock); if (d.netbios.empty()) { try { std::string config; d.src.search("", ldap::Base, "(objectClass=*)", {"configurationNamingContext"}, [&](const ldap::Entry &e) { if (const std::string *c = e.first("configurationnamingcontext")) config = *c; }, 0); if (config.empty()) config = "CN=Configuration," + d.cfg.base; d.src.search("CN=Partitions," + config, ldap::OneLevel, "(&(objectClass=crossRef)(nCName=" + d.cfg.base + "))", {"nETBIOSName"}, [&](const ldap::Entry &e) { if (const std::string *n = e.first("netbiosname")) d.netbios = *n; }, 0); for (char &c : d.netbios) c = (char)toupper((unsigned char)c); } catch (const std::exception &e) { std::cerr << "adconnectd: " << d.cfg.domain << ": NetBIOS name: " << e.what() << std::endl; } } if (!d.netbios.empty()) return d.netbios; std::string nb = d.cfg.domain.substr(0, d.cfg.domain.find('.')); for (char &c : nb) c = (char)toupper((unsigned char)c); return nb; } /* NTLM: a domain user's NTLM response, checked by its domain controller over * NETLOGON (for sharedd/ksmbd SMB logins that don't use Kerberos). The request * arg is tab-separated: domain, account, workstation, base64(server challenge), Loading @@ -154,11 +186,6 @@ std::string acceptNtlm(Server &s, const std::string &arg) { in.serverChallenge = f[3]; in.ntResponse = f[4]; in.lmResponse = f[5]; if (in.serverChallenge.size() != 8) return formatError("bad NTLM request"); auto netbios = [](const std::string &dom) { std::string nb = dom.substr(0, dom.find('.')); for (char &c : nb) c = (char)toupper((unsigned char)c); return nb; }; auto upper = [](std::string v) { for (char &c : v) c = (char)toupper((unsigned char)c); return v; Loading @@ -172,7 +199,7 @@ std::string acceptNtlm(Server &s, const std::string &arg) { if (want.empty() && at != std::string::npos) want = upper(in.account.substr(at + 1)); Domain *d = nullptr; for (auto &dp : s.domains) if (netbios(dp->cfg.domain) == want || upper(dp->cfg.domain) == want || upper(dp->cfg.krbRealm) == want) { if (netbiosName(*dp) == want || upper(dp->cfg.domain) == want || upper(dp->cfg.krbRealm) == want) { d = dp.get(); break; } Loading @@ -198,7 +225,7 @@ std::string acceptNtlm(Server &s, const std::string &arg) { nc.computer = d->cfg.krbPrincipal; if (!nc.computer.empty() && nc.computer.back() == '$') nc.computer.pop_back(); nc.machinePassword = d->cfg.bindPassword; nc.domainNetbios = netbios(d->cfg.domain); nc.domainNetbios = netbiosName(*d); try { NtlmResult r = samLogonNetwork(nc, in); return "N\t" + std::to_string((unsigned long)r.status) + "\t" + base64Encode(r.sessionKey) + "\n" + End; Loading src/netlogon.cpp +15 −3 Changes for src/netlogon.cpp: 15 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -194,7 +194,9 @@ void ndrInlineStr(Bytes &s, const std::string &w) { p32(s, n); p32(s, 0); p32(s, n); s += utf16(w); s += (char)0; s += (char)0; } void align2(Bytes &b) { if (b.size() % 2) b += (char)0; } void ndrUniqStr(Bytes &s, uint32_t &ref, const std::string &w) { align4(s); /* the referent: after an odd-length string only 2-aligned */ p32(s, ref); ref += 4; ndrInlineStr(s, w); } Loading Loading @@ -243,6 +245,7 @@ Bytes buildNetworkStub(const std::string &server, const std::string &computer, c ndrUniqStr(s, ref, computer); p16(s, 2); /* logon_level = NetlogonNetworkInformation */ p16(s, 2); /* union discriminant */ align4(s); /* the union arm, a pointer */ p32(s, ref); ref += 4; /* netr_NetworkInfo referent (pointee inline below) */ /* netr_NetworkInfo fixed part */ lsaFixed(s, ref, domain); Loading @@ -260,13 +263,22 @@ Bytes buildNetworkStub(const std::string &server, const std::string &computer, c crData(s, nt); crData(s, lm); /* remaining top-level [in] params */ align2(s); p16(s, 6); /* validation_level = NetlogonValidationSamInfo4 */ p16(s, 0); /* align to 4 */ align4(s); p32(s, 0); /* flags */ secVt(s); return s; } /* a fault PDU's status (after alloc_hint, context id, cancel count) */ std::string faultStatus(const Bytes &r) { if ((uint8_t)r[2] != 3 || r.size() < 28) return std::string(); char b[32]; snprintf(b, sizeof(b), " (status 0x%08x)", (unsigned)g32((const uint8_t *)r.data() + 24)); return b; } /* ---- DCE/RPC over a libnetplus TCP connection ---- */ struct Rpc { std::unique_ptr<netplus::tcp> sock; Loading Loading @@ -347,7 +359,7 @@ struct Rpc { req += stub; sendAll(req); Bytes r = recvPdu(); if ((uint8_t)r[2] != 2) throw std::runtime_error("netlogon: request faulted"); if ((uint8_t)r[2] != 2) throw std::runtime_error("netlogon: request faulted" + faultStatus(r)); return r.substr(24); } /* a schannel-sealed request, returns the decrypted response stub */ Loading @@ -365,7 +377,7 @@ struct Rpc { req += data; req += sect; req += sig; sendAll(req); Bytes r = recvPdu(); if ((uint8_t)r[2] != 2) throw std::runtime_error("netlogon: sealed request faulted"); if ((uint8_t)r[2] != 2) throw std::runtime_error("netlogon: sealed request faulted" + faultStatus(r)); uint16_t al = g16((const uint8_t *)r.data() + 10); size_t frag = r.size(); if (al == 0 || frag < 24 + 8 + al) throw std::runtime_error("netlogon: bad sealed response"); Loading Loading
src/config.cpp +2 −0 Changes for src/config.cpp: 2 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -187,6 +187,8 @@ void readDomain(const Scope &s, Config &cfg, bool multi, bool &idBaseSet) { if (cfg.servers.empty()) cfg.servers.push_back(cfg.domain); readInt(s, "port", cfg.port, 1, 65535); readInt(s, "netlogon_port", cfg.netlogonPort, 1, 65535); readString(s, "netbios_domain", cfg.netbiosDomain); for (char &c : cfg.netbiosDomain) c = (char)toupper((unsigned char)c); std::string tls = "ldaps"; readString(s, "tls", tls); Loading
src/config.h +1 −0 Changes for src/config.h: 1 added line, 0 removed lines. Original line number Diff line number Diff line Loading @@ -93,6 +93,7 @@ struct Config { int pageSize = 500; int netlogonPort = 0; /* NETLOGON RPC port; 0 = ask the endpoint mapper */ std::string netbiosDomain; /* the short domain name (EXAMPLE); default: from the directory */ int ldapPort() const { return port ? port : tls == ldap::Tls::Ldaps ? 636 : 389; } }; Loading
src/daemon.cpp +34 −7 Changes for src/daemon.cpp: 34 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -86,6 +86,8 @@ struct Domain { /* offline: none of its domain controllers is asked before this */ std::atomic<long long> retryAt{0}; std::atomic<bool> offline{false}; /* the short (NetBIOS) domain name the directory gave, "" until asked */ std::string netbios; explicit Domain(const Config &c) : cfg(c), src(c), dir(c, src) {} }; Loading Loading @@ -130,6 +132,36 @@ std::string acceptKrb5(Server &s, const std::string &arg) { } } /* the short domain name (EXAMPLE): netbios_domain, else the nETBIOSName * of the domain's crossRef in the configuration partition -- it need not * be the DNS name's first label (intranet.example.com may be SAMBA) -- * and, while the directory can't be asked, that first label */ std::string netbiosName(Domain &d) { if (!d.cfg.netbiosDomain.empty()) return d.cfg.netbiosDomain; std::lock_guard<std::mutex> l(d.lock); if (d.netbios.empty()) { try { std::string config; d.src.search("", ldap::Base, "(objectClass=*)", {"configurationNamingContext"}, [&](const ldap::Entry &e) { if (const std::string *c = e.first("configurationnamingcontext")) config = *c; }, 0); if (config.empty()) config = "CN=Configuration," + d.cfg.base; d.src.search("CN=Partitions," + config, ldap::OneLevel, "(&(objectClass=crossRef)(nCName=" + d.cfg.base + "))", {"nETBIOSName"}, [&](const ldap::Entry &e) { if (const std::string *n = e.first("netbiosname")) d.netbios = *n; }, 0); for (char &c : d.netbios) c = (char)toupper((unsigned char)c); } catch (const std::exception &e) { std::cerr << "adconnectd: " << d.cfg.domain << ": NetBIOS name: " << e.what() << std::endl; } } if (!d.netbios.empty()) return d.netbios; std::string nb = d.cfg.domain.substr(0, d.cfg.domain.find('.')); for (char &c : nb) c = (char)toupper((unsigned char)c); return nb; } /* NTLM: a domain user's NTLM response, checked by its domain controller over * NETLOGON (for sharedd/ksmbd SMB logins that don't use Kerberos). The request * arg is tab-separated: domain, account, workstation, base64(server challenge), Loading @@ -154,11 +186,6 @@ std::string acceptNtlm(Server &s, const std::string &arg) { in.serverChallenge = f[3]; in.ntResponse = f[4]; in.lmResponse = f[5]; if (in.serverChallenge.size() != 8) return formatError("bad NTLM request"); auto netbios = [](const std::string &dom) { std::string nb = dom.substr(0, dom.find('.')); for (char &c : nb) c = (char)toupper((unsigned char)c); return nb; }; auto upper = [](std::string v) { for (char &c : v) c = (char)toupper((unsigned char)c); return v; Loading @@ -172,7 +199,7 @@ std::string acceptNtlm(Server &s, const std::string &arg) { if (want.empty() && at != std::string::npos) want = upper(in.account.substr(at + 1)); Domain *d = nullptr; for (auto &dp : s.domains) if (netbios(dp->cfg.domain) == want || upper(dp->cfg.domain) == want || upper(dp->cfg.krbRealm) == want) { if (netbiosName(*dp) == want || upper(dp->cfg.domain) == want || upper(dp->cfg.krbRealm) == want) { d = dp.get(); break; } Loading @@ -198,7 +225,7 @@ std::string acceptNtlm(Server &s, const std::string &arg) { nc.computer = d->cfg.krbPrincipal; if (!nc.computer.empty() && nc.computer.back() == '$') nc.computer.pop_back(); nc.machinePassword = d->cfg.bindPassword; nc.domainNetbios = netbios(d->cfg.domain); nc.domainNetbios = netbiosName(*d); try { NtlmResult r = samLogonNetwork(nc, in); return "N\t" + std::to_string((unsigned long)r.status) + "\t" + base64Encode(r.sessionKey) + "\n" + End; Loading
src/netlogon.cpp +15 −3 Changes for src/netlogon.cpp: 15 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -194,7 +194,9 @@ void ndrInlineStr(Bytes &s, const std::string &w) { p32(s, n); p32(s, 0); p32(s, n); s += utf16(w); s += (char)0; s += (char)0; } void align2(Bytes &b) { if (b.size() % 2) b += (char)0; } void ndrUniqStr(Bytes &s, uint32_t &ref, const std::string &w) { align4(s); /* the referent: after an odd-length string only 2-aligned */ p32(s, ref); ref += 4; ndrInlineStr(s, w); } Loading Loading @@ -243,6 +245,7 @@ Bytes buildNetworkStub(const std::string &server, const std::string &computer, c ndrUniqStr(s, ref, computer); p16(s, 2); /* logon_level = NetlogonNetworkInformation */ p16(s, 2); /* union discriminant */ align4(s); /* the union arm, a pointer */ p32(s, ref); ref += 4; /* netr_NetworkInfo referent (pointee inline below) */ /* netr_NetworkInfo fixed part */ lsaFixed(s, ref, domain); Loading @@ -260,13 +263,22 @@ Bytes buildNetworkStub(const std::string &server, const std::string &computer, c crData(s, nt); crData(s, lm); /* remaining top-level [in] params */ align2(s); p16(s, 6); /* validation_level = NetlogonValidationSamInfo4 */ p16(s, 0); /* align to 4 */ align4(s); p32(s, 0); /* flags */ secVt(s); return s; } /* a fault PDU's status (after alloc_hint, context id, cancel count) */ std::string faultStatus(const Bytes &r) { if ((uint8_t)r[2] != 3 || r.size() < 28) return std::string(); char b[32]; snprintf(b, sizeof(b), " (status 0x%08x)", (unsigned)g32((const uint8_t *)r.data() + 24)); return b; } /* ---- DCE/RPC over a libnetplus TCP connection ---- */ struct Rpc { std::unique_ptr<netplus::tcp> sock; Loading Loading @@ -347,7 +359,7 @@ struct Rpc { req += stub; sendAll(req); Bytes r = recvPdu(); if ((uint8_t)r[2] != 2) throw std::runtime_error("netlogon: request faulted"); if ((uint8_t)r[2] != 2) throw std::runtime_error("netlogon: request faulted" + faultStatus(r)); return r.substr(24); } /* a schannel-sealed request, returns the decrypted response stub */ Loading @@ -365,7 +377,7 @@ struct Rpc { req += data; req += sect; req += sig; sendAll(req); Bytes r = recvPdu(); if ((uint8_t)r[2] != 2) throw std::runtime_error("netlogon: sealed request faulted"); if ((uint8_t)r[2] != 2) throw std::runtime_error("netlogon: sealed request faulted" + faultStatus(r)); uint16_t al = g16((const uint8_t *)r.data() + 10); size_t frag = r.size(); if (al == 0 || frag < 24 + 8 + al) throw std::runtime_error("netlogon: bad sealed response"); Loading