Loading plugin/ad/ad.cpp +67 −4 Changes for plugin/ad/ad.cpp: 67 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -30,6 +30,9 @@ #include <cstring> #include <map> #include <algorithm> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sstream> #include <memory> #include <functional> Loading Loading @@ -360,7 +363,9 @@ ActiveDirectory::ActiveDirectory(AuthBackend &adminBackend, const std::string &bindAddr, int port, int maxconn, const std::string &adminPassword, const std::string &dcHost) const std::string &dcHost, const std::string &sslCert, const std::string &sslKey) : _AdminBackend(adminBackend), _BindAddr(bindAddr), _Port(port), _MaxConn(maxconn), _AdminPassword(adminPassword), _DcHost(dcHost), Loading @@ -376,6 +381,31 @@ ActiveDirectory::ActiveDirectory(AuthBackend &adminBackend, std::cerr << "AD/LDAP plugin listening on " << bindAddr << ":" << port << " (+CLDAP udp)" << std::endl; /* the Global Catalog: the same server, forest-wide and read-only */ if (_GcPort > 0) { _ServerSockets.push_back(new netplus::tcp(bindAddr.c_str(), _GcPort, maxconn, 0)); std::cerr << "Global Catalog listening on " << bindAddr << ":" << _GcPort << std::endl; } /* LDAPS and GC over TLS with the server certificate (/SSL/CERT, /SSL/KEY) */ if (!sslCert.empty()) { netplus::tls::CertificateBundle b; bool loaded = false; try { loaded = b.loadFromFile(sslCert, sslKey); } catch (const std::exception &e) { std::cerr << "LDAPS: certificate " << sslCert << ": " << e.what() << std::endl; } if (loaded) { _Certs[dcHost.empty() ? std::string("default") : dcHost] = std::move(b); for (int p : {_LdapsPort, _GcsPort}) { if (p <= 0) continue; _ServerSockets.push_back(new netplus::ssl(_Certs, bindAddr, p, maxconn, 0)); std::cerr << (p == _GcsPort ? "Global Catalog (TLS)" : "LDAPS") << " listening on " << bindAddr << ":" << p << std::endl; } } else { std::cerr << "LDAPS: no usable certificate, ports 636/3269 stay closed" << std::endl; } } } void ActiveDirectory::init() { Loading Loading @@ -1139,9 +1169,21 @@ void ActiveDirectory::CreateConnection(std::shared_ptr<netplus::con> &res) { } void ActiveDirectory::ConnectEvent(netplus::con &curcon, const int, ULONG_PTR) { std::cerr << "[LDAP] TCP connect" << std::endl; LdapSession sess; /* which listener: our side of the connection */ sockaddr_in la{}; socklen_t ll = sizeof(la); int lport = 0; if (!curcon.slots.empty() && curcon.slots[0].csock && getsockname(curcon.slots[0].csock->fd(), reinterpret_cast<sockaddr*>(&la), &ll) == 0 && la.sin_family == AF_INET) lport = ntohs(la.sin_port); sess.globalCatalog = lport > 0 && (lport == _GcPort || lport == _GcsPort); sess.tls = lport > 0 && (lport == _LdapsPort || lport == _GcsPort); std::cerr << "[LDAP] TCP connect port=" << lport << (sess.globalCatalog ? " (GC)" : "") << (sess.tls ? " (TLS)" : "") << std::endl; std::lock_guard<std::mutex> lock(_Lock); _Sessions[&curcon] = LdapSession(); _Sessions[&curcon] = sess; } void ActiveDirectory::DisconnectEvent(netplus::con &curcon, const int, ULONG_PTR) { Loading Loading @@ -1347,6 +1389,12 @@ std::vector<uint8_t> ActiveDirectory::handleMessage(const uint8_t *data, size_t << ") authed=" << session.authenticated << " as='" << session.bindDN << "'" << std::endl; } /* the Global Catalog is read-only */ if (session.globalCatalog && (appTag == LDAP_ADD_REQUEST || appTag == LDAP_MODIFY_REQUEST || appTag == LDAP_DEL_REQUEST || appTag == LDAP_MODRDN_REQUEST)) return buildLdapMessage(msgId, uint8_t(LdapBer::CLASS_APPLICATION | LdapBer::CONSTRUCTED | (appTag + 1)), buildLdapResult(LDAP_UNWILLING_TO_PERFORM, "", "the global catalog is read-only")); switch (appTag) { case LDAP_BIND_REQUEST: return handleBindRequest(content, contentLen, msgId, session); case LDAP_UNBIND_REQUEST: return handleUnbindRequest(msgId, session); Loading Loading @@ -1700,6 +1748,7 @@ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content out.insert(out.end(), e.begin(), e.end()); } int32_t total = int32_t(ps.entries.size()); std::cerr << "[LDAP] paged msgId=" << msgId << " page up to " << ps.pos << "/" << total << std::endl; if (ps.pos >= ps.entries.size()) { session.paged.erase(key); key.clear(); Loading Loading @@ -1819,6 +1868,16 @@ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content return false; } LdapEntry le = renderEntry(ent, requested, typesOnly, d.dir.get()); if (session.globalCatalog) { /* only the partial attribute set (schema GC flag) */ std::vector<LdapAttribute> keep; for (auto &a : le.attributes) { auto *def = dsdb::findAttribute(a.type); if (def && (def->flags & dsdb::A_GC)) keep.push_back(std::move(a)); } le.attributes = std::move(keep); } { std::string dump; for (auto &at : le.attributes) { Loading Loading @@ -2778,6 +2837,7 @@ fenrirds::Plugin *fenrirds_plugin_create(fenrirds::AuthBackend &backend, int maxconn = 128; std::string adminPassword; std::string dcHost; std::string sslCert, sslKey; if (configJson) { json_object *jobj = json_tokener_parse(configJson); Loading @@ -2792,11 +2852,14 @@ fenrirds::Plugin *fenrirds_plugin_create(fenrirds::AuthBackend &backend, if (jmax) maxconn = json_object_get_int(jmax); if (jpw) adminPassword = json_object_get_string(jpw); if (auto *jh = json_object_object_get(jobj, "dns_hostname")) dcHost = json_object_get_string(jh); if (auto *jc = json_object_object_get(jobj, "ssl_cert")) sslCert = json_object_get_string(jc); if (auto *jk = json_object_object_get(jobj, "ssl_key")) sslKey = json_object_get_string(jk); json_object_put(jobj); } } return new fenrirds::ActiveDirectory(backend, bindAddr, port, maxconn, adminPassword, dcHost); return new fenrirds::ActiveDirectory(backend, bindAddr, port, maxconn, adminPassword, dcHost, sslCert, sslKey); } void fenrirds_plugin_destroy(fenrirds::Plugin *p) { Loading plugin/ad/ad.h +10 −1 Changes for plugin/ad/ad.h: 10 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ #include <uuidp.h> #include <netplus/socket.h> #include <netplus/crypto/tls.h> #include <netplus/connection.h> #include <netplus/eventapi.h> Loading Loading @@ -279,7 +280,9 @@ namespace fenrirds { const std::string &bindAddr = "0.0.0.0", int port = 389, int maxconn = 128, const std::string &adminPassword = "", const std::string &dcHost = ""); const std::string &dcHost = "", const std::string &sslCert = std::string(), const std::string &sslKey = std::string()); ~ActiveDirectory() override; /* Plugin interface */ Loading Loading @@ -419,6 +422,12 @@ namespace fenrirds { std::mutex _Lock; std::vector<netplus::socket*> _ServerSockets; /* Global Catalog (3268) and, with a certificate, LDAPS (636) and * GC over TLS (3269); 0 disables a port */ int _GcPort = 3268; int _LdapsPort = 636; int _GcsPort = 3269; std::map<std::string, netplus::tls::CertificateBundle> _Certs; /* outlives the ssl sockets */ void *_Event; netplus::udp *_Cldap = nullptr; Loading src/fenrirds.cpp +5 −0 Changes for src/fenrirds.cpp: 5 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -1350,6 +1350,11 @@ int main(int argc,char *argv[]){ if (adMax) json_object_object_add(jcfg, "ad_maxconn", json_object_new_int(config.getIntValue(adMax, 0))); /* the server certificate, for LDAPS / GC over TLS */ if (auto *k = cfgKey("/SSL/CERT")) json_object_object_add(jcfg, "ssl_cert", json_object_new_string(config.getValue(k, 0).c_str())); if (auto *k = cfgKey("/SSL/KEY")) json_object_object_add(jcfg, "ssl_key", json_object_new_string(config.getValue(k, 0).c_str())); /* DNS plugin config */ if (auto *k = cfgKey("/DNS/BIND")) Loading Loading
plugin/ad/ad.cpp +67 −4 Changes for plugin/ad/ad.cpp: 67 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -30,6 +30,9 @@ #include <cstring> #include <map> #include <algorithm> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/socket.h> #include <sstream> #include <memory> #include <functional> Loading Loading @@ -360,7 +363,9 @@ ActiveDirectory::ActiveDirectory(AuthBackend &adminBackend, const std::string &bindAddr, int port, int maxconn, const std::string &adminPassword, const std::string &dcHost) const std::string &dcHost, const std::string &sslCert, const std::string &sslKey) : _AdminBackend(adminBackend), _BindAddr(bindAddr), _Port(port), _MaxConn(maxconn), _AdminPassword(adminPassword), _DcHost(dcHost), Loading @@ -376,6 +381,31 @@ ActiveDirectory::ActiveDirectory(AuthBackend &adminBackend, std::cerr << "AD/LDAP plugin listening on " << bindAddr << ":" << port << " (+CLDAP udp)" << std::endl; /* the Global Catalog: the same server, forest-wide and read-only */ if (_GcPort > 0) { _ServerSockets.push_back(new netplus::tcp(bindAddr.c_str(), _GcPort, maxconn, 0)); std::cerr << "Global Catalog listening on " << bindAddr << ":" << _GcPort << std::endl; } /* LDAPS and GC over TLS with the server certificate (/SSL/CERT, /SSL/KEY) */ if (!sslCert.empty()) { netplus::tls::CertificateBundle b; bool loaded = false; try { loaded = b.loadFromFile(sslCert, sslKey); } catch (const std::exception &e) { std::cerr << "LDAPS: certificate " << sslCert << ": " << e.what() << std::endl; } if (loaded) { _Certs[dcHost.empty() ? std::string("default") : dcHost] = std::move(b); for (int p : {_LdapsPort, _GcsPort}) { if (p <= 0) continue; _ServerSockets.push_back(new netplus::ssl(_Certs, bindAddr, p, maxconn, 0)); std::cerr << (p == _GcsPort ? "Global Catalog (TLS)" : "LDAPS") << " listening on " << bindAddr << ":" << p << std::endl; } } else { std::cerr << "LDAPS: no usable certificate, ports 636/3269 stay closed" << std::endl; } } } void ActiveDirectory::init() { Loading Loading @@ -1139,9 +1169,21 @@ void ActiveDirectory::CreateConnection(std::shared_ptr<netplus::con> &res) { } void ActiveDirectory::ConnectEvent(netplus::con &curcon, const int, ULONG_PTR) { std::cerr << "[LDAP] TCP connect" << std::endl; LdapSession sess; /* which listener: our side of the connection */ sockaddr_in la{}; socklen_t ll = sizeof(la); int lport = 0; if (!curcon.slots.empty() && curcon.slots[0].csock && getsockname(curcon.slots[0].csock->fd(), reinterpret_cast<sockaddr*>(&la), &ll) == 0 && la.sin_family == AF_INET) lport = ntohs(la.sin_port); sess.globalCatalog = lport > 0 && (lport == _GcPort || lport == _GcsPort); sess.tls = lport > 0 && (lport == _LdapsPort || lport == _GcsPort); std::cerr << "[LDAP] TCP connect port=" << lport << (sess.globalCatalog ? " (GC)" : "") << (sess.tls ? " (TLS)" : "") << std::endl; std::lock_guard<std::mutex> lock(_Lock); _Sessions[&curcon] = LdapSession(); _Sessions[&curcon] = sess; } void ActiveDirectory::DisconnectEvent(netplus::con &curcon, const int, ULONG_PTR) { Loading Loading @@ -1347,6 +1389,12 @@ std::vector<uint8_t> ActiveDirectory::handleMessage(const uint8_t *data, size_t << ") authed=" << session.authenticated << " as='" << session.bindDN << "'" << std::endl; } /* the Global Catalog is read-only */ if (session.globalCatalog && (appTag == LDAP_ADD_REQUEST || appTag == LDAP_MODIFY_REQUEST || appTag == LDAP_DEL_REQUEST || appTag == LDAP_MODRDN_REQUEST)) return buildLdapMessage(msgId, uint8_t(LdapBer::CLASS_APPLICATION | LdapBer::CONSTRUCTED | (appTag + 1)), buildLdapResult(LDAP_UNWILLING_TO_PERFORM, "", "the global catalog is read-only")); switch (appTag) { case LDAP_BIND_REQUEST: return handleBindRequest(content, contentLen, msgId, session); case LDAP_UNBIND_REQUEST: return handleUnbindRequest(msgId, session); Loading Loading @@ -1700,6 +1748,7 @@ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content out.insert(out.end(), e.begin(), e.end()); } int32_t total = int32_t(ps.entries.size()); std::cerr << "[LDAP] paged msgId=" << msgId << " page up to " << ps.pos << "/" << total << std::endl; if (ps.pos >= ps.entries.size()) { session.paged.erase(key); key.clear(); Loading Loading @@ -1819,6 +1868,16 @@ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content return false; } LdapEntry le = renderEntry(ent, requested, typesOnly, d.dir.get()); if (session.globalCatalog) { /* only the partial attribute set (schema GC flag) */ std::vector<LdapAttribute> keep; for (auto &a : le.attributes) { auto *def = dsdb::findAttribute(a.type); if (def && (def->flags & dsdb::A_GC)) keep.push_back(std::move(a)); } le.attributes = std::move(keep); } { std::string dump; for (auto &at : le.attributes) { Loading Loading @@ -2778,6 +2837,7 @@ fenrirds::Plugin *fenrirds_plugin_create(fenrirds::AuthBackend &backend, int maxconn = 128; std::string adminPassword; std::string dcHost; std::string sslCert, sslKey; if (configJson) { json_object *jobj = json_tokener_parse(configJson); Loading @@ -2792,11 +2852,14 @@ fenrirds::Plugin *fenrirds_plugin_create(fenrirds::AuthBackend &backend, if (jmax) maxconn = json_object_get_int(jmax); if (jpw) adminPassword = json_object_get_string(jpw); if (auto *jh = json_object_object_get(jobj, "dns_hostname")) dcHost = json_object_get_string(jh); if (auto *jc = json_object_object_get(jobj, "ssl_cert")) sslCert = json_object_get_string(jc); if (auto *jk = json_object_object_get(jobj, "ssl_key")) sslKey = json_object_get_string(jk); json_object_put(jobj); } } return new fenrirds::ActiveDirectory(backend, bindAddr, port, maxconn, adminPassword, dcHost); return new fenrirds::ActiveDirectory(backend, bindAddr, port, maxconn, adminPassword, dcHost, sslCert, sslKey); } void fenrirds_plugin_destroy(fenrirds::Plugin *p) { Loading
plugin/ad/ad.h +10 −1 Changes for plugin/ad/ad.h: 10 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -38,6 +38,7 @@ #include <uuidp.h> #include <netplus/socket.h> #include <netplus/crypto/tls.h> #include <netplus/connection.h> #include <netplus/eventapi.h> Loading Loading @@ -279,7 +280,9 @@ namespace fenrirds { const std::string &bindAddr = "0.0.0.0", int port = 389, int maxconn = 128, const std::string &adminPassword = "", const std::string &dcHost = ""); const std::string &dcHost = "", const std::string &sslCert = std::string(), const std::string &sslKey = std::string()); ~ActiveDirectory() override; /* Plugin interface */ Loading Loading @@ -419,6 +422,12 @@ namespace fenrirds { std::mutex _Lock; std::vector<netplus::socket*> _ServerSockets; /* Global Catalog (3268) and, with a certificate, LDAPS (636) and * GC over TLS (3269); 0 disables a port */ int _GcPort = 3268; int _LdapsPort = 636; int _GcsPort = 3269; std::map<std::string, netplus::tls::CertificateBundle> _Certs; /* outlives the ssl sockets */ void *_Event; netplus::udp *_Cldap = nullptr; Loading
src/fenrirds.cpp +5 −0 Changes for src/fenrirds.cpp: 5 added lines, 0 removed lines. Original line number Diff line number Diff line Loading @@ -1350,6 +1350,11 @@ int main(int argc,char *argv[]){ if (adMax) json_object_object_add(jcfg, "ad_maxconn", json_object_new_int(config.getIntValue(adMax, 0))); /* the server certificate, for LDAPS / GC over TLS */ if (auto *k = cfgKey("/SSL/CERT")) json_object_object_add(jcfg, "ssl_cert", json_object_new_string(config.getValue(k, 0).c_str())); if (auto *k = cfgKey("/SSL/KEY")) json_object_object_add(jcfg, "ssl_key", json_object_new_string(config.getValue(k, 0).c_str())); /* DNS plugin config */ if (auto *k = cfgKey("/DNS/BIND")) Loading