Commit 8e379da8 authored by jan.koester's avatar jan.koester
Browse files

test

parent a965a932
Loading
Loading
Loading
Loading
Loading
+17 −4
Changes for plugin/smb/smb.cpp: 17 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include "crypto/crypto.h"
#include "crypto/gss.h"
#include "rpc/ndr.h"
#include "rpc/services.h"
#include "smb.h"

namespace fenrirds {
@@ -72,10 +73,20 @@ void RpcPipe::handlePdu(const std::vector<uint8_t> &pdu) {
}

std::vector<uint8_t> RpcPipe::dispatch(const rpc::RequestPdu &req) {
    /* the lsarpc/samr/netlogon opnum handlers plug in here (next step).
     * Until then every call faults with nca_op_rng_error so the transport is
     * exercised end to end. */
    return rpc::buildFault(req.callId, req.contextId, 0x1C010002 /* nca_op_rng_error */);
    rpc::DispatchResult res;
    if (rpc::isLsarpc(_boundAbstract.uuid))
        res = rpc::lsarpcDispatch(req.opnum, req.stub, _sessionKey);
    else if (rpc::isSamr(_boundAbstract.uuid))
        res = rpc::samrDispatch(req.opnum, req.stub, _sessionKey);
    else if (rpc::isNetlogon(_boundAbstract.uuid))
        res = rpc::netlogonDispatch(req.opnum, req.stub, _sessionKey);
    else {
        res.fault = true;
        res.faultCode = 0x1C010003;   /* nca_unk_if */
    }
    if (res.fault)
        return rpc::buildFault(req.callId, req.contextId, res.faultCode);
    return rpc::buildResponse(req.callId, req.contextId, res.stub);
}

/*****************************************************************************
@@ -251,6 +262,7 @@ std::vector<uint8_t> SmbServer::handleMessage(Conn &c, const std::vector<uint8_t

        c.authenticated = true;
        c.clientPrincipal = res.clientPrincipal;
        c.sessionKey = res.sessionKey;
        if (!c.sessionId) {
            auto r = crypto::randomBytes(8);
            c.sessionId = (rd64(r.data()) | 1);
@@ -320,6 +332,7 @@ std::vector<uint8_t> SmbServer::handleMessage(Conn &c, const std::vector<uint8_t
            return errorResponse(req, smb::STATUS_OBJECT_NAME_NOT_FOUND);
        uint64_t fid = c.nextFid++;
        c.pipes[fid] = new RpcPipe(pname);
        c.pipes[fid]->setSessionKey(c.sessionKey);
        smb::Smb2Header h; h.command=req.command; h.messageId=req.messageId; h.sessionId=c.sessionId;
        h.treeId=req.treeId; h.status=smb::STATUS_SUCCESS; h.creditReqResp=1;
        auto resp = smb::buildHeader(h);
+3 −0
Changes for plugin/smb/smb.h: 3 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ namespace fenrirds {
    public:
        explicit RpcPipe(const std::string &name) : _name(name) {}
        const std::string &name() const { return _name; }
        void setSessionKey(const std::vector<uint8_t> &k) { _sessionKey = k; }

        /* feed bytes written to the pipe; produce any complete PDU responses */
        void write(const uint8_t *data, size_t len);
@@ -50,6 +51,7 @@ namespace fenrirds {
        size_t               _outPos = 0;
        rpc::SyntaxId        _boundAbstract;  /* the interface bound on this pipe */
        bool                 _bound = false;
        std::vector<uint8_t> _sessionKey;
    };

    /***************************************************************************
@@ -88,6 +90,7 @@ namespace fenrirds {
            std::string clientPrincipal;
            smb::Smb3Keys keys;
            bool     haveKeys = false;
            std::vector<uint8_t> sessionKey;     /* GSS session key for SAMR */
            uint32_t treeIpc = 0;                /* IPC$ tree id (0 = none) */
            std::map<uint64_t, RpcPipe*> pipes;  /* volatile file id -> pipe */
            uint64_t nextFid = 1;
+4 −0
Changes for src/CMakeLists.txt: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -34,6 +34,10 @@ list(APPEND fenrirdsSrcs
    dsdb/pac.cpp
    rpc/ndr.cpp
    rpc/dcerpc.cpp
    rpc/services.cpp
    rpc/lsarpc.cpp
    rpc/samr.cpp
    rpc/netlogon.cpp
    smb/smb2.cpp
)

src/rpc/lsarpc.cpp

0 → 100644
+145 −0
Changes for src/rpc/lsarpc.cpp: 145 added lines, 0 removed lines.
Original line number Diff line number Diff line
#include <cstring>

#include "crypto/crypto.h"
#include "domains.h"
#include "ndr.h"
#include "services.h"

namespace fenrirds {
namespace rpc {

/* ---- shared: resolve the primary domain ---- */
DomainInfo primaryDomain() {
    DomainInfo di;
    auto doms = DomainRegistry::instance().list();
    if (doms.empty())
        return di;
    auto &d = doms.front();
    AuthBackend *bck = DomainRegistry::instance().get(d.id);
    if (!bck)
        return di;
    dsdb::DomainMeta meta;
    meta.dnsName = d.name;
    auto dot = d.name.find('.');
    meta.netbios = dsdb::upper(dot == std::string::npos ? d.name : d.name.substr(0, dot));
    meta.baseDn = dsdb::dnFromDomain(d.name);
    meta.realm = dsdb::upper(d.name);
    dsdb::Directory dir(*bck, meta);
    auto head = dir.byDn(meta.baseDn);
    if (!head)
        return di;
    di.ok = true;
    di.dnsName = d.name;
    di.netbios = meta.netbios;
    di.forest = d.name;
    di.sid = head->objectSid;
    std::memcpy(di.guid, head->objectGuid.value, 16);
    di.baseDn = meta.baseDn;
    di.domainId = d.id;
    return di;
}

namespace {
    /* lsarpc UUID 12345778-1234-abcd-ef00-0123456789ab (NDR LE field order) */
    const uint8_t LSA_UUID[16] = {0x78,0x57,0x34,0x12,0x34,0x12,0xcd,0xab,0xef,0x00,0x01,0x23,0x45,0x67,0x89,0xab};

