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

test

parent ce630f9b
Loading
Loading
Loading
Loading
Loading
+52 −0
Changes for data/dirviews.js: 52 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -186,6 +186,7 @@ function loadWinGpos() {
                if (!g.links.length) h += '<em>-</em>';
                h += '</td><td><ul>';
                h += '<li><a class="button" href="#" onclick="loadWinGpoRegistry(' + i + ', \'machine\'); return false;">Registry</a></li>';
                h += '<li><a class="button" href="#" onclick="loadWinGpoFilter(' + i + '); return false;">Sicherheitsfilter</a></li>';
                h += '<li><a class="button" href="#" onclick="winGpoRename(' + i + '); return false;">Rename</a></li>';
                if (!g.builtin)
                    h += '<li><a class="button danger" href="#" onclick="winGpoDelete(' + i + '); return false;">Delete</a></li>';
@@ -237,6 +238,57 @@ function winGpoLink(i, k, what) {
    dirPost("wingpolink", gpoId(g.guid), f, function() { loadWinGpos(); });
}

/* ---- security filtering: which groups a GPO applies to ---- */
var g_filterGpo = -1, g_filter = [], g_filterCandidates = [];

function loadWinGpoFilter(i) {
    g_filterGpo = i;
    dirPost("wingpofilter", gpoId(g_winGpos[i].guid), {}, function(data) {
        g_filter = data.filter || [];
        g_filterCandidates = data.candidates || [];
        renderWinGpoFilter();
    });
}

function renderWinGpoFilter() {
    var g = g_winGpos[g_filterGpo];
    var h = '<h3>' + escapeHtml(g.name) + ' &ndash; Sicherheitsfilterung</h3>';
    h += '<p><a href="#" onclick="loadWinGpos(); return false;">&larr; alle GPOs</a></p>';
    h += '<p class="hint">Die GPO wird nur auf Benutzer und Computer angewendet, die Mitglied einer der hier aufgeführten Gruppen sind (Recht &bdquo;Gruppenrichtlinie übernehmen&ldquo;). Standard ist &bdquo;Authenticated Users&ldquo; = alle. Für Computerrichtlinien muss das Computerkonto Mitglied sein, für Benutzerrichtlinien der Benutzer. Lesen dürfen die GPO weiterhin alle authentifizierten Konten.</p>';
    h += '<table class="admin-table userlist"><tr><th>Gruppe</th><th>SID</th><th></th></tr>';
    for (var i = 0; i < g_filter.length; i++) {
        var f = g_filter[i];
        h += '<tr><td>' + escapeHtml(f.name) + '</td><td><small>' + escapeHtml(f.sid) + '</small></td>';
        h += '<td><a class="button danger" href="#" onclick="g_filter.splice(' + i + ',1); renderWinGpoFilter(); return false;">&times;</a></td></tr>';
    }
    if (!g_filter.length) h += '<tr><td colspan="3"><em>niemand &ndash; die GPO wird auf keinen Client angewendet</em></td></tr>';
    h += '</table>';
    h += '<form onsubmit="winGpoFilterAdd(this); return false;"><label>Gruppe hinzufügen <select name="sid">';
    g_filterCandidates.forEach(function(c) {
        if (g_filter.some(function(f) { return f.sid === c.sid; })) return;
        h += '<option value="' + escapeHtml(c.sid) + '">' + escapeHtml(c.name) + '</option>';
    });
    h += '</select></label> <input type="submit" class="button" value="Hinzufügen"></form>';
    h += '<p><a class="button" href="#" onclick="saveWinGpoFilter(); return false;">Speichern</a></p>';
    setContent(h);
}

function winGpoFilterAdd(f) {
    var sid = f.sid.value;
    var c = g_filterCandidates.filter(function(x) { return x.sid === sid; })[0];
    if (c) g_filter.push(c);
    renderWinGpoFilter();
}

function saveWinGpoFilter() {
    var g = g_winGpos[g_filterGpo];
    if (!g_filter.length && !confirm('Ohne Einträge wird die GPO auf niemanden angewendet. Trotzdem speichern?')) return;
    dirPost("wingposetfilter", gpoId(g.guid), {sids: JSON.stringify(g_filter.map(function(f) { return f.sid; }))}, function() {
        alert('Gespeichert. Die Clients übernehmen die Änderung bei der nächsten Richtlinienaktualisierung (gpupdate).');
        loadWinGpoFilter(g_filterGpo);
    });
}

/* ---- Registry.pol editor ---- */
var REG_TYPES = ['REG_DWORD', 'REG_SZ', 'REG_EXPAND_SZ', 'REG_MULTI_SZ', 'REG_QWORD', 'REG_BINARY'];
var g_regGpo = -1, g_regPart = 'machine', g_regValues = [];
+58 −0
Changes for src/dirapi.cpp: 58 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -494,6 +494,64 @@ json_object *winGpo(const std::string &action, const std::string &domain,
            }
            json_object_object_add(result, "values", arr);
            return;
        } else if (action == "wingpofilter") {
            /* security filtering: current trustees + the groups to choose from */
            std::vector<dsdb::Sid> sids;
            if (!gm.securityFilter(guid, sids)) throw std::runtime_error("no such GPO");
            auto nameOf = [&](const dsdb::Sid &sid) -> std::string {
                if (sid == dsdb::wk::authenticatedUsers()) return "Authenticated Users";
                if (sid == dsdb::wk::enterpriseDCs()) return "Enterprise Domain Controllers";
                if (auto e = dir.bySid(sid)) {
                    std::string n = e->str("sAMAccountName");
                    return n.empty() ? e->dn.rdn().value : n;
                }
                return sid.str();
            };
            auto item = [&](const dsdb::Sid &sid, const std::string &name, const char *kind) {
                json_object *o = json_object_new_object();
                json_object_object_add(o, "sid", json_object_new_string(sid.str().c_str()));
                json_object_object_add(o, "name", json_object_new_string(name.c_str()));
                json_object_object_add(o, "kind", json_object_new_string(kind));
                return o;
            };
            result = json_object_new_object();
            json_object *jf = json_object_new_array();
            for (auto &sid : sids)
                json_object_array_add(jf, item(sid, nameOf(sid), "trustee"));
            json_object *jc = json_object_new_array();
            json_object_array_add(jc, item(dsdb::wk::authenticatedUsers(), "Authenticated Users", "wellknown"));
            std::vector<std::pair<std::string, dsdb::Sid>> groups;
            dir.search(dir.meta().baseDn, 2, dsdb::Filter::equal("objectClass", "group"), [&](const dsdb::Entry &e) {
                if (!e.objectSid.empty())
                    groups.push_back({e.str("sAMAccountName").empty() ? e.dn.rdn().value : e.str("sAMAccountName"),
                                      e.objectSid});
                return true;
            });
            std::sort(groups.begin(), groups.end(), [](const auto &a, const auto &b) {
                return dsdb::lower(a.first) < dsdb::lower(b.first);
            });
            for (auto &g : groups)
                json_object_array_add(jc, item(g.second, g.first, "group"));
            json_object_object_add(result, "filter", jf);
            json_object_object_add(result, "candidates", jc);
            return;
        } else if (action == "wingposetfilter") {
            json_object *jv = json_tokener_parse(field(form, "sids").c_str());
            if (!jv || !json_object_is_type(jv, json_type_array)) {
                if (jv) json_object_put(jv);
                throw std::runtime_error("sids must be a JSON array");
            }
            std::vector<dsdb::Sid> sids;
            bool bad = false;
            for (size_t i = 0; i < json_object_array_length(jv); ++i) {
                dsdb::Sid sid;
                if (!dsdb::Sid::parse(json_object_get_string(json_object_array_get_idx(jv, i)), sid)) { bad = true; break; }
                if (std::find(sids.begin(), sids.end(), sid) == sids.end())
                    sids.push_back(sid);
            }
            json_object_put(jv);
            if (bad) throw std::runtime_error("invalid SID");
            if (!gm.setSecurityFilter(guid, sids, err)) throw std::runtime_error(err);
        } else if (action == "wingposetregistry") {
            bool machine = field(form, "part") != "user";
            json_object *jv = json_tokener_parse(field(form, "values").c_str());
+37 −0
Changes for tests/proto/test_grouppolicy.cpp: 37 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#include "check.h"
#include "backend.h"
#include "dsdb/directory.h"
#include "dsdb/secdesc.h"
#include "grouppolicy.h"
#include "sysvol.h"

@@ -110,6 +111,42 @@ TEST(manager) {
        CHECK(store.read("Policies/" + guid + "/GPT.INI", data));
        CHECK(data.find("Version=65537\r\n") != std::string::npos);

        /* security filtering: default = Authenticated Users */
        std::vector<dsdb::Sid> filter;
        CHECK(gm.securityFilter(guid, filter));
        CHECK_EQ(filter.size(), size_t(1));
        CHECK(filter[0] == dsdb::wk::authenticatedUsers());
        dsdb::Sid domSid = dir.byDn(m.baseDn)->objectSid;
        dsdb::Sid computers = domSid.withRid(515), admins = domSid.withRid(512);
        CHECK(gm.setSecurityFilter(guid, {computers, admins}, err));
        CHECK(gm.securityFilter(guid, filter));
        CHECK_EQ(filter.size(), size_t(2));
        CHECK(filter[0] == computers && filter[1] == admins);
        {
            dsdb::sd::Descriptor d;
            auto e = dir.byDn(m.baseDn.child("CN", "System").child("CN", "Policies").child("CN", guid));
            CHECK(dsdb::sd::parse(e->str("nTSecurityDescriptor"), d));
            bool auRead = false, compRead = false;
            for (auto &a : d.dacl) {
                if (a.type == dsdb::sd::ACCESS_ALLOWED && a.sid == dsdb::wk::authenticatedUsers()) auRead = true;
                if (a.type == dsdb::sd::ACCESS_ALLOWED && a.sid == computers) compRead = true;
            }
            CHECK(auRead);                          /* computers can still read */
            CHECK(compRead);
            /* parse -> build round trip keeps the bytes */
            CHECK(dsdb::sd::build(d) == e->str("nTSecurityDescriptor"));
        }
        CHECK(gm.setSecurityFilter(guid, {dsdb::wk::authenticatedUsers()}, err));
        CHECK(gm.securityFilter(guid, filter));
        CHECK_EQ(filter.size(), size_t(1));
        {
            dsdb::sd::Descriptor d;
            auto e = dir.byDn(m.baseDn.child("CN", "System").child("CN", "Policies").child("CN", guid));
            CHECK(dsdb::sd::parse(e->str("nTSecurityDescriptor"), d));
            for (auto &a : d.dacl)
                CHECK(a.sid != computers);          /* its Read grant left with it */
        }

        CHECK(gm.setName(guid, "Renamed", err));
        CHECK(gm.setFlags(guid, FLAG_USER_DISABLED, err));
        CHECK(gm.get(guid, p));