Commit b0b231ca authored by jan.koester's avatar jan.koester
Browse files

tes

parent 1cff0dbd
Loading
Loading
Loading
Loading
Loading
+73 −9
Changes for plugin/ad/ad.cpp: 73 added lines, 9 removed lines.
Original line number Diff line number Diff line
@@ -549,6 +549,9 @@ bool ActiveDirectory::dirList(const std::string &domain, int kind,
    if (!d) { err = "unknown domain '" + domain + "'"; return false; }

    dsdb::Filter f;
    if (kind == DirComputer) {
        f = dsdb::Filter::equal("objectClass", "computer");
    } else {
        f.type = dsdb::Filter::And;
        f.children.push_back(dsdb::Filter::equal("objectClass",
                                                 kind == DirGroup ? "group" : "user"));
@@ -559,14 +562,18 @@ bool ActiveDirectory::dirList(const std::string &domain, int kind,
            notComp.children.push_back(dsdb::Filter::equal("objectClass", "computer"));
            f.children.push_back(notComp);
        }
    }

    dsdb::Dn base = d->meta.baseDn.child("CN", "Users");
    /* computers can live under CN=Computers or CN=Users — search the whole
     * domain for them; users/groups stay under CN=Users */
    dsdb::Dn base = (kind == DirComputer) ? d->meta.baseDn : d->meta.baseDn.child("CN", "Users");
    out.clear();
    d->dir->search(base, LDAP_SCOPE_SUBTREE, f, [&](const dsdb::Entry &e) -> bool {
        out.push_back(e);
        return true;
    });
    std::cerr << "[AD] dirList domain=" << domain << " kind=" << (kind == DirGroup ? "group" : "user")
    const char *kn = kind == DirGroup ? "group" : (kind == DirComputer ? "computer" : "user");
    std::cerr << "[AD] dirList domain=" << domain << " kind=" << kn
              << " base=" << base.str() << " -> " << out.size() << " objects" << std::endl;
    return true;
}
@@ -693,7 +700,7 @@ bool ActiveDirectory::dirModify(const std::string &domain, const uuid::uuid &gui
    }

    try {
        d->dir->modify(guid, merged, sec.get());
        d->dir->modify(guid, merged, sec.get(), true);   /* merge: keep other attrs */
    } catch (std::exception &e2) {
        err = e2.what();
        return false;
@@ -720,6 +727,55 @@ bool ActiveDirectory::dirRemove(const std::string &domain, const uuid::uuid &gui
    return true;
}

bool ActiveDirectory::dirSetWindowsLogin(const std::string &domain, const uuid::uuid &guid,
                                         bool enable, const std::string &clearPassword,
                                         std::string &err) {
    if (g_Replication && g_Replication->isSlave()) {
        err = "this node is a replication slave (read-only); make changes on the master";
        return false;
    }
    std::lock_guard<std::mutex> lock(_Lock);
    Dom *d = domainByName(domain);
    if (!d) { err = "unknown domain '" + domain + "'"; return false; }
    auto e = d->dir->byGuid(guid);
    if (!e) { err = "object not found"; return false; }
    std::string sam = e->str("sAMAccountName");
    if (sam.empty()) { err = "object has no sAMAccountName"; return false; }

    if (enable) {
        if (!e->objectSid.empty()) return true;   /* already a Windows account */
        if (clearPassword.empty()) { err = "a password is required to enable Windows login"; return false; }
        uint32_t rid = d->dir->allocateRid();
        std::string salt = dsdb::kerberosSalt(dsdb::PrincipalKind::User,
                                              d->meta.realm, sam, d->meta.dnsName);
        dsdb::Secrets sec = dsdb::deriveSecrets(clearPassword, salt, 0);
        dsdb::AttrMap a;
        a["objectSid"] = {d->meta.sid.withRid(rid).binary()};
        dsdb::setInt(a, "sAMAccountType", dsdb::SAM_USER_OBJECT);
        dsdb::setInt(a, "userAccountControl", dsdb::UAC_NORMAL_ACCOUNT | dsdb::UAC_DONT_EXPIRE_PASSWORD);
        dsdb::setInt(a, "primaryGroupID", dsdb::RID_DOMAIN_USERS);
        dsdb::setInt(a, "msDS-KeyVersionNumber", sec.kvno);
        dsdb::setInt(a, "pwdLastSet", sec.pwdLastSet);
        if (e->str("userPrincipalName").empty())
            a["userPrincipalName"] = {sam + "@" + d->meta.dnsName};
        try { d->dir->modify(guid, a, &sec, true); }
        catch (std::exception &ex) { err = ex.what(); return false; }
        std::cerr << "[AD] enabled Windows login for '" << sam << "' (RID " << rid << ")" << std::endl;
    } else {
        if (e->objectSid.empty()) return true;    /* already SID-less */
        dsdb::AttrMap a;                           /* empty value = delete attribute */
        a["objectSid"] = {};
        a["sAMAccountType"] = {};
        a["userAccountControl"] = {};
        a["primaryGroupID"] = {};
        a["msDS-KeyVersionNumber"] = {};
        try { d->dir->modify(guid, a, nullptr, true); }
        catch (std::exception &ex) { err = ex.what(); return false; }
        std::cerr << "[AD] disabled Windows login for '" << sam << "' (now SID-less)" << std::endl;
    }
    return true;
}

bool ActiveDirectory::dirAuthenticate(const std::string &domain, const std::string &sam,
                                      const std::string &pw, uuid::uuid &outGuid,
                                      std::string &err) {
@@ -808,8 +864,8 @@ bool ActiveDirectory::dirAddMember(const std::string &domain, const uuid::uuid &
    ma["memberOf"] = memberOf;

    try {
        d->dir->modify(group, ga, nullptr);
        d->dir->modify(member, ma, nullptr);
        d->dir->modify(group, ga, nullptr, true);
        d->dir->modify(member, ma, nullptr, true);
    } catch (std::exception &e) {
        err = e.what();
        return false;
@@ -842,8 +898,8 @@ bool ActiveDirectory::dirRemoveMember(const std::string &domain, const uuid::uui
    ma["memberOf"] = memberOf;

    try {
        d->dir->modify(group, ga, nullptr);
        d->dir->modify(member, ma, nullptr);
        d->dir->modify(group, ga, nullptr, true);
        d->dir->modify(member, ma, nullptr, true);
    } catch (std::exception &e) {
        err = e.what();
        return false;
@@ -919,6 +975,14 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated,
            if (existing) {
                groupGuid = existing->objectGuid;
                groupDn   = existing->dn.str();
                /* repair a group whose attributes an earlier buggy modify wiped */
                if (existing->str("sAMAccountName").empty()) {
                    dsdb::AttrMap fix;
                    fix["objectClass"]    = {"top", "group"};
                    fix["sAMAccountName"] = {gname};
                    fix["cn"] = {gname}; fix["name"] = {gname};
                    try { d->dir->modify(groupGuid, fix, nullptr, true); } catch (...) {}
                }
            } else {
                dsdb::AttrMap a;
                a["objectClass"]    = {"top", "group"};
@@ -953,14 +1017,14 @@ bool ActiveDirectory::dirImportNative(const std::string &domain, int &migrated,
                    if (std::find(members.begin(), members.end(), mdn) == members.end()) {
                        members.push_back(mdn);
                        dsdb::AttrMap ga = grpEnt->attrs; ga["member"] = members;
                        try { d->dir->modify(groupGuid, ga, nullptr); } catch (...) {}
                        try { d->dir->modify(groupGuid, ga, nullptr, true); } catch (...) {}
                    }
                }
                dsdb::Values mo = me->get("memberOf") ? *me->get("memberOf") : dsdb::Values{};
                if (std::find(mo.begin(), mo.end(), groupDn) == mo.end()) {
                    mo.push_back(groupDn);
                    dsdb::AttrMap ua = me->attrs; ua["memberOf"] = mo;
                    try { d->dir->modify(me->objectGuid, ua, nullptr); } catch (...) {}
                    try { d->dir->modify(me->objectGuid, ua, nullptr, true); } catch (...) {}
                }
            }
        }
+3 −0
Changes for plugin/ad/ad.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -283,6 +283,9 @@ namespace fenrirds {
        bool dirImportNative(const std::string &domain, int &migrated,
                             std::string &err,
                             std::map<std::string, std::string> *groupMap = nullptr) override;
        bool dirSetWindowsLogin(const std::string &domain, const uuid::uuid &guid,
                                bool enable, const std::string &clearPassword,
                                std::string &err) override;
        bool dirList(const std::string &domain, int kind,
                     std::vector<dsdb::Entry> &out, std::string &err) override;
        bool dirGet(const std::string &domain, const uuid::uuid &guid,
+39 −0
Changes for src/admin.cpp: 39 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -403,6 +403,45 @@ namespace fenrirds {
            json_object_put(jobj);
        }

        /* ---- JSON API: List Computers (directory objectClass=computer) ---- */
        void apiListComputers(libhttppp::HttpRequest &curreq, const char *cdid) {
            std::string domain = domainNameFor(cdid);
            if (domain.empty() || !g_AdPlugin)
                throw AuthBackendError("listComputers: no directory for domain!");
            std::vector<dsdb::Entry> ents;
            std::string err;
            if (!g_AdPlugin->dirList(domain, AdPlugin::DirComputer, ents, err))
                throw AuthBackendError("listComputers: " + err);
            json_object *jobj = json_object_new_object();
            json_object *jarr = json_object_new_array();
            for (const auto &e : ents) {
                json_object *jc = json_object_new_object();
                json_object_object_add(jc, "id", json_object_new_string(e.objectGuid.c_str()));
                json_object_object_add(jc, "name", json_object_new_string(e.str("sAMAccountName").c_str()));
                json_object_object_add(jc, "dnshostname", json_object_new_string(e.str("dNSHostName").c_str()));
                json_object_array_add(jarr, jc);
            }
            json_object_object_add(jobj, "computers", jarr);
            sendJson(curreq, jobj);
            json_object_put(jobj);
        }

        /* ---- JSON API: Remove Computer (enables a clean domain rejoin) ---- */
        void apiRemoveComputer(libhttppp::HttpRequest &curreq, const char *cdid, const char *cid) {
            std::string domain = domainNameFor(cdid);
            if (domain.empty() || !g_AdPlugin)
                throw AuthBackendError("removeComputer: no directory for domain!");
            uuid::uuid gid;
            gid.parse(cid);
            std::string err;
            if (!g_AdPlugin->dirRemove(domain, gid, err))
                throw AuthBackendError("removeComputer: " + err);
            json_object *jobj = json_object_new_object();
            json_object_object_add(jobj, "status", json_object_new_string("ok"));
            sendJson(curreq, jobj);
            json_object_put(jobj);
        }

        /* ---- JSON API: Edit User (directory object) ---- */
        void apiEditUser(libhttppp::HttpRequest &curreq, const char *cdid, const char *cuid) {
            if (strcmp(cdid, "admin") == 0) {
+12 −1
Changes for src/dsdb/directory.cpp: 12 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -284,7 +284,7 @@ uuid::uuid Directory::create(const Dn &dn, AttrMap attrs, const Secrets *secrets
    return guid;
}

void Directory::modify(const uuid::uuid &guid, const AttrMap &attrs, const Secrets *secrets) {
void Directory::modify(const uuid::uuid &guid, const AttrMap &attrs, const Secrets *secrets, bool mergeAttrs) {
    std::lock_guard<std::mutex> lk(_Mtx);
    ensureFresh();
    Dn dn;
@@ -292,7 +292,18 @@ void Directory::modify(const uuid::uuid &guid, const AttrMap &attrs, const Secre
    std::string sblob;
    if (!loadObject(_Backend, guid, dn, cur, sblob))
        throw std::runtime_error("no such object");
    if (mergeAttrs) {
        /* overlay: keep every attribute not named in `attrs`; an empty value
         * list deletes that attribute */
        for (const auto &kv : attrs) {
            if (kv.second.empty())
                cur.erase(kv.first);
            else
                cur[kv.first] = kv.second;
        }
    } else {
        cur = attrs;
    }
    cur["whenChanged"] = {generalizedTime(unixFromFileTime(fileTimeNow()))};
    if (secrets)
        sblob = secrets->serialize();
+7 −1
Changes for src/dsdb/directory.h: 7 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -117,7 +117,13 @@ namespace fenrirds {
             * consistent. create allocates objectGUID (if empty) and the SID
             * for a security principal; returns the new objectGUID. */
            uuid::uuid create(const Dn &dn, AttrMap attrs, const Secrets *secrets = nullptr);
            void modify(const uuid::uuid &guid, const AttrMap &attrs, const Secrets *secrets = nullptr);
            /* Replace the object's attributes with `attrs` (LDAP semantics).
             * With mergeAttrs=true, overlay `attrs` onto the existing ones
             * instead (partial update) — used for member/memberOf and single
             * field edits so unrelated attributes (sAMAccountName, objectSid…)
             * are preserved. */
            void modify(const uuid::uuid &guid, const AttrMap &attrs,
                        const Secrets *secrets = nullptr, bool mergeAttrs = false);
            void remove(const uuid::uuid &guid);
            /* next RID from the domain head's pool */
            uint32_t allocateRid();
Loading