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

test

parent a6c56836
Loading
Loading
Loading
Loading
Loading
+54 −4
Changes for src/dsdb/secdesc.cpp: 54 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -35,11 +35,12 @@ namespace {
            ace.push_back(char(a.flags));
            put16(ace, 0);                               /* size, set below */
            put32(ace, a.mask);
            if (a.type == ACCESS_ALLOWED_OBJECT) {
            if (a.type == ACCESS_ALLOWED_OBJECT || a.type == ACCESS_DENIED_OBJECT) {
                object = true;
                put32(ace, a.objectType.size() == 16 ? 1u : 0u);   /* ACE_OBJECT_TYPE_PRESENT */
                if (a.objectType.size() == 16)
                    ace += a.objectType;
                bool ot = a.objectType.size() == 16, iot = a.inheritedObjectType.size() == 16;
                put32(ace, (ot ? 1u : 0u) | (iot ? 2u : 0u));   /* ACE_(INHERITED_)OBJECT_TYPE_PRESENT */
                if (ot) ace += a.objectType;
                if (iot) ace += a.inheritedObjectType;
            }
            ace += sid;
            ace[2] = char(ace.size() & 0xff);
@@ -83,6 +84,55 @@ std::string build(const Descriptor &d, uint32_t parts) {
    return o;
}

bool parse(const std::string &s, Descriptor &out) {
    out = Descriptor();
    if (s.size() < 20 || s[0] != 1)
        return false;
    uint16_t control = get16(s, 2);
    if (!(control & SE_SELF_RELATIVE))
        return false;
    out.daclProtected = (control & SE_DACL_PROTECTED) != 0;
    auto sidAt = [&](uint32_t off, Sid &sid) {
        if (!off || off >= s.size()) return;
        Sid::fromBinary(reinterpret_cast<const uint8_t*>(s.data()) + off, s.size() - off, sid);
    };
    sidAt(get32(s, 4), out.owner);
    sidAt(get32(s, 8), out.group);
    uint32_t dacl = get32(s, 16);
    if (!(control & SE_DACL_PRESENT) || !dacl)
        return true;
    if (dacl + 8 > s.size())
        return false;
    uint16_t count = get16(s, dacl + 4);
    size_t o = dacl + 8;
    for (uint16_t i = 0; i < count; ++i) {
        if (o + 8 > s.size()) return false;
        Ace a;
        a.type = uint8_t(s[o]);
        a.flags = uint8_t(s[o + 1]);
        uint16_t size = get16(s, o + 2);
        if (size < 8 || o + size > s.size()) return false;
        a.mask = get32(s, o + 4);
        size_t p = o + 8, end = o + size;
        if (a.type == ACCESS_ALLOWED_OBJECT || a.type == ACCESS_DENIED_OBJECT) {
            if (p + 4 > end) return false;
            uint32_t f = get32(s, p);
            p += 4;
            if (f & 1) { if (p + 16 > end) return false; a.objectType = s.substr(p, 16); p += 16; }
            if (f & 2) { if (p + 16 > end) return false; a.inheritedObjectType = s.substr(p, 16); p += 16; }
        } else if (a.type != ACCESS_ALLOWED && a.type != ACCESS_DENIED) {
            o = end;                                     /* callback/audit ACEs: skipped */
            continue;
        }
        size_t used = 0;
        if (!Sid::fromBinary(reinterpret_cast<const uint8_t*>(s.data()) + p, end - p, a.sid, &used))
            return false;
        out.dacl.push_back(a);
        o = end;
    }
    return true;
}

std::string select(const std::string &s, uint32_t parts) {
    if (s.size() < 20 || s[0] != 1)
        return s;
+6 −0
Changes for src/dsdb/secdesc.h: 6 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ namespace fenrirds {
                ACCESS_ALLOWED        = 0x00,
                ACCESS_DENIED         = 0x01,
                ACCESS_ALLOWED_OBJECT = 0x05,
                ACCESS_DENIED_OBJECT  = 0x06,
            };
            enum AceFlags : uint8_t {
                OBJECT_INHERIT    = 0x01,
@@ -59,6 +60,8 @@ namespace fenrirds {
                /* ACCESS_ALLOWED_OBJECT: an extended right / property set
                 * (16 bytes, Windows GUID byte order); empty = none */
                std::string objectType;
                /* object ACEs: the object class the ACE is inherited to */
                std::string inheritedObjectType;
            };

            struct Descriptor {
@@ -69,6 +72,9 @@ namespace fenrirds {

            /* self-relative bytes; `parts` selects owner/group/dacl */
            std::string build(const Descriptor &d, uint32_t parts = INFO_OWNER | INFO_GROUP | INFO_DACL);
            /* parse a self-relative SD (owner, group, DACL; the SACL is
             * dropped); false if it is not one */
            bool parse(const std::string &sd, Descriptor &out);
            /* strip a stored self-relative SD down to the requested parts
             * (returns it unchanged if it cannot be parsed) */
            std::string select(const std::string &sd, uint32_t parts);
+84 −0
Changes for src/grouppolicy.cpp: 84 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -412,6 +412,90 @@ bool Manager::setFlags(const std::string &guid, uint32_t flags, std::string &err
    return true;
}

namespace {
    bool isApplyAce(const dsdb::sd::Ace &a) {
        static const std::string apply = dsdb::sd::guidBytes(dsdb::sd::RIGHT_APPLY_GROUP_POLICY);
        return a.type == dsdb::sd::ACCESS_ALLOWED_OBJECT && (a.mask & dsdb::sd::DS_CONTROL_ACCESS) &&
               a.objectType == apply;
    }
    /* a plain "Read" grant as GPMC writes it for a filter trustee */
    bool isReadAce(const dsdb::sd::Ace &a) {
        return a.type == dsdb::sd::ACCESS_ALLOWED && a.mask == dsdb::sd::DS_GENERIC_READ;
    }
}

bool Manager::securityFilter(const std::string &guid, std::vector<dsdb::Sid> &out) {
    out.clear();
    auto e = _Dir.byDn(gpoDn(guid));
    if (!e) return false;
    dsdb::sd::Descriptor d;
    std::string raw = e->str("nTSecurityDescriptor");
    if (!dsdb::sd::parse(raw, d)) return false;
    for (auto &a : d.dacl)
        if (isApplyAce(a) && std::find(out.begin(), out.end(), a.sid) == out.end())
            out.push_back(a.sid);
    return true;
}

bool Manager::setSecurityFilter(const std::string &guid, const std::vector<dsdb::Sid> &sids,
                                std::string &err) {
    auto e = _Dir.byDn(gpoDn(guid));
    if (!e) { err = "no such GPO"; return false; }
    dsdb::sd::Descriptor d;
    if (!dsdb::sd::parse(e->str("nTSecurityDescriptor"), d)) {
        /* none or unreadable: start from the default */
        dsdb::Sid domSid = _Dir.meta().sid;
        if (auto head = _Dir.byDn(_Dir.meta().baseDn))
            if (!head->objectSid.empty()) domSid = head->objectSid;
        d = dsdb::sd::gpoContainer(domSid);
    }
    auto inNew = [&](const dsdb::Sid &s) { return std::find(sids.begin(), sids.end(), s) != sids.end(); };
    std::vector<dsdb::Sid> old;
    for (auto &a : d.dacl)
        if (isApplyAce(a)) old.push_back(a.sid);
    const dsdb::Sid au = dsdb::wk::authenticatedUsers(), edc = dsdb::wk::enterpriseDCs();
    std::vector<dsdb::sd::Ace> dacl;
    for (auto &a : d.dacl) {
        if (isApplyAce(a))
            continue;                                   /* re-added below */
        /* the Read grant of a trustee leaving the filter goes with it */
        if (isReadAce(a) && !inNew(a.sid) && a.sid != au && a.sid != edc &&
            std::find(old.begin(), old.end(), a.sid) != old.end())
            continue;
        dacl.push_back(a);
    }
    auto hasRead = [&](const dsdb::Sid &s) {
        for (auto &a : dacl)
            if (a.type == dsdb::sd::ACCESS_ALLOWED && a.sid == s &&
                (a.mask & dsdb::sd::DS_READ_PROP) && (a.mask & dsdb::sd::DS_LIST))
                return true;
        return false;
    };
    auto readAce = [](const dsdb::Sid &s) {
        dsdb::sd::Ace a;
        a.sid = s; a.mask = dsdb::sd::DS_GENERIC_READ; a.flags = dsdb::sd::CONTAINER_INHERIT;
        return a;
    };
    if (!hasRead(au))
        dacl.push_back(readAce(au));
    for (auto &s : sids) {
        if (!hasRead(s))
            dacl.push_back(readAce(s));
        dsdb::sd::Ace apply;
        apply.type = dsdb::sd::ACCESS_ALLOWED_OBJECT;
        apply.flags = dsdb::sd::CONTAINER_INHERIT;
        apply.mask = dsdb::sd::DS_CONTROL_ACCESS;
        apply.sid = s;
        apply.objectType = dsdb::sd::guidBytes(dsdb::sd::RIGHT_APPLY_GROUP_POLICY);
        dacl.push_back(apply);
    }
    d.dacl = dacl;
    dsdb::AttrMap a;
    a["nTSecurityDescriptor"] = {dsdb::sd::build(d)};
    try { _Dir.modify(e->objectGuid, a, nullptr, true); } catch (const std::exception &ex) { err = ex.what(); return false; }
    return true;
}

std::vector<dsdb::Dn> Manager::linkTargets() {
    const dsdb::Dn &base = _Dir.meta().baseDn;
    std::vector<dsdb::Dn> out{base};
+9 −0
Changes for src/grouppolicy.h: 9 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <vector>

#include "dsdb/dn.h"
#include "dsdb/sid.h"

namespace fenrirds {
    namespace dsdb { class Directory; }
@@ -80,6 +81,14 @@ namespace fenrirds {
            bool registry(const std::string &guid, bool machine, std::vector<RegValue> &out);
            bool setRegistry(const std::string &guid, bool machine,
                             const std::vector<RegValue> &values, std::string &err);
            /* security filtering: the trustees the GPO applies to (the ACEs
             * granting the Apply-Group-Policy right on its container) */
            bool securityFilter(const std::string &guid, std::vector<dsdb::Sid> &out);
            /* make exactly `sids` the trustees: each gets Read + Apply Group
             * Policy; removed ones lose both. Authenticated Users keeps Read
             * in any case, so computers can still read the GPO (MS16-072) */
            bool setSecurityFilter(const std::string &guid, const std::vector<dsdb::Sid> &sids,
                                   std::string &err);

        private:
            dsdb::Dn policiesDn() const;