Commit 471818a1 authored by jan.koester's avatar jan.koester
Browse files

test

parent e30786fd
Loading
Loading
Loading
Loading
Loading
+111 −3
Changes for plugin/ad/ad.cpp: 111 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -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"};
@@ -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);
@@ -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;
@@ -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 */
@@ -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);
@@ -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) {
+23 −1
Changes for plugin/ad/ad.h: 23 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -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     */
@@ -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,
@@ -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 = "");