Commit 301c75f5 authored by jan.koester's avatar jan.koester
Browse files

test

parent e2272978
Loading
Loading
Loading
Loading
Loading
+1 −1
Changes for Dockerfile: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
# images from another registry/namespace -- see DOCKER_PREFIX in systempp/build-chroot.sh.

ARG IMAGE_PREFIX=tuxist/
ARG FENRIROS_TAG=20261023
ARG FENRIROS_TAG=20261025

FROM ${IMAGE_PREFIX}fenriros-devel:${FENRIROS_TAG} AS build

+6 −1
Changes for data/admin.html: 6 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -289,6 +289,7 @@ function createUserForm() {
    html += '<option value="mitarbeiter">Mitarbeiter</option>';
    html += '<option value="admin">Admin</option>';
    html += '</select></td></tr>';
    html += '<tr><td><label for="windowslogin">Windows Login:</label></td><td><input type="checkbox" name="windowslogin" id="cu_windowslogin"> <span class="hint">nur anhaken, wenn der Benutzer sich an Windows anmelden soll (vergibt eine SID)</span></td></tr>';
    html += '<tr><td><label for="avatar">ProfilBild:</label></td><td><input type="file" name="avatar" id="cu_avatar"></td></tr>';
    html += '<tr><td></td><td><input type="button" value="Create" onclick="submitCreateUser()"></td></tr>';
    html += '</table></form>';
@@ -303,6 +304,8 @@ function submitCreateUser() {
    formData.append("mail", document.getElementById("cu_mail").value);
    formData.append("pwhash", document.getElementById("cu_password").value);
    formData.append("role", document.getElementById("cu_role").value);
    if (document.getElementById("cu_windowslogin").checked)
        formData.append("windowslogin", "1");
    var avatar = document.getElementById("cu_avatar").files[0];
    if (avatar) formData.append("avatar", avatar);

@@ -686,6 +689,7 @@ function createGroupForm() {
            }
        }
        html += '</select></td></tr>';
        html += '<tr><td><label for="cg_windowslogin">Windows Login:</label></td><td><input type="checkbox" id="cg_windowslogin"> <span class="hint">nur anhaken, wenn die Gruppe als Windows-Sicherheitsgruppe (mit SID) angelegt werden soll</span></td></tr>';
        html += '<tr><td></td><td><input type="button" value="Create group" onclick="submitCreateGroup()"></td></tr>';
        html += '</table></form>';
        setContent(html);
@@ -700,7 +704,8 @@ function submitCreateGroup() {
    }
    var obj = {
        groupname: document.getElementById("cg_name").value,
        groupmembers: members
        groupmembers: members,
        windowslogin: document.getElementById("cg_windowslogin").checked ? 1 : 0
    };
    ajaxPost("/settings/" + currentDomain + "/api/creategroup", obj, function(err, data) {
        if (err) { showError(err); return; }
+105 −0
Changes for plugin/ad/ad.cpp: 105 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -396,6 +396,8 @@ void ActiveDirectory::run() {
    _Running = true;
    _CldapThread = std::thread([this]() {
        while (_Running) {
            if (!waitReadable(_Cldap->fd(), 500))
                continue;
            std::vector<std::vector<uint8_t>> dgrams;
            std::vector<sockaddr_storage> addrs;
            size_t n = 0;
@@ -421,6 +423,109 @@ std::string ActiveDirectory::baseDN(const std::string &domain) const {
    return dsdb::dnFromDomain(domain).str();
}

/* Create a Windows-login-capable user (objectSid + Kerberos secrets) in the
 * live directory of a domain. Mirrors the Administrator object that
 * Directory::provision mints, but takes its RID from the live pool so the
 * admin GUI and LDAP share one allocator. */
bool ActiveDirectory::createWindowsUser(const std::string &domain, const std::string &sam,
                                        const std::string &displayName,
                                        const std::string &clearPassword, bool admin,
                                        std::string &err) {
    if (sam.empty())            { err = "empty username"; return false; }
    if (clearPassword.empty())  { err = "a password is required for Windows login"; return false; }

    std::lock_guard<std::mutex> lock(_Lock);
    Dom *d = nullptr;
    for (auto &dd : _Domains)
        if (dsdb::iequals(dd.meta.dnsName, domain)) { d = &dd; break; }
    if (!d) { err = "unknown domain '" + domain + "'"; return false; }

    dsdb::Dn users = d->meta.baseDn.child("CN", "Users");
    dsdb::Dn dn    = users.child("CN", sam);
    if (d->dir->byDn(dn) || d->dir->bySamAccountName(sam)) {
        err = "a directory object for '" + sam + "' already exists";
        return false;
    }

    dsdb::AttrMap a;
    a["objectClass"]    = {"top", "person", "organizationalPerson", "user"};
    a["objectCategory"] = {"CN=Person,CN=Schema,CN=Configuration," + d->meta.baseDn.str()};
    uint32_t rid = d->dir->allocateRid();
    a["objectSid"]        = {d->meta.sid.withRid(rid).binary()};
    a["sAMAccountName"]   = {sam};
    a["userPrincipalName"]= {sam + "@" + d->meta.dnsName};
    a["name"]             = {sam};
    a["cn"]               = {sam};
    a["distinguishedName"]= {dn.str()};
    if (!displayName.empty()) a["displayName"] = {displayName};
    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, "accountExpires", dsdb::NEVER);
    if (admin)
        a["memberOf"] = {"CN=Domain Admins," + users.str(),
                         "CN=Administrators,CN=Builtin," + d->meta.baseDn.str()};

    std::string salt = dsdb::kerberosSalt(dsdb::PrincipalKind::User,
                                          d->meta.realm, sam, d->meta.dnsName);
    dsdb::Secrets sec = dsdb::deriveSecrets(clearPassword, salt, 0);
    dsdb::setInt(a, "msDS-KeyVersionNumber", sec.kvno);
    dsdb::setInt(a, "pwdLastSet", sec.pwdLastSet);

    try {
        d->dir->create(dn, a, &sec);
    } catch (std::exception &e) {
        err = e.what();
        return false;
    }
    std::cerr << "AD: created Windows user '" << sam << "' in " << d->meta.dnsName
              << " (RID " << rid << ")" << std::endl;
    return true;
}

/* Create a Windows security group (objectSid from the RID pool) in a domain's
 * live directory. Mirrors the universal security groups in Directory::provision. */
bool ActiveDirectory::createWindowsGroup(const std::string &domain, const std::string &sam,
                                         std::string &err) {
    if (sam.empty()) { err = "empty group name"; return false; }

    std::lock_guard<std::mutex> lock(_Lock);
    Dom *d = nullptr;
    for (auto &dd : _Domains)
        if (dsdb::iequals(dd.meta.dnsName, domain)) { d = &dd; break; }
    if (!d) { err = "unknown domain '" + domain + "'"; return false; }

    dsdb::Dn users = d->meta.baseDn.child("CN", "Users");
    dsdb::Dn dn    = users.child("CN", sam);
    if (d->dir->byDn(dn) || d->dir->bySamAccountName(sam)) {
        err = "a directory object for '" + sam + "' already exists";
        return false;
    }

    dsdb::AttrMap a;
    a["objectClass"]      = {"top", "group"};
    a["objectCategory"]   = {"CN=Group,CN=Schema,CN=Configuration," + d->meta.baseDn.str()};
    uint32_t rid = d->dir->allocateRid();
    a["objectSid"]        = {d->meta.sid.withRid(rid).binary()};
    a["sAMAccountName"]   = {sam};
    a["name"]             = {sam};
    a["cn"]               = {sam};
    a["distinguishedName"]= {dn.str()};
    dsdb::setInt(a, "groupType", int32_t(dsdb::GROUP_UNIVERSAL | dsdb::GROUP_SECURITY));
    dsdb::setInt(a, "sAMAccountType", dsdb::SAM_GROUP_OBJECT);

    try {
        d->dir->create(dn, a, nullptr);
    } catch (std::exception &e) {
        err = e.what();
        return false;
    }
    std::cerr << "AD: created Windows group '" << sam << "' in " << d->meta.dnsName
              << " (RID " << rid << ")" << std::endl;
    return true;
}

/******************************************************************************
 * Domain loading: every domain of the registry becomes a provisioned
 * directory naming context.
+6 −0
Changes for plugin/ad/ad.h: 6 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -270,6 +270,12 @@ namespace fenrirds {

        /* AdPlugin interface */
        std::string baseDN(const std::string &domain) const override;
        bool createWindowsUser(const std::string &domain, const std::string &sam,
                               const std::string &displayName,
                               const std::string &clearPassword, bool admin,
                               std::string &err) override;
        bool createWindowsGroup(const std::string &domain, const std::string &sam,
                                std::string &err) override;

        /* netplus::eventapi overrides */
        void RequestEvent(netplus::con &curcon, const int tid, ULONG_PTR args) override;
+6 −5
Changes for plugin/dns/dns.cpp: 6 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -334,15 +334,14 @@ void DnsServer::run() {
    /* TCP responder in its own thread (prefixed with a 2-byte length) */
    _TcpThread = std::thread([this]() {
        while (_Running) {
            if (!waitReadable(_Tcp->fd(), 500))
                continue;
            std::unique_ptr<netplus::socket> cs;
            try {
                _Tcp->accept(cs, true);
            } catch (...) { cs.reset(); }
            if (!cs) {
                struct timespec ts{0, 20 * 1000 * 1000};
                nanosleep(&ts, nullptr);
            if (!cs)
                continue;
            }
            try {
                netplus::buffer in(4096);
                size_t n = cs->recvData(in);
@@ -364,8 +363,10 @@ void DnsServer::run() {
        }
    });

    /* UDP request/response loop (blocking recvmmsg) */
    /* UDP request/response loop (recvmmsg is non-blocking, so poll first) */
    while (_Running) {
        if (!waitReadable(_Udp->fd(), 500))
            continue;
        std::vector<std::vector<uint8_t>> dgrams;
        std::vector<sockaddr_storage> addrs;
        size_t n = 0;
Loading