Commit 65d76a3b authored by jan.koester's avatar jan.koester
Browse files

test

parent 3876ffad
Loading
Loading
Loading
Loading
Loading
+152 −8
Changes for plugin/krb5/krb5.cpp: 152 added lines, 8 removed lines.
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include <random>
#include <algorithm>
#include <memory>
#include <optional>
#include <set>
#include <chrono>
#include <fstream>
@@ -59,6 +60,7 @@
#include "session.h"
#include "crypto/krb5keys.h"
#include "dsdb/pac.h"
#include "crypto/crypto.h"
#include "domains.h"
#include "types.h"
#include "exception.h"
@@ -1332,11 +1334,21 @@ bool Asn1Der::parseKdcReq(const uint8_t *data, size_t len, Krb5KdcReq &req) {
                            }
                            break;
                        }
                        case 11: /* additional-tickets */
                        case 11: /* additional-tickets: SEQUENCE OF Ticket */
                        {
                            /* For TGS-REQ, this may contain the TGT
                             * Actually in standard Kerberos, additional-tickets is
                             * used for user-to-user. The TGT is in the PA-TGS-REQ. */
                            const uint8_t *seq; size_t seqLen; size_t so = 0; uint8_t st;
                            if (!parseTLV(bbcontent, bbcontent_len, so, st, seq, seqLen) || st != TAG_SEQUENCE)
                                break;
                            size_t to = 0;
                            while (to < seqLen) {
                                size_t start = to;
                                uint8_t tt; const uint8_t *tc; size_t tl;
                                if (!parseTLV(seq, seqLen, to, tt, tc, tl))
                                    break;
                                Krb5Ticket t;
                                if (parseTicket(seq + start, to - start, t))
                                    req.additional_tickets.push_back(std::move(t));
                            }
                            break;
                        }
                    }
