Commit 32b9089b authored by jan.koester's avatar jan.koester
Browse files

tset

parent 83fbe89b
Loading
Loading
Loading
Loading
+1 −1
Changes for CMakeLists.txt: 1 added line, 1 removed line.
Original line number Diff line number Diff line
cmake_minimum_required(VERSION 3.16)
project(adconnect VERSION 0.2.0 LANGUAGES CXX)
project(adconnect VERSION 0.2.1 LANGUAGES CXX)

# adconnect: the users and groups of an Active Directory domain for
# systempp's user lookups (getpwnam(), getgrouplist(), ... -- see
+29 −2
Changes for src/daemon.cpp: 29 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -292,8 +292,10 @@ void serve(Server &s, std::unique_ptr<netplus::socket> c) {
void usage() {
    std::cerr << "usage: adconnectd [-c config] [-n | -t | -q \"REQUEST\"]\n"
                 "       adconnectd -j DOMAIN -u ADMIN [-s SECRETDIR]   (the password on stdin)\n"
                 "       adconnectd [-c config] -l DOMAIN -u ADMIN      (the password on stdin)\n"
                 "  -j  join DOMAIN: a computer account for this system, made with the\n"
                 "      administrator's password; prints the settings for adconnect.yaml\n"
                 "  -l  leave DOMAIN: its computer account deleted (an administrator's password)\n"
                 "  -n  only read the configuration (no network), then exit\n"
                 "  -t  connect and bind to a domain controller of each domain, then exit\n"
                 "  -q  answer one request (\"PWNAM jan\", \"GROUPS jan\", ...) and exit\n";
@@ -307,12 +309,13 @@ int main(int argc, char **argv) {

    std::string configPath = ADCONNECT_CONFIG, query;
    bool check = false, parseOnly = false;
    std::string joinDomainName, joinAdmin, secretDir = "/etc/adconnect";
    std::string joinDomainName, joinAdmin, leaveDomainName, secretDir = "/etc/adconnect";
    int opt;
    while ((opt = getopt(argc, argv, "c:ntq:j:u:s:h")) != -1) {
    while ((opt = getopt(argc, argv, "c:ntq:j:l:u:s:h")) != -1) {
        switch (opt) {
        case 'c': configPath = optarg; break;
        case 'j': joinDomainName = optarg; break;
        case 'l': leaveDomainName = optarg; break;
        case 'u': joinAdmin = optarg; break;
        case 's': secretDir = optarg; break;
        case 'n': parseOnly = true; break;
@@ -355,6 +358,30 @@ int main(int argc, char **argv) {
        for (const Config &c : configs) std::cout << "adconnectd: " << c.domain << " (" << c.base << ")" << std::endl;
        return 0;
    }
    if (!leaveDomainName.empty()) {
        if (joinAdmin.empty()) {
            usage();
            return 2;
        }
        const Config *d = nullptr;
        for (const Config &c : configs)
            if (c.domain == leaveDomainName) d = &c;
        if (!d) {
            std::cerr << "adconnectd: no domain " << leaveDomainName << " in " << configPath << std::endl;
            return 1;
        }
        std::string password;
        std::getline(std::cin, password);
        while (!password.empty() && (password.back() == '\r' || password.back() == '\n')) password.pop_back();
        try {
            bool deleted = leaveDomain(*d, d->krbPrincipal, joinAdmin, password);
            std::cout << "deleted=" << (deleted ? "yes" : "no") << std::endl;
        } catch (const std::exception &e) {
            std::cerr << "adconnectd: leaving " << leaveDomainName << ": " << e.what() << std::endl;
            return 1;
        }
        return 0;
    }
    const Config &cfg = configs.front();
    Server s{ cfg, {} };
    for (const Config &c : configs) s.domains.push_back(std::make_unique<Domain>(c));
+5 −0
Changes for src/directory.cpp: 5 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -136,6 +136,11 @@ bool LdapSource::ownKey(int etype, krb5::Key &key) {
    return true;
}

void LdapSource::remove(const std::string &dn, bool treeDelete) {
    ensure();
    _conn.remove(dn, treeDelete);
}

void LdapSource::add(const std::string &dn, const ldap::Connection::Attributes &attrs) {
    ensure();
    _conn.add(dn, attrs);
+1 −0
Changes for src/directory.h: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ public:
    void add(const std::string &dn, const ldap::Connection::Attributes &attrs);
    void modify(const std::string &dn,
                const std::vector<std::pair<ldap::Connection::ModOp, ldap::Connection::Attributes::value_type>> &changes);
    void remove(const std::string &dn, bool treeDelete);

private:
    void ensure();
+44 −7
Changes for src/join.cpp: 44 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -204,14 +204,29 @@ JoinResult joinWith(const Config &admin, const std::string &hostname, const std:
    return r;
}

JoinResult joinDomain(const std::string &domainIn, const std::string &adminIn, const std::string &adminPassword,
                      const std::string &secretDir, int timeoutSec) {
    std::string domain = lower(domainIn);
    while (!domain.empty() && domain.back() == '.') domain.pop_back();
    if (domain.empty()) throw std::runtime_error("no domain");
bool leaveWith(const Config &admin, const std::string &account) {
    LdapSource src(admin);
    src.check();
    std::string dn;
    src.search(admin.base, ldap::Subtree, "(&(objectClass=computer)(sAMAccountName=" + ldap::escape(account) + "))",
               { "sAMAccountName" }, [&](const ldap::Entry &e) { dn = e.dn; }, 0);
    if (dn.empty()) return false;
    try {
        src.remove(dn, false);
    } catch (const ldap::Error &e) {
        if (e.code() != 66) writeFailed(e, "deleting " + dn);  /* notAllowedOnNonLeaf: it has entries below */
        try {
            src.remove(dn, true);
        } catch (const ldap::Error &e2) {
            writeFailed(e2, "deleting " + dn + " with what's below it");
        }
    }
    return true;
}

    /* DOMAIN\admin, admin@REALM or admin */
    std::string admin = adminIn, realm = upper(domain);
/* DOMAIN\admin, admin@REALM or admin: the account and its realm */
static void adminPrincipal(const std::string &in, std::string &admin, std::string &realm) {
    admin = in;
    size_t bs = admin.find('\\');
    if (bs != std::string::npos) admin.erase(0, bs + 1);
    size_t at = admin.find('@');
@@ -220,6 +235,28 @@ JoinResult joinDomain(const std::string &domainIn, const std::string &adminIn, c
        admin.erase(at);
    }
    if (admin.empty()) throw std::runtime_error("no administrator account");
}

bool leaveDomain(const Config &domain, const std::string &account, const std::string &adminIn, const std::string &adminPassword) {
    if (account.empty()) throw std::runtime_error(domain.domain + ": no computer account configured (kerberos_principal)");
    Config c = domain;
    c.auth = Auth::Kerberos;
    c.krbRealm = domain.auth == Auth::Kerberos && !domain.krbRealm.empty() ? domain.krbRealm : upper(domain.domain);
    adminPrincipal(adminIn, c.krbPrincipal, c.krbRealm);
    c.bindPassword = adminPassword;
    c.keytab.clear();
    if (c.kdcs.empty()) c.kdcs = c.servers;
    return leaveWith(c, account);
}

JoinResult joinDomain(const std::string &domainIn, const std::string &adminIn, const std::string &adminPassword,
                      const std::string &secretDir, int timeoutSec) {
    std::string domain = lower(domainIn);
    while (!domain.empty() && domain.back() == '.') domain.pop_back();
    if (domain.empty()) throw std::runtime_error("no domain");

    std::string admin, realm = upper(domain);
    adminPrincipal(adminIn, admin, realm);

    Config c;
    c.domain = domain;
Loading