Loading src/daemon.cpp +26 −0 Changes for src/daemon.cpp: 26 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -431,6 +431,32 @@ int main(int argc, char **argv) { sigaddset(&signals, SIGINT); pthread_sigmask(SIG_BLOCK, &signals, nullptr); std::thread(saver, std::ref(s), signals).detach(); /* joined domains: SPNs older joins lack (cifs/ -- without it Windows * gets no ticket for SMB and falls back to NTLM), until a DC answers */ for (auto &dp : s.domains) { Domain *d = dp.get(); const std::string &acct = d->cfg.krbPrincipal; if (d->cfg.auth != Auth::Kerberos || acct.empty() || acct.back() != '$') continue; std::thread([d, acct]() { for (int attempt = 0; attempt < 48; attempt++) { try { std::vector<std::string> added; { std::lock_guard<std::mutex> l(d->lock); added = addMissingSpns(d->src, d->cfg, acct); } for (const std::string &spn : added) std::cerr << "adconnectd: " << d->cfg.domain << ": SPN " << spn << " added to " << acct << std::endl; return; } catch (const std::exception &e) { if (attempt == 0) std::cerr << "adconnectd: " << d->cfg.domain << ": checking the SPNs of " << acct << ": " << e.what() << std::endl; } std::this_thread::sleep_for(std::chrono::minutes(5)); } }).detach(); } std::unique_ptr<netplus::tcp> srv; try { unlink(cfg.socket.c_str()); Loading src/join.cpp +30 −1 Changes for src/join.cpp: 30 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -127,6 +127,12 @@ void writeSecret(const std::string &path, const std::string &secret) { } // namespace /* the SPNs of a computer account of ours: HOST/ for logins, cifs/ for SMB * (Windows asks for cifs/<server>; Samba's KDC doesn't map that to HOST/) */ static std::vector<std::string> computerSpns(const std::string &name, const std::string &dnsName) { return { "HOST/" + name, "HOST/" + dnsName, "cifs/" + name, "cifs/" + dnsName }; } std::string computerName(const std::string &hostname) { std::string out; for (char c : hostname.substr(0, hostname.find('.'))) { Loading Loading @@ -163,7 +169,7 @@ JoinResult joinWith(const Config &admin, const std::string &hostname, const std: { "sAMAccountName", { sam } }, { "userAccountControl", { WorkstationTrust } }, { "dNSHostName", { dnsName } }, { "servicePrincipalName", { "HOST/" + name, "HOST/" + dnsName } }, { "servicePrincipalName", computerSpns(name, dnsName) }, { "msDS-SupportedEncryptionTypes", { AesOnly } }, { "unicodePwd", { unicodePwd(password) } }, }); Loading @@ -174,6 +180,7 @@ JoinResult joinWith(const Config &admin, const std::string &hostname, const std: { ldap::Connection::ModReplace, { "userAccountControl", { WorkstationTrust } } }, { ldap::Connection::ModReplace, { "msDS-SupportedEncryptionTypes", { AesOnly } } }, { ldap::Connection::ModReplace, { "dNSHostName", { dnsName } } }, { ldap::Connection::ModReplace, { "servicePrincipalName", computerSpns(name, dnsName) } }, }); } } catch (const ldap::Error &e) { Loading Loading @@ -211,6 +218,28 @@ JoinResult joinWith(const Config &admin, const std::string &hostname, const std: return r; } std::vector<std::string> addMissingSpns(LdapSource &src, const Config &c, const std::string &account) { std::string dn, dnsName; std::vector<std::string> have; src.search(c.base, ldap::Subtree, "(&(objectClass=computer)(sAMAccountName=" + ldap::escape(account) + "))", { "servicePrincipalName", "dNSHostName" }, [&](const ldap::Entry &e) { dn = e.dn; if (const std::string *d = e.first("dnshostname")) dnsName = *d; if (const auto *v = e.all("serviceprincipalname")) have = *v; }, 0); if (dn.empty()) throw std::runtime_error("no computer account " + account); std::string name = account.substr(0, account.size() - (!account.empty() && account.back() == '$')); if (dnsName.empty()) dnsName = lower(name) + "." + c.domain; std::vector<std::string> missing; for (const std::string &spn : computerSpns(name, dnsName)) { bool found = false; for (const std::string &h : have) found = found || lower(h) == lower(spn); if (!found) missing.push_back(spn); } if (!missing.empty()) src.modify(dn, { { ldap::Connection::ModAdd, { "servicePrincipalName", missing } } }); return missing; } bool leaveWith(const Config &admin, const std::string &account) { LdapSource src(admin); src.check(); Loading src/join.h +6 −0 Changes for src/join.h: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -64,6 +64,12 @@ bool leaveDomain(const Config &domain, const std::string &account, const std::st /* the deletion on a connection admin describes (tests) */ bool leaveWith(const Config &admin, const std::string &account); class LdapSource; /* adds the SPNs a computer account of ours (account: HOST$) lacks -- cifs/ * for SMB, which older joins didn't register; the account may do that * itself. The ones added; throws std::runtime_error */ std::vector<std::string> addMissingSpns(LdapSource &src, const Config &c, const std::string &account); /* this system's name as a computer account: short, upper case, at most 15 */ std::string computerName(const std::string &hostname); Loading Loading
src/daemon.cpp +26 −0 Changes for src/daemon.cpp: 26 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -431,6 +431,32 @@ int main(int argc, char **argv) { sigaddset(&signals, SIGINT); pthread_sigmask(SIG_BLOCK, &signals, nullptr); std::thread(saver, std::ref(s), signals).detach(); /* joined domains: SPNs older joins lack (cifs/ -- without it Windows * gets no ticket for SMB and falls back to NTLM), until a DC answers */ for (auto &dp : s.domains) { Domain *d = dp.get(); const std::string &acct = d->cfg.krbPrincipal; if (d->cfg.auth != Auth::Kerberos || acct.empty() || acct.back() != '$') continue; std::thread([d, acct]() { for (int attempt = 0; attempt < 48; attempt++) { try { std::vector<std::string> added; { std::lock_guard<std::mutex> l(d->lock); added = addMissingSpns(d->src, d->cfg, acct); } for (const std::string &spn : added) std::cerr << "adconnectd: " << d->cfg.domain << ": SPN " << spn << " added to " << acct << std::endl; return; } catch (const std::exception &e) { if (attempt == 0) std::cerr << "adconnectd: " << d->cfg.domain << ": checking the SPNs of " << acct << ": " << e.what() << std::endl; } std::this_thread::sleep_for(std::chrono::minutes(5)); } }).detach(); } std::unique_ptr<netplus::tcp> srv; try { unlink(cfg.socket.c_str()); Loading
src/join.cpp +30 −1 Changes for src/join.cpp: 30 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -127,6 +127,12 @@ void writeSecret(const std::string &path, const std::string &secret) { } // namespace /* the SPNs of a computer account of ours: HOST/ for logins, cifs/ for SMB * (Windows asks for cifs/<server>; Samba's KDC doesn't map that to HOST/) */ static std::vector<std::string> computerSpns(const std::string &name, const std::string &dnsName) { return { "HOST/" + name, "HOST/" + dnsName, "cifs/" + name, "cifs/" + dnsName }; } std::string computerName(const std::string &hostname) { std::string out; for (char c : hostname.substr(0, hostname.find('.'))) { Loading Loading @@ -163,7 +169,7 @@ JoinResult joinWith(const Config &admin, const std::string &hostname, const std: { "sAMAccountName", { sam } }, { "userAccountControl", { WorkstationTrust } }, { "dNSHostName", { dnsName } }, { "servicePrincipalName", { "HOST/" + name, "HOST/" + dnsName } }, { "servicePrincipalName", computerSpns(name, dnsName) }, { "msDS-SupportedEncryptionTypes", { AesOnly } }, { "unicodePwd", { unicodePwd(password) } }, }); Loading @@ -174,6 +180,7 @@ JoinResult joinWith(const Config &admin, const std::string &hostname, const std: { ldap::Connection::ModReplace, { "userAccountControl", { WorkstationTrust } } }, { ldap::Connection::ModReplace, { "msDS-SupportedEncryptionTypes", { AesOnly } } }, { ldap::Connection::ModReplace, { "dNSHostName", { dnsName } } }, { ldap::Connection::ModReplace, { "servicePrincipalName", computerSpns(name, dnsName) } }, }); } } catch (const ldap::Error &e) { Loading Loading @@ -211,6 +218,28 @@ JoinResult joinWith(const Config &admin, const std::string &hostname, const std: return r; } std::vector<std::string> addMissingSpns(LdapSource &src, const Config &c, const std::string &account) { std::string dn, dnsName; std::vector<std::string> have; src.search(c.base, ldap::Subtree, "(&(objectClass=computer)(sAMAccountName=" + ldap::escape(account) + "))", { "servicePrincipalName", "dNSHostName" }, [&](const ldap::Entry &e) { dn = e.dn; if (const std::string *d = e.first("dnshostname")) dnsName = *d; if (const auto *v = e.all("serviceprincipalname")) have = *v; }, 0); if (dn.empty()) throw std::runtime_error("no computer account " + account); std::string name = account.substr(0, account.size() - (!account.empty() && account.back() == '$')); if (dnsName.empty()) dnsName = lower(name) + "." + c.domain; std::vector<std::string> missing; for (const std::string &spn : computerSpns(name, dnsName)) { bool found = false; for (const std::string &h : have) found = found || lower(h) == lower(spn); if (!found) missing.push_back(spn); } if (!missing.empty()) src.modify(dn, { { ldap::Connection::ModAdd, { "servicePrincipalName", missing } } }); return missing; } bool leaveWith(const Config &admin, const std::string &account) { LdapSource src(admin); src.check(); Loading
src/join.h +6 −0 Changes for src/join.h: 6 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -64,6 +64,12 @@ bool leaveDomain(const Config &domain, const std::string &account, const std::st /* the deletion on a connection admin describes (tests) */ bool leaveWith(const Config &admin, const std::string &account); class LdapSource; /* adds the SPNs a computer account of ours (account: HOST$) lacks -- cifs/ * for SMB, which older joins didn't register; the account may do that * itself. The ones added; throws std::runtime_error */ std::vector<std::string> addMissingSpns(LdapSource &src, const Config &c, const std::string &account); /* this system's name as a computer account: short, upper case, at most 15 */ std::string computerName(const std::string &hostname); Loading