@@ -3587,6 +3599,7 @@ std::vector<uint8_t> Krb5KDC::handleStandardTgsReq(const uint8_t *data, size_t l
     */
    Krb5ApReq tgsApReq;
    bool foundApReq = false;
    std::vector<uint8_t> paForUser;   /* S4U2Self (MS-SFU PA-FOR-USER, type 129) */

    if (!req.padata.empty()) {
        /* Parse SEQUENCE OF PA-DATA to find PA-TGS-REQ */
@@ -3633,6 +3646,8 @@ std::vector<uint8_t> Krb5KDC::handleStandardTgsReq(const uint8_t *data, size_t l
                    }
                }

                if (pdType == 129 && !pdValue.empty())
                    paForUser = pdValue;
                if (pdType == KRB5_PADATA_TGS_REQ && !pdValue.empty()) {
                    /* pdValue is an AP-REQ containing the TGT */
                    size_t apConsumed = 0;
@@ -3797,6 +3812,133 @@ std::vector<uint8_t> Krb5KDC::handleStandardTgsReq(const uint8_t *data, size_t l
                                   svcRealm, req.sname.name, req.sname.instance);
    }

    /* ---- S4U (MS-SFU): whose ticket this is ---- */
    Krb5Principal ticketClient = tgt_enc.cname;
    bool haveClientUid = false;
    uuid::uuid s4uClientUid;
    bool s4uForwardable = true;
    std::optional<dsdb::Entry> requester;
    {
        uuid::uuid ru; std::vector<uint8_t> rk; int32_t re = selectedEtype; std::string rs;
        if (svcRi->dir && lookupPrincipal(*svcRi, tgt_enc.cname, selectedEtype, ru, rk, re, rs))
            requester = svcRi->dir->byGuid(ru);
    }
    auto principalOf = [&](const dsdb::Entry &e) {
        return Krb5Principal(KRB5_NT_PRINCIPAL, svcRi->meta.realm, e.str("sAMAccountName"));
    };
    if (!paForUser.empty()) {
        /* S4U2Self: PA-FOR-USER ::= SEQUENCE { userName[0] PrincipalName,
         *   userRealm[1] Realm, cksum[2] Checksum, auth-package[3] KerberosString } */
        const uint8_t *seq; size_t seqLen; size_t o = 0; uint8_t t;
        Krb5PrincipalType ut = KRB5_NT_PRINCIPAL;
        std::vector<std::string> comps;
        std::string urealm, pkg;
        std::vector<uint8_t> cksum;
        if (!Asn1Der::parseTLV(paForUser.data(), paForUser.size(), o, t, seq, seqLen) || t != Asn1Der::TAG_SEQUENCE)
            return buildStandardError(KRB_ERR_GENERIC, "bad PA-FOR-USER", svcRealm, req.sname.name, req.sname.instance);
        size_t f = 0;
        while (f < seqLen) {
            uint8_t ft; const uint8_t *fc; size_t fl;
            if (!Asn1Der::parseTLV(seq, seqLen, f, ft, fc, fl)) break;
            size_t io = 0; uint8_t it; const uint8_t *ic; size_t il;
            switch (ft & 0x1F) {
            case 0: Asn1Der::parsePrincipalName(fc, fl, ut, comps); break;
            case 1: if (Asn1Der::parseTLV(fc, fl, io, it, ic, il)) Asn1Der::parseGeneralString(ic, il, urealm); break;
            case 2: /* Checksum ::= SEQUENCE { cksumtype[0], checksum[1] OCTET STRING } */
                if (Asn1Der::parseTLV(fc, fl, io, it, ic, il)) {
                    size_t co = 0; uint8_t ct; const uint8_t *cc; size_t cl;
                    while (co < il && Asn1Der::parseTLV(ic, il, co, ct, cc, cl))
                        if ((ct & 0x1F) == 1) {
                            size_t xo = 0; uint8_t xt; const uint8_t *xc; size_t xl;
                            if (Asn1Der::parseTLV(cc, cl, xo, xt, xc, xl)) cksum.assign(xc, xc + xl);
                        }
                }
                break;
            case 3: if (Asn1Der::parseTLV(fc, fl, io, it, ic, il)) Asn1Der::parseGeneralString(ic, il, pkg); break;
            }
        }
        /* KERB_CHECKSUM_HMAC_MD5 (RFC 4757) with the TGT session key, usage 17,
         * over name-type (LE32) | name strings | realm | auth-package */
        crypto::Bytes data;
        for (int i = 0; i < 4; ++i) data.push_back(uint8_t(uint32_t(ut) >> (8 * i)));
        for (auto &c : comps) data.insert(data.end(), c.begin(), c.end());
        data.insert(data.end(), urealm.begin(), urealm.end());
        data.insert(data.end(), pkg.begin(), pkg.end());
        const char sk[] = "signaturekey";
        crypto::Bytes ksign = crypto::hmacMd5(tgt_enc.session_key.keydata, crypto::Bytes(sk, sk + sizeof sk));
        crypto::Bytes tmp = {17, 0, 0, 0};
        tmp.insert(tmp.end(), data.begin(), data.end());
        crypto::Bytes want = crypto::hmacMd5(ksign, crypto::md5(tmp));
        if (!crypto::equal(want, crypto::Bytes(cksum.begin(), cksum.end())))
            return buildStandardError(KRB_AP_ERR_MODIFIED, "PA-FOR-USER checksum invalid",
                                       svcRealm, req.sname.name, req.sname.instance);
        /* only for itself: the requested service is the requester */
        if (!requester || !(requester->objectGuid == svc_uid))
            return buildStandardError(KDC_ERR_BADOPTION, "S4U2Self only to the requesting service",
                                       svcRealm, req.sname.name, req.sname.instance);
        std::string uname;
        for (auto &c : comps) { if (!uname.empty()) uname += "/"; uname += c; }
        Krb5Principal up(KRB5_NT_PRINCIPAL, svcRi->meta.realm, uname);
        uuid::uuid uu; std::vector<uint8_t> uk; int32_t ue = selectedEtype; std::string us;
        if (!lookupPrincipal(*svcRi, up, selectedEtype, uu, uk, ue, us))
            return buildStandardError(KDC_ERR_C_PRINCIPAL_UNKNOWN, "S4U2Self: unknown user " + uname,
                                       svcRealm, req.sname.name, req.sname.instance);
        auto ue2 = svcRi->dir->byGuid(uu);
        if (!ue2)
            return buildStandardError(KDC_ERR_C_PRINCIPAL_UNKNOWN, "S4U2Self: unknown user", svcRealm, req.sname.name, req.sname.instance);
        ticketClient = principalOf(*ue2);
        s4uClientUid = uu;
        haveClientUid = true;
        /* forwardable (usable for S4U2Proxy) only with protocol transition
         * (TRUSTED_TO_AUTH_FOR_DELEGATION) and a delegable user */
        uint32_t ruac = uint32_t(dsdb::intValue(requester->attrs, "userAccountControl", 0));
        uint32_t uuac = uint32_t(dsdb::intValue(ue2->attrs, "userAccountControl", 0));
        s4uForwardable = (ruac & 0x1000000) && !(uuac & 0x100000);
        std::cerr << "[KDC] S4U2Self " << tgt_enc.cname.name << " for '" << uname << "' forwardable=" << s4uForwardable << std::endl;
    }
    if (req.kdc_options & 0x00020000) {
        /* S4U2Proxy (cname-in-addl-tkt): the evidence ticket is the user's
         * ticket to the requester; the target must be in the requester's
         * msDS-AllowedToDelegateTo */
        if (req.additional_tickets.empty() || !requester)
            return buildStandardError(KDC_ERR_BADOPTION, "S4U2Proxy: no evidence ticket", svcRealm, req.sname.name, req.sname.instance);
        Krb5Ticket &ev = req.additional_tickets.front();
        uuid::uuid eu; std::vector<uint8_t> ek; int32_t ee = ev.enc_etype; std::string es;
        Krb5EncTicketPart evEnc;
        bool ok = false;
        if (lookupPrincipal(*svcRi, tgt_enc.cname, ev.enc_etype, eu, ek, ee, es)) {
            try {
                auto pl = Krb5Crypto::decrypt(ek, ev.enc_part, KRB5_KU_TICKET);
                size_t cc = 0;
                ok = evEnc.deserialize(pl.data(), pl.size(), cc);
            } catch (const std::exception &) { ok = false; }
        }
        if (!ok)
            return buildStandardError(KDC_ERR_BADOPTION, "S4U2Proxy: evidence ticket is not for the requester",
                                       svcRealm, req.sname.name, req.sname.instance);
        if (!(evEnc.flags & KRB5_TF_FORWARDABLE))
            return buildStandardError(KDC_ERR_BADOPTION, "S4U2Proxy: evidence ticket not forwardable",
                                       svcRealm, req.sname.name, req.sname.instance);
        std::string target = req.sname.name + (req.sname.instance.empty() ? "" : "/" + req.sname.instance);
        bool allowed = false;
        if (auto *atd = requester->get("msDS-AllowedToDelegateTo"))
            for (auto &v : *atd) {
                std::string a = v.substr(0, v.find('@'));
                if (dsdb::iequals(a, target)) allowed = true;
            }
        std::cerr << "[KDC] S4U2Proxy " << tgt_enc.cname.name << " for '" << evEnc.cname.name << "' -> "
                  << target << (allowed ? " allowed" : " NOT allowed") << std::endl;
        if (!allowed)
            return buildStandardError(KDC_ERR_BADOPTION, "S4U2Proxy: target not in msDS-AllowedToDelegateTo",
                                       svcRealm, req.sname.name, req.sname.instance);
        ticketClient = evEnc.cname;
        uuid::uuid cu; std::vector<uint8_t> ck; int32_t ce = selectedEtype; std::string cs;
        if (lookupPrincipal(*svcRi, evEnc.cname, selectedEtype, cu, ck, ce, cs)) {
            s4uClientUid = cu;
            haveClientUid = true;
        }
    }

    /* Generate service session key */
    Krb5SessionKey svc_session_key = Krb5Crypto::generateSessionKey(selectedEtype);

@@ -3807,7 +3949,7 @@ std::vector<uint8_t> Krb5KDC::handleStandardTgsReq(const uint8_t *data, size_t l
        endtime = tgt_enc.endtime;

    uint32_t svcFlags = KRB5_TF_PRE_AUTHENT;
    if (tgt_enc.flags & KRB5_TF_FORWARDABLE)
    if ((tgt_enc.flags & KRB5_TF_FORWARDABLE) && s4uForwardable)
        svcFlags |= KRB5_TF_FORWARDABLE;
    if (tgt_enc.flags & KRB5_TF_RENEWABLE)
        svcFlags |= KRB5_TF_RENEWABLE;
@@ -3816,7 +3958,7 @@ std::vector<uint8_t> Krb5KDC::handleStandardTgsReq(const uint8_t *data, size_t l
    Krb5EncTicketPart svc_enc_tkt;
    svc_enc_tkt.flags = svcFlags;
    svc_enc_tkt.session_key = svc_session_key;
    svc_enc_tkt.cname = tgt_enc.cname;
    svc_enc_tkt.cname = ticketClient;
    svc_enc_tkt.transited = tgt_enc.transited;
    svc_enc_tkt.authtime = tgt_enc.authtime;
    svc_enc_tkt.starttime = now;
@@ -3827,7 +3969,9 @@ std::vector<uint8_t> Krb5KDC::handleStandardTgsReq(const uint8_t *data, size_t l
     * from the TGT's cname (a standard, serialized field) so the service ticket
     * gets a valid PAC; without it Windows rejects the logon ("bad password"). */
    uuid::uuid pacUid = tgt_enc.uid;
    {
    if (haveClientUid) {
        pacUid = s4uClientUid;
    } else {
        std::vector<uint8_t> _k; int32_t _ke = selectedEtype; std::string _s; uuid::uuid cu;
        if (lookupPrincipal(*svcRi, tgt_enc.cname, selectedEtype, cu, _k, _ke, _s))
            pacUid = cu;
@@ -3878,7 +4022,7 @@ std::vector<uint8_t> Krb5KDC::handleStandardTgsReq(const uint8_t *data, size_t l
    Krb5KdcRep tgs_rep;
    tgs_rep.pvno = 5;
    tgs_rep.msg_type = KRB_TGS_REP;
    tgs_rep.cname = tgt_enc.cname;
    tgs_rep.cname = ticketClient;
    tgs_rep.ticket = svc_ticket;
    tgs_rep.enc_etype = selectedEtype;
    tgs_rep.enc_kvno = 1;
+1 −0
Changes for plugin/krb5/krb5.h: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -504,6 +504,7 @@ namespace fenrirds {
        std::vector<Krb5EncType> etypes;    /* requested enc types        */
        std::vector<uint8_t> padata;        /* pre-auth data (PA-DATA)    */
        Krb5Ticket           tgt;           /* TGT (only for TGS-REQ)    */
        std::vector<Krb5Ticket> additional_tickets;   /* [11]: S4U2Proxy evidence, user-to-user */
        std::vector<uint8_t> authenticator; /* AP-REQ in padata (TGS-REQ)*/

        Krb5KdcReq();