Commit 1cc85528 authored by jan.koester's avatar jan.koester
Browse files

test

parent bfbb6c3e
Loading
Loading
Loading
Loading
+6 −2
Changes for dist/adconnect.yaml: 6 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -42,9 +42,13 @@ adconnect:
  # kdcs: [dc1.example.com, dc2.example.com]

  # ids: rid (id_base + the RID of the account's SID), rfc2307 (uidNumber
  # and gidNumber; accounts without them are left out) or guid (the
  # and gidNumber; accounts without them are left out), guid (the
  # objectGUID as a GUID id: only programs built for <sys/uid_r.h> see it,
  # the others the overflow id)
  # the others the overflow id) or uuid (FenrirDirectoryServer only: its
  # objectGUID is the account's native UUID, so the id is that UUID). Every
  # file server of a domain needs the same id_mapping (and id_base): file
  # owners are stored as these ids. Switching guid -> uuid changes the ids
  # of all accounts -- see README, "Migrating GUID owners".
  id_mapping: rid
  id_base: 200000
  id_range: 2000000
+2 −1
Changes for src/config.cpp: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -252,7 +252,8 @@ void readDomain(const Scope &s, Config &cfg, bool multi, bool &idBaseSet) {
    if (mapping == "rid") cfg.idMapping = IdMapping::Rid;
    else if (mapping == "rfc2307") cfg.idMapping = IdMapping::Rfc2307;
    else if (mapping == "guid") cfg.idMapping = IdMapping::Guid;
    else throw std::runtime_error(l + "id_mapping: expected rid, rfc2307 or guid, got \"" + mapping + "\"");
    else if (mapping == "uuid") cfg.idMapping = IdMapping::Uuid;
    else throw std::runtime_error(l + "id_mapping: expected rid, rfc2307, guid or uuid, got \"" + mapping + "\"");
    idBaseSet = s.own("id_base");
    readNumber(s, "id_base", cfg.idBase, 1000, 0xfffffffeUL);
    readNumber(s, "id_range", cfg.idRange, 1, 0xfffffffeUL);
+3 −0
Changes for src/config.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -49,6 +49,9 @@ enum class IdMapping {
    Rid,        /* id_base + the RID of the objectSid (classic ids) */
    Rfc2307,    /* the uidNumber / gidNumber attributes */
    Guid,       /* the objectGUID as a FenrirOS GUID id (<sys/uid_r.h>) */
    Uuid,       /* the objectGUID as a FenrirOS UUID id: only for a directory
                 * whose objectGUIDs are native UUIDs (FenrirDirectoryServer
                 * says so in its rootDSE, fenrirObjectGuidIsUuid) */
};

struct Config {
+3 −1
Changes for src/daemon.cpp: 3 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -372,7 +372,9 @@ bool resolve(Server &s, Cmd cmd, const std::string &arg, const std::string &line
        out = End;  /* no domain's name */
        return true;
    case Cmd::PwUid:
    case Cmd::GrGid: {
    case Cmd::GrGid:
    case Cmd::SidOf:
    case Cmd::IdOf: {
        bool failed = false;
        for (auto &d : s.domains) {
            if (!ask(*d, cmd, arg, line, records, error)) {
+135 −5
Changes for src/directory.cpp: 135 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -193,6 +193,34 @@ bool split(const std::string &b, std::string &domain, uint32_t &rid) {
    return true;
}

bool fromString(const std::string &t, std::string &bin) {
    if (t.size() < 5 || t.compare(0, 4, "S-1-") != 0) return false;
    std::vector<unsigned long long> parts;
    size_t p = 4;
    for (;;) {
        size_t q = t.find('-', p);
        std::string part = t.substr(p, q == std::string::npos ? std::string::npos : q - p);
        if (part.empty() || part.size() > 15) return false;
        unsigned long long v = 0;
        for (char c : part) {
            if (c < '0' || c > '9') return false;
            v = v * 10 + (unsigned long long)(c - '0');
        }
        parts.push_back(v);
        if (q == std::string::npos) break;
        p = q + 1;
    }
    if (parts[0] >= 1ULL << 48 || parts.size() - 1 > 15) return false;
    bin.assign(1, (char)1);
    bin += (char)(parts.size() - 1);
    for (int i = 5; i >= 0; i--) bin += (char)(parts[0] >> (8 * i) & 0xff);
    for (size_t i = 1; i < parts.size(); i++) {
        if (parts[i] > 0xffffffffULL) return false;
        for (int k = 0; k < 4; k++) bin += (char)(parts[i] >> (8 * k) & 0xff);
    }
    return true;
}

std::string withRid(const std::string &domain, uint32_t rid) {
    std::string b = domain;
    b[1] = (char)((uint8_t)b[1] + 1);
@@ -230,6 +258,35 @@ bool guidFromText(const std::string &t, std::string &bin) {
    return true;
}

std::string uuidText(const std::string &b) {
    if (b.size() != 16) return std::string();
    const unsigned char *u = (const unsigned char *)b.data();
    if ((u[6] & 0xf0) != 0x40 || (u[8] & 0xc0) != 0x80) return std::string();
    char buf[40];
    snprintf(buf, sizeof buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
             u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]);
    return buf;
}

bool uuidFromText(const std::string &t, std::string &bin) {
    if (t.size() != 36 || t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-') return false;
    std::string digits;
    for (size_t i = 0; i < t.size(); i++)
        if (i != 8 && i != 13 && i != 18 && i != 23) digits += t[i];
    bin.clear();
    for (size_t i = 0; i < digits.size(); i += 2) {
        int v = 0;
        for (int k = 0; k < 2; k++) {
            char c = digits[i + k];
            int h = c >= '0' && c <= '9' ? c - '0' : c >= 'a' && c <= 'f' ? c - 'a' + 10 : c >= 'A' && c <= 'F' ? c - 'A' + 10 : -1;
            if (h < 0) return false;
            v = v << 4 | h;
        }
        bin += (char)v;
    }
    return !uuidText(bin).empty();
}

/* ---- Directory ---- */

std::vector<ldap::Entry> Directory::find(const std::string &filter, const std::vector<std::string> &attrs) {
@@ -281,6 +338,20 @@ const std::string &Directory::domainSid() {
    return _domainSid;
}

void Directory::nativeUuid() {
    if (_nativeUuid) return;
    bool yes = false;
    _src.search("", ldap::Base, "(objectClass=*)", { "fenrirObjectGuidIsUuid" }, [&](const ldap::Entry &e) {
        const std::string *v = e.first("fenrirobjectguidisuuid");
        yes = v && lower(*v) == "true";
    }, 0);
    if (!yes)
        throw std::runtime_error("id_mapping: uuid, but " + _cfg.domain +
                                 "'s directory doesn't hand out UUIDs (no fenrirObjectGuidIsUuid in its rootDSE)"
                                 " -- id_mapping: guid is for other directories");
    _nativeUuid = true;
}

std::string Directory::idOf(const ldap::Entry &e, bool group) {
    switch (_cfg.idMapping) {
    case IdMapping::Rid: {
@@ -299,6 +370,11 @@ std::string Directory::idOf(const ldap::Entry &e, bool group) {
        const std::string *g = e.first("objectguid");
        return g ? guidText(*g) : std::string();
    }
    case IdMapping::Uuid: {
        nativeUuid();
        const std::string *g = e.first("objectguid");
        return g ? uuidText(*g) : std::string();
    }
    }
    return std::string();
}
@@ -315,7 +391,7 @@ std::string Directory::primaryGid(const ldap::Entry &user) {
    if (_cfg.idMapping == IdMapping::Rid)
        return rid < _cfg.idRange ? std::to_string(_cfg.idBase + rid) : std::string();

    /* Guid: the primary group's objectGUID */
    /* Guid, Uuid: the primary group's objectGUID */
    auto it = _primaryGids.find((uint32_t)rid);
    if (it != _primaryGids.end()) return it->second;
    std::string gid;
@@ -344,6 +420,12 @@ std::string Directory::idFilter(const std::string &id, bool group) {
        if (!guidFromText(id, bin)) return std::string();
        return "(objectGUID=" + ldap::escape(bin) + ")";
    }
    case IdMapping::Uuid: {
        std::string bin;
        if (!uuidFromText(id, bin)) return std::string();
        nativeUuid();
        return "(objectGUID=" + ldap::escape(bin) + ")";
    }
    }
    return std::string();
}
@@ -490,10 +572,18 @@ std::string Directory::groupsOf(const std::string &n) {
    };
    add(primaryGid(user));
    if (sids.empty()) {
        /* no tokenGroups (not readable for the bind account): the groups
         * naming the user as a direct member */
        for (const ldap::Entry &g : find(groupFilter("(member=" + ldap::escape(user.dn) + ")"), GroupAttrs))
            add(idOf(g, true));
        /* no tokenGroups (not readable for the bind account, or a directory
         * without them): the groups the user is in, nested ones too, by
         * LDAP_MATCHING_RULE_IN_CHAIN -- only direct memberships where a
         * server refuses that rule */
        std::vector<ldap::Entry> groups;
        try {
            groups = find(groupFilter("(member:1.2.840.113556.1.4.1941:=" + ldap::escape(user.dn) + ")"), GroupAttrs);
        } catch (const ldap::Error &e) {
            if (e.code() == -1) throw;
            groups = find(groupFilter("(member=" + ldap::escape(user.dn) + ")"), GroupAttrs);
        }
        for (const ldap::Entry &g : groups) add(idOf(g, true));
        return out + End;
    }
    for (size_t i = 0; i < sids.size(); i += SidsPerSearch) {
@@ -506,6 +596,44 @@ std::string Directory::groupsOf(const std::string &n) {
    return out + End;
}

/* SIDOF u|g <id>: the objectSid of the account with this id. Only the
 * account's own id counts (idOf() of the entry found must give it back):
 * no name matching, no RID arithmetic beyond what id_mapping defines. */
std::string Directory::sidOf(const std::string &arg) {
    if (arg.size() < 3 || (arg[0] != 'u' && arg[0] != 'g') || arg[1] != ' ') return End;
    bool group = arg[0] == 'g';
    std::string id = arg.substr(2);
    std::string f = idFilter(id, group);
    if (f.empty()) return End;
    for (const ldap::Entry &e : find(group ? groupFilter(f) : userFilter(f), group ? GroupAttrs : UserAttrs)) {
        User u;
        Group g;
        if (group ? !toGroup(e, g) || g.gid != id : !toUser(e, u) || u.uid != id) continue;
        const std::string *s = e.first("objectsid");
        if (!s || !sidValid(*s)) return End;  /* an account without one: its S-1-22 SID stays */
        return formatSid(SidMap{ group, id, sid::toString(*s) }) + End;
    }
    return End;
}

/* IDOF <SID>: the id of the user or group with this objectSid -- only SIDs
 * of our domain are looked up */
std::string Directory::idOfSid(const std::string &text) {
    std::string bin, domain;
    uint32_t rid;
    if (!sid::fromString(text, bin) || !sid::split(bin, domain, rid) || domain != domainSid()) return End;
    std::string f = "(objectSid=" + ldap::escape(bin) + ")";
    for (const ldap::Entry &e : find(userFilter(f), UserAttrs)) {
        User u;
        if (toUser(e, u)) return formatSid(SidMap{ false, u.uid, sid::toString(bin) }) + End;
    }
    for (const ldap::Entry &e : find(groupFilter(f), GroupAttrs)) {
        Group g;
        if (toGroup(e, g)) return formatSid(SidMap{ true, g.gid, sid::toString(bin) }) + End;
    }
    return End;
}

std::string Directory::handle(Cmd cmd, const std::string &arg) {
    switch (cmd) {
    case Cmd::PwNam: return userByName(arg);
@@ -515,6 +643,8 @@ std::string Directory::handle(Cmd cmd, const std::string &arg) {
    case Cmd::GrGid: return groupById(arg);
    case Cmd::GrEnt: return allGroups();
    case Cmd::Groups: return groupsOf(arg);
    case Cmd::SidOf: return sidOf(arg);
    case Cmd::IdOf: return idOfSid(arg);
    case Cmd::Krb5:
    case Cmd::Ntlm:
    case Cmd::Auth:
Loading