Loading plugin/ad/ad.cpp +111 −3 Changes for plugin/ad/ad.cpp: 111 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -1317,6 +1317,28 @@ std::vector<uint8_t> ActiveDirectory::handleMessage(const uint8_t *data, size_t return {}; uint8_t appTag = tag & 0x1F; /* controls [0] SEQUENCE OF Control { type, criticality, value } */ std::vector<LdapControl> controls; { size_t coff = off; uint8_t ct; const uint8_t *cc; size_t cl; if (off < len && LdapBer::parseTLV(data, len, coff, ct, cc, cl) && ct == 0xA0) { size_t o = 0; uint8_t t; const uint8_t *c; size_t l; while (o < cl && LdapBer::parseTLV(cc, cl, o, t, c, l)) { if (t != LdapBer::TAG_SEQUENCE) continue; LdapControl ctl; size_t f = 0; uint8_t ft; const uint8_t *fc; size_t fl; if (!LdapBer::parseTLV(c, l, f, ft, fc, fl)) continue; LdapBer::parseOctetString(fc, fl, ctl.oid); while (f < l && LdapBer::parseTLV(c, l, f, ft, fc, fl)) { if (ft == 0x01) LdapBer::parseBoolean(fc, fl, ctl.critical); else if (ft == 0x04) ctl.value.assign(fc, fc + fl); } controls.push_back(std::move(ctl)); } } } { static const char *opName[] = {"BIND","BINDRESP","UNBIND","SEARCH","SEARCHENTRY", "SEARCHDONE","?6?","MODIFY","ADD","DEL","?10?","MODRDN","?12?","COMPARE"}; Loading @@ -1328,7 +1350,7 @@ std::vector<uint8_t> ActiveDirectory::handleMessage(const uint8_t *data, size_t switch (appTag) { case LDAP_BIND_REQUEST: return handleBindRequest(content, contentLen, msgId, session); case LDAP_UNBIND_REQUEST: return handleUnbindRequest(msgId, session); case LDAP_SEARCH_REQUEST: return handleSearchRequest(content, contentLen, msgId, session); case LDAP_SEARCH_REQUEST: return handleSearchRequest(content, contentLen, msgId, session, controls); case LDAP_ADD_REQUEST: return handleAddRequest(content, contentLen, msgId, session); case LDAP_MODIFY_REQUEST: return handleModifyRequest(content, contentLen, msgId, session); case LDAP_DEL_REQUEST: return handleDelRequest(content, contentLen, msgId, session); Loading Loading @@ -1653,8 +1675,50 @@ std::vector<uint8_t> ActiveDirectory::handleUnbindRequest(int32_t, LdapSession & * Search *****************************************************************************/ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content, size_t len, int32_t msgId, LdapSession &session) { (void)session; int32_t msgId, LdapSession &session, const std::vector<LdapControl> &controls) { /* paged results: SEQUENCE { size INTEGER, cookie OCTET STRING } */ bool paged = false; int32_t pageSize = 0; std::string cookie; for (auto &c : controls) { if (c.oid != "1.2.840.113556.1.4.319") continue; size_t o = 0; uint8_t t; const uint8_t *v; size_t vl; if (!LdapBer::parseTLV(c.value.data(), c.value.size(), o, t, v, vl) || t != LdapBer::TAG_SEQUENCE) return buildSearchResultDone(msgId, LDAP_PROTOCOL_ERROR, "bad paged results control"); size_t f = 0; uint8_t ft; const uint8_t *fc; size_t fl; if (LdapBer::parseTLV(v, vl, f, ft, fc, fl)) LdapBer::parseInteger(fc, fl, pageSize); if (LdapBer::parseTLV(v, vl, f, ft, fc, fl)) LdapBer::parseOctetString(fc, fl, cookie); paged = true; if (pageSize > 1000) pageSize = 1000; /* AD's MaxPageSize */ } auto nextPage = [&](LdapSession::PagedSearch &ps, std::string key) { std::vector<uint8_t> out; size_t n = pageSize > 0 ? size_t(pageSize) : ps.entries.size(); for (size_t i = 0; i < n && ps.pos < ps.entries.size(); ++i, ++ps.pos) { auto e = buildSearchResultEntry(msgId, ps.entries[ps.pos]); out.insert(out.end(), e.begin(), e.end()); } int32_t total = int32_t(ps.entries.size()); if (ps.pos >= ps.entries.size()) { session.paged.erase(key); key.clear(); } auto done = buildPagedDone(msgId, LDAP_SUCCESS, key, total); out.insert(out.end(), done.begin(), done.end()); return out; }; if (paged && !cookie.empty()) { auto it = session.paged.find(cookie); if (it == session.paged.end()) return buildSearchResultDone(msgId, LDAP_UNWILLING_TO_PERFORM, "unknown paged results cookie"); if (pageSize == 0) { /* the client abandons the search */ session.paged.erase(it); return buildPagedDone(msgId, LDAP_SUCCESS, "", 0); } return nextPage(it->second, cookie); } size_t off = 0; uint8_t tag; const uint8_t *data; Loading Loading @@ -1739,6 +1803,7 @@ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content std::lock_guard<std::mutex> lock(_Lock); bool found = false; LdapResultCode rc = LDAP_SUCCESS; LdapSession::PagedSearch collected; /* paged: entries kept for later pages */ for (auto &d : _Domains) { /* this domain is involved if base is at/under its NC, or (subtree) * the NC is under base */ Loading Loading @@ -1767,13 +1832,29 @@ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content } std::cerr << "[LDAP] entry dn='" << le.dn << "'" << dump << std::endl; } if (paged) { collected.entries.push_back(std::move(le)); } else { auto e = buildSearchResultEntry(msgId, le); out.insert(out.end(), e.begin(), e.end()); } ++count; return true; }); } (void)found; if (paged && rc == LDAP_SUCCESS) { std::cerr << "[LDAP] SEARCH msgId=" << msgId << " -> " << count << " entries, paged by " << pageSize << std::endl; auto c = crypto::randomBytes(8); std::string key(c.begin(), c.end()); if (session.paged.size() >= 32) { /* bound abandoned searches */ session.paged.erase(session.paged.begin()); } auto &ps = session.paged[key] = std::move(collected); auto page = nextPage(ps, key); out.insert(out.end(), page.begin(), page.end()); return out; } std::cerr << "[LDAP] SEARCH msgId=" << msgId << " -> " << count << " entries, rc=" << int(rc) << (found ? "" : " (no matching NC)") << std::endl; auto done = buildSearchResultDone(msgId, rc); Loading Loading @@ -2646,6 +2727,33 @@ std::vector<uint8_t> ActiveDirectory::buildSearchResultEntry(int32_t msgId, LdapBer::CLASS_APPLICATION | LdapBer::CONSTRUCTED | LDAP_SEARCH_RESULT_ENTRY, body); } std::vector<uint8_t> ActiveDirectory::buildPagedDone(int32_t msgId, LdapResultCode code, const std::string &cookie, int32_t estimate) { std::vector<uint8_t> body; auto id = LdapBer::encodeInteger(msgId); body.insert(body.end(), id.begin(), id.end()); auto res = buildLdapResult(code, "", ""); body.push_back(LdapBer::CLASS_APPLICATION | LdapBer::CONSTRUCTED | LDAP_SEARCH_RESULT_DONE); LdapBer::encodeLength(body, res.size()); body.insert(body.end(), res.begin(), res.end()); /* controls [0] { Control { "1.2.840.113556.1.4.319", value = SEQUENCE { size, cookie } } } */ std::vector<uint8_t> val; auto est = LdapBer::encodeInteger(estimate); auto ck = LdapBer::encodeOctetString(std::vector<uint8_t>(cookie.begin(), cookie.end())); val.insert(val.end(), est.begin(), est.end()); val.insert(val.end(), ck.begin(), ck.end()); std::vector<uint8_t> ctl; auto oid = LdapBer::encodeOctetString(std::string("1.2.840.113556.1.4.319")); auto v = LdapBer::encodeOctetString(LdapBer::encodeSequence(val)); ctl.insert(ctl.end(), oid.begin(), oid.end()); ctl.insert(ctl.end(), v.begin(), v.end()); auto ctlSeq = LdapBer::encodeSequence(ctl); body.push_back(0xA0); LdapBer::encodeLength(body, ctlSeq.size()); body.insert(body.end(), ctlSeq.begin(), ctlSeq.end()); return LdapBer::encodeSequence(body); } std::vector<uint8_t> ActiveDirectory::buildSearchResultDone(int32_t msgId, LdapResultCode code, const std::string &msg) { Loading plugin/ad/ad.h +23 −1 Changes for plugin/ad/ad.h: 23 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -204,8 +204,26 @@ namespace fenrirds { /*************************************************************************** * Per-connection LDAP session state **************************************************************************/ /* an LDAP request control (RFC 4511 4.1.11) */ struct LdapControl { std::string oid; bool critical = false; std::vector<uint8_t> value; }; struct VISIBILITY LdapSession { bool authenticated = false; /* the listener this connection came in on */ bool globalCatalog = false; /* 3268/3269: forest-wide, read-only */ bool tls = false; /* 636/3269 */ /* paged results (1.2.840.113556.1.4.319): the rest of a search, * handed out page by page under a cookie */ struct PagedSearch { std::vector<LdapEntry> entries; size_t pos = 0; }; std::map<std::string, PagedSearch> paged; std::string bindDN; /* DN of the authenticated principal */ uuid::uuid userUid; /* UUID if bound as a user */ std::string realm; /* domain realm for the session */ Loading Loading @@ -329,7 +347,8 @@ namespace fenrirds { std::vector<uint8_t> handleBindRequest(const uint8_t *content, size_t len, int32_t msgId, LdapSession &session); std::vector<uint8_t> handleSearchRequest(const uint8_t *content, size_t len, int32_t msgId, LdapSession &session); int32_t msgId, LdapSession &session, const std::vector<LdapControl> &controls = {}); std::vector<uint8_t> handleAddRequest(const uint8_t *content, size_t len, int32_t msgId, LdapSession &session); std::vector<uint8_t> handleModifyRequest(const uint8_t *content, size_t len, Loading @@ -351,6 +370,9 @@ namespace fenrirds { const std::string &diagnosticMsg); std::vector<uint8_t> buildSearchResultEntry(int32_t msgId, const LdapEntry &entry); /* a SearchResultDone carrying a paged-results response control */ std::vector<uint8_t> buildPagedDone(int32_t msgId, LdapResultCode code, const std::string &cookie, int32_t estimate); std::vector<uint8_t> buildSearchResultDone(int32_t msgId, LdapResultCode code, const std::string &msg = ""); Loading Loading
plugin/ad/ad.cpp +111 −3 Changes for plugin/ad/ad.cpp: 111 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -1317,6 +1317,28 @@ std::vector<uint8_t> ActiveDirectory::handleMessage(const uint8_t *data, size_t return {}; uint8_t appTag = tag & 0x1F; /* controls [0] SEQUENCE OF Control { type, criticality, value } */ std::vector<LdapControl> controls; { size_t coff = off; uint8_t ct; const uint8_t *cc; size_t cl; if (off < len && LdapBer::parseTLV(data, len, coff, ct, cc, cl) && ct == 0xA0) { size_t o = 0; uint8_t t; const uint8_t *c; size_t l; while (o < cl && LdapBer::parseTLV(cc, cl, o, t, c, l)) { if (t != LdapBer::TAG_SEQUENCE) continue; LdapControl ctl; size_t f = 0; uint8_t ft; const uint8_t *fc; size_t fl; if (!LdapBer::parseTLV(c, l, f, ft, fc, fl)) continue; LdapBer::parseOctetString(fc, fl, ctl.oid); while (f < l && LdapBer::parseTLV(c, l, f, ft, fc, fl)) { if (ft == 0x01) LdapBer::parseBoolean(fc, fl, ctl.critical); else if (ft == 0x04) ctl.value.assign(fc, fc + fl); } controls.push_back(std::move(ctl)); } } } { static const char *opName[] = {"BIND","BINDRESP","UNBIND","SEARCH","SEARCHENTRY", "SEARCHDONE","?6?","MODIFY","ADD","DEL","?10?","MODRDN","?12?","COMPARE"}; Loading @@ -1328,7 +1350,7 @@ std::vector<uint8_t> ActiveDirectory::handleMessage(const uint8_t *data, size_t switch (appTag) { case LDAP_BIND_REQUEST: return handleBindRequest(content, contentLen, msgId, session); case LDAP_UNBIND_REQUEST: return handleUnbindRequest(msgId, session); case LDAP_SEARCH_REQUEST: return handleSearchRequest(content, contentLen, msgId, session); case LDAP_SEARCH_REQUEST: return handleSearchRequest(content, contentLen, msgId, session, controls); case LDAP_ADD_REQUEST: return handleAddRequest(content, contentLen, msgId, session); case LDAP_MODIFY_REQUEST: return handleModifyRequest(content, contentLen, msgId, session); case LDAP_DEL_REQUEST: return handleDelRequest(content, contentLen, msgId, session); Loading Loading @@ -1653,8 +1675,50 @@ std::vector<uint8_t> ActiveDirectory::handleUnbindRequest(int32_t, LdapSession & * Search *****************************************************************************/ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content, size_t len, int32_t msgId, LdapSession &session) { (void)session; int32_t msgId, LdapSession &session, const std::vector<LdapControl> &controls) { /* paged results: SEQUENCE { size INTEGER, cookie OCTET STRING } */ bool paged = false; int32_t pageSize = 0; std::string cookie; for (auto &c : controls) { if (c.oid != "1.2.840.113556.1.4.319") continue; size_t o = 0; uint8_t t; const uint8_t *v; size_t vl; if (!LdapBer::parseTLV(c.value.data(), c.value.size(), o, t, v, vl) || t != LdapBer::TAG_SEQUENCE) return buildSearchResultDone(msgId, LDAP_PROTOCOL_ERROR, "bad paged results control"); size_t f = 0; uint8_t ft; const uint8_t *fc; size_t fl; if (LdapBer::parseTLV(v, vl, f, ft, fc, fl)) LdapBer::parseInteger(fc, fl, pageSize); if (LdapBer::parseTLV(v, vl, f, ft, fc, fl)) LdapBer::parseOctetString(fc, fl, cookie); paged = true; if (pageSize > 1000) pageSize = 1000; /* AD's MaxPageSize */ } auto nextPage = [&](LdapSession::PagedSearch &ps, std::string key) { std::vector<uint8_t> out; size_t n = pageSize > 0 ? size_t(pageSize) : ps.entries.size(); for (size_t i = 0; i < n && ps.pos < ps.entries.size(); ++i, ++ps.pos) { auto e = buildSearchResultEntry(msgId, ps.entries[ps.pos]); out.insert(out.end(), e.begin(), e.end()); } int32_t total = int32_t(ps.entries.size()); if (ps.pos >= ps.entries.size()) { session.paged.erase(key); key.clear(); } auto done = buildPagedDone(msgId, LDAP_SUCCESS, key, total); out.insert(out.end(), done.begin(), done.end()); return out; }; if (paged && !cookie.empty()) { auto it = session.paged.find(cookie); if (it == session.paged.end()) return buildSearchResultDone(msgId, LDAP_UNWILLING_TO_PERFORM, "unknown paged results cookie"); if (pageSize == 0) { /* the client abandons the search */ session.paged.erase(it); return buildPagedDone(msgId, LDAP_SUCCESS, "", 0); } return nextPage(it->second, cookie); } size_t off = 0; uint8_t tag; const uint8_t *data; Loading Loading @@ -1739,6 +1803,7 @@ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content std::lock_guard<std::mutex> lock(_Lock); bool found = false; LdapResultCode rc = LDAP_SUCCESS; LdapSession::PagedSearch collected; /* paged: entries kept for later pages */ for (auto &d : _Domains) { /* this domain is involved if base is at/under its NC, or (subtree) * the NC is under base */ Loading Loading @@ -1767,13 +1832,29 @@ std::vector<uint8_t> ActiveDirectory::handleSearchRequest(const uint8_t *content } std::cerr << "[LDAP] entry dn='" << le.dn << "'" << dump << std::endl; } if (paged) { collected.entries.push_back(std::move(le)); } else { auto e = buildSearchResultEntry(msgId, le); out.insert(out.end(), e.begin(), e.end()); } ++count; return true; }); } (void)found; if (paged && rc == LDAP_SUCCESS) { std::cerr << "[LDAP] SEARCH msgId=" << msgId << " -> " << count << " entries, paged by " << pageSize << std::endl; auto c = crypto::randomBytes(8); std::string key(c.begin(), c.end()); if (session.paged.size() >= 32) { /* bound abandoned searches */ session.paged.erase(session.paged.begin()); } auto &ps = session.paged[key] = std::move(collected); auto page = nextPage(ps, key); out.insert(out.end(), page.begin(), page.end()); return out; } std::cerr << "[LDAP] SEARCH msgId=" << msgId << " -> " << count << " entries, rc=" << int(rc) << (found ? "" : " (no matching NC)") << std::endl; auto done = buildSearchResultDone(msgId, rc); Loading Loading @@ -2646,6 +2727,33 @@ std::vector<uint8_t> ActiveDirectory::buildSearchResultEntry(int32_t msgId, LdapBer::CLASS_APPLICATION | LdapBer::CONSTRUCTED | LDAP_SEARCH_RESULT_ENTRY, body); } std::vector<uint8_t> ActiveDirectory::buildPagedDone(int32_t msgId, LdapResultCode code, const std::string &cookie, int32_t estimate) { std::vector<uint8_t> body; auto id = LdapBer::encodeInteger(msgId); body.insert(body.end(), id.begin(), id.end()); auto res = buildLdapResult(code, "", ""); body.push_back(LdapBer::CLASS_APPLICATION | LdapBer::CONSTRUCTED | LDAP_SEARCH_RESULT_DONE); LdapBer::encodeLength(body, res.size()); body.insert(body.end(), res.begin(), res.end()); /* controls [0] { Control { "1.2.840.113556.1.4.319", value = SEQUENCE { size, cookie } } } */ std::vector<uint8_t> val; auto est = LdapBer::encodeInteger(estimate); auto ck = LdapBer::encodeOctetString(std::vector<uint8_t>(cookie.begin(), cookie.end())); val.insert(val.end(), est.begin(), est.end()); val.insert(val.end(), ck.begin(), ck.end()); std::vector<uint8_t> ctl; auto oid = LdapBer::encodeOctetString(std::string("1.2.840.113556.1.4.319")); auto v = LdapBer::encodeOctetString(LdapBer::encodeSequence(val)); ctl.insert(ctl.end(), oid.begin(), oid.end()); ctl.insert(ctl.end(), v.begin(), v.end()); auto ctlSeq = LdapBer::encodeSequence(ctl); body.push_back(0xA0); LdapBer::encodeLength(body, ctlSeq.size()); body.insert(body.end(), ctlSeq.begin(), ctlSeq.end()); return LdapBer::encodeSequence(body); } std::vector<uint8_t> ActiveDirectory::buildSearchResultDone(int32_t msgId, LdapResultCode code, const std::string &msg) { Loading
plugin/ad/ad.h +23 −1 Changes for plugin/ad/ad.h: 23 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -204,8 +204,26 @@ namespace fenrirds { /*************************************************************************** * Per-connection LDAP session state **************************************************************************/ /* an LDAP request control (RFC 4511 4.1.11) */ struct LdapControl { std::string oid; bool critical = false; std::vector<uint8_t> value; }; struct VISIBILITY LdapSession { bool authenticated = false; /* the listener this connection came in on */ bool globalCatalog = false; /* 3268/3269: forest-wide, read-only */ bool tls = false; /* 636/3269 */ /* paged results (1.2.840.113556.1.4.319): the rest of a search, * handed out page by page under a cookie */ struct PagedSearch { std::vector<LdapEntry> entries; size_t pos = 0; }; std::map<std::string, PagedSearch> paged; std::string bindDN; /* DN of the authenticated principal */ uuid::uuid userUid; /* UUID if bound as a user */ std::string realm; /* domain realm for the session */ Loading Loading @@ -329,7 +347,8 @@ namespace fenrirds { std::vector<uint8_t> handleBindRequest(const uint8_t *content, size_t len, int32_t msgId, LdapSession &session); std::vector<uint8_t> handleSearchRequest(const uint8_t *content, size_t len, int32_t msgId, LdapSession &session); int32_t msgId, LdapSession &session, const std::vector<LdapControl> &controls = {}); std::vector<uint8_t> handleAddRequest(const uint8_t *content, size_t len, int32_t msgId, LdapSession &session); std::vector<uint8_t> handleModifyRequest(const uint8_t *content, size_t len, Loading @@ -351,6 +370,9 @@ namespace fenrirds { const std::string &diagnosticMsg); std::vector<uint8_t> buildSearchResultEntry(int32_t msgId, const LdapEntry &entry); /* a SearchResultDone carrying a paged-results response control */ std::vector<uint8_t> buildPagedDone(int32_t msgId, LdapResultCode code, const std::string &cookie, int32_t estimate); std::vector<uint8_t> buildSearchResultDone(int32_t msgId, LdapResultCode code, const std::string &msg = ""); Loading