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

ntsec added

parent f7c26b31
Loading
Loading
Loading
Loading
+48 −6
Changes for plugin/smb/smb.cpp: 48 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -164,11 +164,30 @@ void RpcPipe::handlePdu(const std::vector<uint8_t> &pdu) {
            std::cerr << "[RPC] BIND auth=" << b.hasAuth << " type=" << int(b.authType)
                      << " level=" << int(b.authLevel) << " token=" << b.authToken.size() << "B"
                      << " iface=" << _name << std::endl;
            auto known = [](const rpc::SyntaxId &a) {
                return rpc::isLsarpc(a.uuid) || rpc::isSamr(a.uuid) || rpc::isNetlogon(a.uuid);
            };
            _boundAbstract = b.contexts[0].abstractSyntax;
            _bound = true;
            resp = rpc::buildBindAck(b, "\\PIPE\\" + _name, {}, 0, 0, [](const rpc::SyntaxId &a) {
                return rpc::isLsarpc(a.uuid) || rpc::isSamr(a.uuid) || rpc::isNetlogon(a.uuid);
            });
            if (b.hasAuth && b.authType == rpc::AUTH_TYPE_SCHANNEL) {
                /* netlogon secure channel: the client names its computer, we
                 * take the session key of its ServerAuthenticate3 */
                rpc::NlAuthNegotiate neg;
                std::vector<uint8_t> sk;
                if (rpc::parseNlAuthNegotiate(b.authToken, neg) && rpc::netlogonChannelKey(neg.computer, sk)) {
                    _netsec = rpc::NetsecContext(sk, neg.computer);
                    resp = rpc::buildBindAck(b, "\\PIPE\\" + _name, rpc::buildNlAuthResponse(),
                                             b.authType, b.authLevel, known, b.authContextId);
                    std::cerr << "[RPC] schannel bind for '" << neg.computer << "'" << std::endl;
                } else {
                    std::cerr << "[RPC] schannel bind: no secure channel for '" << neg.computer << "'" << std::endl;
                    resp = rpc::buildBindNak(h.callId);
                }
            } else {
                resp = rpc::buildBindAck(b, "\\PIPE\\" + _name, {}, 0, 0, known);
            }
            if (h.pduType == rpc::PDU_ALTER && resp.size() > 2 && resp[2] == rpc::PDU_BIND_ACK)
                resp[2] = rpc::PDU_ALTER_ACK;
        } else {
            resp = rpc::buildBindNak(h.callId);
        }
@@ -180,13 +199,25 @@ void RpcPipe::handlePdu(const std::vector<uint8_t> &pdu) {
            if (req.hasAuth)
                std::cerr << "[RPC] REQUEST opnum=" << req.opnum << " auth type=" << int(req.authType)
                          << " level=" << int(req.authLevel) << " token=" << req.authToken.size() << "B" << std::endl;
            if (req.hasAuth && req.authType == rpc::AUTH_TYPE_SCHANNEL) {
                bool seal = req.authLevel == rpc::AUTH_LEVEL_PRIVACY;
                if (!_netsec.valid() || !_netsec.unwrapRequest(req.stub, req.authToken, seal)) {
                    std::cerr << "[RPC] schannel request rejected (signature)" << std::endl;
                    resp = rpc::buildFault(req.callId, req.contextId, 0x00000005);   /* access denied */
                } else {
                    if (req.authPadLength <= req.stub.size())
                        req.stub.resize(req.stub.size() - req.authPadLength);
                    resp = dispatch(req, true);
                }
            } else {
                resp = dispatch(req);
            }
        }
    }
    _out.insert(_out.end(), resp.begin(), resp.end());
}