    enum { LSA_CLOSE = 0, LSA_QUERYINFOPOLICY = 7, LSA_OPENPOLICY2 = 44, LSA_QUERYINFOPOLICY2 = 46 };

    /* an lsa_String / lsa_StringLarge: Length, Size, [ref]Buffer (conformant
     * varying wchar). lsa_String Size = Length; lsa_StringLarge Size = Length+2. */
    void pushLsaString(NdrPush &p, const std::string &s, bool large) {
        auto u = crypto::utf16le(s);
        uint16_t len = uint16_t(u.size());
        p.u16(len);
        p.u16(large ? uint16_t(len + 2) : len);
        p.ref(true);
    }
    void pushLsaStringBuffer(NdrPush &p, const std::string &s, bool large) {
        auto u = crypto::utf16le(s);
        uint32_t chars = uint32_t(u.size() / 2);
        uint32_t maxc = large ? chars + 1 : chars;
        p.align(4);
        p.u32(maxc);
        p.u32(0);
        p.u32(chars);
        p.bytes(u.data(), u.size());
    }
}

bool isLsarpc(const uint8_t uuid[16]) { return std::memcmp(uuid, LSA_UUID, 16) == 0; }

DispatchResult lsarpcDispatch(uint16_t opnum, const std::vector<uint8_t> &stub, const std::vector<uint8_t> &) {
    DispatchResult r;
    switch (opnum) {
    case LSA_OPENPOLICY2:
    case 6 /* LSA_OPENPOLICY */: {
        /* return a fresh policy handle + success */
        NdrPush p;
        PolicyHandle h;
        h.type = 0;
        auto rnd = crypto::randomBytes(16);
        std::memcpy(h.uuid, rnd.data(), 16);
        p.handle(h);
        p.u32(0);                 /* NTSTATUS success */
        r.stub = p.buf;
        return r;
    }
    case LSA_CLOSE: {
        NdrPush p;
        PolicyHandle h;           /* null handle on close */
        p.handle(h);
        p.u32(0);
        r.stub = p.buf;
        return r;
    }
    case LSA_QUERYINFOPOLICY:
    case LSA_QUERYINFOPOLICY2: {
        /* request: policy handle + u16 level */
        NdrPull in(stub);
        in.handle();
        uint16_t level = in.u16();
        DomainInfo di = primaryDomain();
        if (!di.ok) { r.status = 0xC0000034; /* object name not found */
            NdrPush p; p.u32(0); p.u32(r.status); r.stub = p.buf; return r; }

        NdrPush p;
        p.ref(true);              /* [out,ref] **info -> the union pointer */
        p.u16(level);             /* union discriminant */
        if (level == 5) {         /* PolicyAccountDomainInformation */
            p.align(4);
            pushLsaString(p, di.netbios, false);
            p.ref(true);          /* sid pointer */
            pushLsaStringBuffer(p, di.netbios, false);
            p.sid(di.sid);
        } else if (level == 12) { /* PolicyDnsDomainInformation */
            p.align(4);
            pushLsaString(p, di.netbios, true);     /* name (NetBIOS) */
            pushLsaString(p, di.dnsName, true);     /* dns_domain */
            pushLsaString(p, di.forest, true);      /* dns_forest */
            p.bytes(di.guid, 16);                   /* domain_guid */
            p.ref(true);                            /* sid pointer */
            pushLsaStringBuffer(p, di.netbios, true);
            pushLsaStringBuffer(p, di.dnsName, true);
            pushLsaStringBuffer(p, di.forest, true);
            p.sid(di.sid);
        } else {
            /* unsupported level: return a null pointer + invalid info class */
            p.buf.clear();
            p.ref(false);
            p.u32(0xC00000DD);    /* STATUS_INVALID_INFO_CLASS */
            r.stub = p.buf;
            return r;
        }
        p.u32(0);                 /* NTSTATUS success */
        r.stub = p.buf;
        return r;
    }
    default:
        r.fault = true;
        r.faultCode = 0x1C010002;  /* nca_op_rng_error */
        return r;
    }
}

}
}
+2 −2
Changes for src/rpc/ndr.cpp: 2 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ void NdrPush::unistrBuffer(const std::string &s, bool nul) {
    u32(actual);        /* ActualCount */
    bytes(u.data(), u.size());
    if (nul) { u8(0); u8(0); }
    align(4);
}

void NdrPush::sid(const dsdb::Sid &s) {
@@ -61,7 +60,8 @@ std::string NdrPull::unistrBuffer() {
        throw NdrError("unistr offset != 0");
    auto raw = takeVec(size_t(actual) * 2);
    std::string out = fromUtf16(raw.data(), raw.size());
    align(4);
    /* NDR aligns at the START of each field, not after; the next read aligns
     * itself. (A trailing align here would eat the following u16's bytes.) */
    /* strip a trailing NUL if present */
    if (!out.empty() && out.back() == '\0')
        out.pop_back();
Loading