std::vector<uint8_t> RpcPipe::dispatch(const rpc::RequestPdu &req) {
std::vector<uint8_t> RpcPipe::dispatch(const rpc::RequestPdu &req, bool schannel) {
    rpc::DispatchResult res;
    const char *iface = "?";
    if (rpc::isLsarpc(_boundAbstract.uuid)) {
@@ -197,7 +228,8 @@ std::vector<uint8_t> RpcPipe::dispatch(const rpc::RequestPdu &req) {
        res = rpc::samrDispatch(req.opnum, req.stub, _sessionKey, _appKey);
    } else if (rpc::isNetlogon(_boundAbstract.uuid)) {
        iface = "netlogon";
        res = rpc::netlogonDispatch(req.opnum, req.stub, _sessionKey);
        res = rpc::netlogonDispatch(req.opnum, req.stub, _sessionKey,
                                    schannel ? _netsec.computer() : std::string());
    } else {
        res.fault = true;
        res.faultCode = 0x1C010003;   /* nca_unk_if */
@@ -214,6 +246,14 @@ std::vector<uint8_t> RpcPipe::dispatch(const rpc::RequestPdu &req) {
    }
    if (res.fault)
        return rpc::buildFault(req.callId, req.contextId, res.faultCode);
    if (schannel) {
        bool seal = req.authLevel == rpc::AUTH_LEVEL_PRIVACY;
        size_t sigLen = req.authToken.size();
        return rpc::buildAuthResponse(req.callId, req.contextId, res.stub, req.authType, req.authLevel,
                                      req.authContextId, [&](std::vector<uint8_t> &data) {
            return _netsec.wrapResponse(data, seal, sigLen);
        });
    }
    return rpc::buildResponse(req.callId, req.contextId, res.stub);
}

@@ -1817,7 +1857,7 @@ std::vector<uint8_t> SmbServer::setInfo(Conn &c, Session &s, const smb::Smb2Head
extern "C" {

fenrirds::Plugin *fenrirds_plugin_create(fenrirds::AuthBackend &backend, const char *configJson) {
    std::string bindAddr = "0.0.0.0", host;
    std::string bindAddr = "0.0.0.0", host, address;
    int port = 445;
    if (configJson) {
        json_object *j = json_tokener_parse(configJson);
@@ -1825,9 +1865,11 @@ fenrirds::Plugin *fenrirds_plugin_create(fenrirds::AuthBackend &backend, const c
            if (auto *x = json_object_object_get(j, "smb_bind")) bindAddr = json_object_get_string(x);
            if (auto *x = json_object_object_get(j, "smb_port")) port = json_object_get_int(x);
            if (auto *x = json_object_object_get(j, "dns_hostname")) host = json_object_get_string(x);
            if (auto *x = json_object_object_get(j, "dns_address")) address = json_object_get_string(x);
            json_object_put(j);
        }
    }
    fenrirds::rpc::setDcIdentity(host, address);
    return new fenrirds::SmbServer(backend, bindAddr, port, host);
}

+2 −1
Changes for plugin/smb/smb.h: 2 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ namespace fenrirds {

    private:
        void handlePdu(const std::vector<uint8_t> &pdu);
        std::vector<uint8_t> dispatch(const rpc::RequestPdu &req);
        std::vector<uint8_t> dispatch(const rpc::RequestPdu &req, bool schannel = false);

        std::string          _name;
        std::vector<uint8_t> _in;        /* request assembly */
@@ -55,6 +55,7 @@ namespace fenrirds {
        bool                 _bound = false;
        std::vector<uint8_t> _sessionKey;
        std::vector<uint8_t> _appKey;
        rpc::NetsecContext   _netsec;    /* netlogon schannel bind (auth type 68) */
    };

    /***************************************************************************
+37 −7
Changes for src/rpc/dcerpc.cpp: 37 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -47,7 +47,8 @@ bool parseCommonHeader(const uint8_t *d, size_t n, CommonHeader &h) {
}

static void parseAuthTrailer(const uint8_t *d, size_t n, const CommonHeader &h,
                             bool &hasAuth, std::vector<uint8_t> &tok, uint8_t &type, uint8_t &level) {
                             bool &hasAuth, std::vector<uint8_t> &tok, uint8_t &type, uint8_t &level,
                             uint8_t *padLen = nullptr, uint32_t *ctxId = nullptr) {
    if (h.authLength == 0 || h.fragLength > n)
        return;
    /* auth trailer = 8-byte sec_trailer header + authLength bytes, at the end */
@@ -56,6 +57,8 @@ static void parseAuthTrailer(const uint8_t *d, size_t n, const CommonHeader &h,
        return;
    type = d[authStart];
    level = d[authStart + 1];
    if (padLen) *padLen = d[authStart + 2];
    if (ctxId) *ctxId = rd32(d + authStart + 4);
    tok.assign(d + authStart + 8, d + authStart + 8 + h.authLength);
    hasAuth = true;
}
@@ -98,7 +101,8 @@ bool parseBind(const uint8_t *d, size_t n, BindPdu &out) {
        }
        out.contexts.push_back(std::move(ctx));
    }
    parseAuthTrailer(d, n, h, out.hasAuth, out.authToken, out.authType, out.authLevel);
    parseAuthTrailer(d, n, h, out.hasAuth, out.authToken, out.authType, out.authLevel,
                     nullptr, &out.authContextId);
    return true;
}

@@ -120,7 +124,8 @@ bool parseRequest(const uint8_t *d, size_t n, RequestPdu &out) {
        stubEnd = size_t(h.fragLength) - h.authLength - 8;
    if (stubEnd > n || stubEnd < o) return false;
    out.stub.assign(d + o, d + stubEnd);
    parseAuthTrailer(d, n, h, out.hasAuth, out.authToken, out.authType, out.authLevel);
    parseAuthTrailer(d, n, h, out.hasAuth, out.authToken, out.authType, out.authLevel,
                     &out.authPadLength, &out.authContextId);
    return true;
}

@@ -138,7 +143,7 @@ static std::vector<uint8_t> header(uint8_t type, uint8_t flags, uint32_t callId,
}

static void appendAuthTrailer(std::vector<uint8_t> &pdu, const std::vector<uint8_t> &tok,
                              uint8_t type, uint8_t level) {
                              uint8_t type, uint8_t level, uint32_t ctxId = 0) {
    if (tok.empty())
        return;
    /* pad the stub to a 4-byte boundary (auth_pad) */
@@ -148,13 +153,14 @@ static void appendAuthTrailer(std::vector<uint8_t> &pdu, const std::vector<uint8
    pdu.push_back(level);
    pdu.push_back(pad);
    pdu.push_back(0);                 /* reserved */
    wr32(pdu, 0);                     /* auth context id */
    wr32(pdu, ctxId);                 /* auth context id */
    pdu.insert(pdu.end(), tok.begin(), tok.end());
}

std::vector<uint8_t> buildBindAck(const BindPdu &bind, const std::string &sec,
                                  const std::vector<uint8_t> &authToken, uint8_t authType, uint8_t authLevel,
                                  const std::function<bool(const SyntaxId &)> &knownInterface) {
                                  const std::function<bool(const SyntaxId &)> &knownInterface,
                                  uint32_t authContextId) {
    std::vector<uint8_t> body;
    wr16(body, bind.maxXmitFrag ? bind.maxXmitFrag : 4280);
    wr16(body, bind.maxRecvFrag ? bind.maxRecvFrag : 4280);
@@ -193,7 +199,7 @@ std::vector<uint8_t> buildBindAck(const BindPdu &bind, const std::string &sec,
    std::vector<uint8_t> pdu = header(PDU_BIND_ACK, PFC_FIRST_FRAG | PFC_LAST_FRAG, bind.callId, 0, authLen);
    pdu.insert(pdu.end(), body.begin(), body.end());
    if (!authToken.empty())
        appendAuthTrailer(pdu, authToken, authType, authLevel);
        appendAuthTrailer(pdu, authToken, authType, authLevel, authContextId);
    uint16_t frag = uint16_t(pdu.size());
    pdu[8] = frag & 0xff; pdu[9] = frag >> 8;
    return pdu;
@@ -226,6 +232,30 @@ std::vector<uint8_t> buildResponse(uint32_t callId, uint16_t contextId,
    return pdu;
}

std::vector<uint8_t> buildAuthResponse(uint32_t callId, uint16_t contextId,
                                       const std::vector<uint8_t> &stub,
                                       uint8_t authType, uint8_t authLevel, uint32_t authContextId,
                                       const std::function<std::vector<uint8_t>(std::vector<uint8_t> &)> &wrap) {
    std::vector<uint8_t> data = stub;
    uint8_t pad = uint8_t((16 - data.size() % 16) % 16);
    data.resize(data.size() + pad, 0);
    std::vector<uint8_t> tok = wrap(data);
    std::vector<uint8_t> pdu = header(PDU_RESPONSE, PFC_FIRST_FRAG | PFC_LAST_FRAG, callId, 0, uint16_t(tok.size()));
    wr32(pdu, uint32_t(stub.size()));   /* alloc_hint */
    wr16(pdu, contextId);
    wr16(pdu, 0);
    pdu.insert(pdu.end(), data.begin(), data.end());
    pdu.push_back(authType);
    pdu.push_back(authLevel);
    pdu.push_back(pad);
    pdu.push_back(0);
    wr32(pdu, authContextId);
    pdu.insert(pdu.end(), tok.begin(), tok.end());
    uint16_t frag = uint16_t(pdu.size());
    pdu[8] = frag & 0xff; pdu[9] = frag >> 8;
    return pdu;
}

std::vector<uint8_t> buildFault(uint32_t callId, uint16_t contextId, uint32_t status) {
    std::vector<uint8_t> pdu = header(PDU_FAULT, PFC_FIRST_FRAG | PFC_LAST_FRAG, callId, 0, 0);
    wr32(pdu, 0);            /* alloc_hint */
+11 −1
Changes for src/rpc/dcerpc.h: 11 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ namespace fenrirds {
            std::vector<uint8_t> authToken;
            uint8_t  authType = 0;
            uint8_t  authLevel = 0;
            uint32_t authContextId = 0;
        };

        /* a parsed REQUEST */
@@ -75,6 +76,8 @@ namespace fenrirds {
            std::vector<uint8_t> authToken;
            uint8_t  authType = 0;
            uint8_t  authLevel = 0;
            uint8_t  authPadLength = 0;   /* stub ends with this many pad bytes */
            uint32_t authContextId = 0;
        };

        bool parseCommonHeader(const uint8_t *d, size_t n, CommonHeader &h);
@@ -90,7 +93,8 @@ namespace fenrirds {
        std::vector<uint8_t> buildBindAck(const BindPdu &bind, const std::string &secondaryAddr,
                                          const std::vector<uint8_t> &authToken = {},
                                          uint8_t authType = 0, uint8_t authLevel = 0,
                                          const std::function<bool(const SyntaxId &)> &knownInterface = nullptr);
                                          const std::function<bool(const SyntaxId &)> &knownInterface = nullptr,
                                          uint32_t authContextId = 0);
        std::vector<uint8_t> buildBindNak(uint32_t callId);

        /* build a RESPONSE carrying the NDR stub (single fragment) */
@@ -98,6 +102,12 @@ namespace fenrirds {
                                           const std::vector<uint8_t> &stub,
                                           const std::vector<uint8_t> &authToken = {},
                                           uint8_t authType = 0, uint8_t authLevel = 0);
        /* a RESPONSE protected by a security provider: the stub is padded to
         * 16 bytes, wrap() signs/seals it in place and returns the auth_value */
        std::vector<uint8_t> buildAuthResponse(uint32_t callId, uint16_t contextId,
                                               const std::vector<uint8_t> &stub,
                                               uint8_t authType, uint8_t authLevel, uint32_t authContextId,
                                               const std::function<std::vector<uint8_t>(std::vector<uint8_t> &)> &wrap);
        std::vector<uint8_t> buildFault(uint32_t callId, uint16_t contextId, uint32_t status);
    };
};
+378 −10

File changed.

Preview size limit exceeded, changes collapsed.

Loading