Loading plugin/ad/ad.cpp +2 −4 Changes for plugin/ad/ad.cpp: 2 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -1544,10 +1544,8 @@ std::vector<uint8_t> ActiveDirectory::handleBindRequest(const uint8_t *content, std::vector<uint8_t> creds; if (spnego) { std::vector<uint8_t> krb5oid = gss::firstMechOid(session.gssMechTypes); std::vector<uint8_t> mic = gss::getMic(session.gssKey, session.gssEtype, true, session.sendSeq++, session.gssMechTypes, session.gssAccSubkey); creds = gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, &krb5oid, krb5reply, mic); /* the client's preferred mech: no mechListMIC (see smb.cpp) */ creds = gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, &krb5oid, krb5reply, {}); /* GSS-SPNEGO has no RFC 4752 layer negotiation: the bind is * done, and the client signs or seals the following PDUs as * it asked for in its authenticator (MS-ADTS 5.1.1.1.2) */ Loading plugin/smb/smb.cpp +28 −5 Changes for plugin/smb/smb.cpp: 28 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -166,7 +166,9 @@ void RpcPipe::handlePdu(const std::vector<uint8_t> &pdu) { << " iface=" << _name << std::endl; _boundAbstract = b.contexts[0].abstractSyntax; _bound = true; resp = rpc::buildBindAck(b, "\\PIPE\\" + _name); 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); }); } else { resp = rpc::buildBindNak(h.callId); } Loading Loading @@ -752,16 +754,30 @@ SmbServer::Reply SmbServer::sessionSetup(Conn &c, const smb::Smb2Header &req, } std::vector<uint8_t> outTok; if (spnego) { /* we take the client's preferred mech (its optimistic token), so no * mechListMIC (RFC 4178 5): with one, Windows wants to send its own * MIC in another leg and fails the context (LsaSrv event 40962) */ std::vector<uint8_t> oid = gss::firstMechOid(mechTypes); auto m = gss::getMic(res.sessionKey, res.sessionEtype, true, res.acceptorSeq, mechTypes, res.acceptorSubkey); outTok = gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, &oid, krb5reply, m); outTok = gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, &oid, krb5reply, {}); } else { outTok = krb5reply; } s.keys = smb::deriveKeys(c.dialect, res.sessionKey, c.dialect >= smb::SMB_3_1_1 ? s.preauth : std::vector<uint8_t>()); s.haveKeys = true; reply.pdu = respond(ST_SUCCESS, 0, outTok); { /* TEMPORARY interop diagnosis: the full token exchange */ static const char *hx = "0123456789abcdef"; auto hex = [](const std::vector<uint8_t> &b) { std::string o; for (uint8_t x : b) { o += hx[x >> 4]; o += hx[x & 15]; } return o; }; std::vector<uint8_t> sk(res.sessionKey.begin(), res.sessionKey.end()); std::cerr << "[SMB] KRB in (" << tok.size() << "B): " << hex(tok) << "\n" << "[SMB] KRB out (" << outTok.size() << "B): " << hex(outTok) << "\n" << "[SMB] KRB dialect=0x" << std::hex << c.dialect << std::dec << " sesskey=" << hex(sk) << " preauth=" << hex(s.preauth) << std::endl; } reply.signKey = s.keys.signing; return reply; } Loading Loading @@ -910,6 +926,9 @@ std::vector<uint8_t> SmbServer::create(Conn &c, Session &s, const smb::Smb2Heade if (nameLen && nameOff >= 64 && size_t(nameOff-64)+nameLen <= blen) name = rpc::fromUtf16(body + (nameOff-64), nameLen); const Tree &tree = s.trees[req.treeId]; std::cerr << "[SMB] CREATE req share=" << int(tree.kind) << " name='" << name << "' access=0x" << std::hex << access << " disp=" << disposition << " opts=0x" << options << std::dec << " hdrflags=0x" << std::hex << req.flags << std::dec << std::endl; /* TEMPORARY */ Open o; o.sessionId = s.id; Loading Loading @@ -941,8 +960,11 @@ std::vector<uint8_t> SmbServer::create(Conn &c, Session &s, const smb::Smb2Heade if (tree.kind == SHARE_IPC) { std::string pname = lowerAscii(name); if (!pname.empty() && pname[0] == '\\') pname.erase(0, 1); /* protected_storage (DPAPI backup keys) and ntsvcs (PnP) exist on a * DC; we bind-reject their interfaces, so clients fail fast */ if (pname != "lsarpc" && pname != "samr" && pname != "netlogon" && pname != "lsass" && pname != "srvsvc" && pname != "wkssvc") pname != "lsass" && pname != "srvsvc" && pname != "wkssvc" && pname != "protected_storage" && pname != "ntsvcs") return errorResponse(req, ST_NAME_NOT_FOUND); uint64_t fid = c.nextFid++; o.pipe = new RpcPipe(pname); Loading Loading @@ -1186,6 +1208,7 @@ std::vector<uint8_t> SmbServer::dfsReferral(const uint8_t *in, size_t inLen, boo reqName = rpc::fromUtf16(in + 10, nlen); } while (!reqName.empty() && reqName.back() == '\0') reqName.pop_back(); std::cerr << "[SMB] DFS referral request '" << reqName << "' ex=" << ex << std::endl; /* TEMPORARY */ std::vector<std::string> parts; { std::string cur; Loading src/rpc/dcerpc.cpp +14 −3 Changes for src/rpc/dcerpc.cpp: 14 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -153,7 +153,8 @@ static void appendAuthTrailer(std::vector<uint8_t> &pdu, const std::vector<uint8 } 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::vector<uint8_t> &authToken, uint8_t authType, uint8_t authLevel, const std::function<bool(const SyntaxId &)> &knownInterface) { std::vector<uint8_t> body; wr16(body, bind.maxXmitFrag ? bind.maxXmitFrag : 4280); wr16(body, bind.maxRecvFrag ? bind.maxRecvFrag : 4280); Loading @@ -172,11 +173,21 @@ std::vector<uint8_t> buildBindAck(const BindPdu &bind, const std::string &sec, bool ok = false; for (auto &ts : c.transferSyntaxes) if (sameSyntax(ts, ndr)) { ok = true; break; } wr16(body, ok ? 0 : 2); /* 0 = acceptance, 2 = provider_rejection */ wr16(body, 0); /* reason */ /* an interface this endpoint doesn't serve: provider_rejection with * reason abstract_syntax_not_supported, so the client fails fast * with RPC_S_UNKNOWN_IF instead of retrying */ bool known = !knownInterface || knownInterface(c.abstractSyntax); if (ok && known) { wr16(body, 0); /* acceptance */ wr16(body, 0); body.insert(body.end(), ndr.uuid, ndr.uuid + 16); wr16(body, ndr.versionMajor); wr16(body, ndr.versionMinor); } else { wr16(body, 2); /* provider_rejection */ wr16(body, known ? 2 : 1); /* transfer syntaxes / abstract syntax not supported */ for (int i = 0; i < 20; ++i) body.push_back(0); /* null syntax */ } } uint16_t authLen = uint16_t(authToken.size()); std::vector<uint8_t> pdu = header(PDU_BIND_ACK, PFC_FIRST_FRAG | PFC_LAST_FRAG, bind.callId, 0, authLen); Loading src/rpc/dcerpc.h +3 −1 Changes for src/rpc/dcerpc.h: 3 added lines, 1 removed line. Original line number Diff line number Diff line #pragma once #include <cstdint> #include <functional> #include <map> #include <string> #include <vector> Loading Loading @@ -88,7 +89,8 @@ namespace fenrirds { * transfer syntaxes include NDR), echoing the secondary address. */ 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); uint8_t authType = 0, uint8_t authLevel = 0, const std::function<bool(const SyntaxId &)> &knownInterface = nullptr); std::vector<uint8_t> buildBindNak(uint32_t callId); /* build a RESPONSE carrying the NDR stub (single fragment) */ Loading src/rpc/lsarpc.cpp +3 −2 Changes for src/rpc/lsarpc.cpp: 3 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -431,7 +431,8 @@ DispatchResult lsarpcDispatch(uint16_t opnum, const std::vector<uint8_t> &stub, NdrPush p; p.ref(true); /* [out,ref] **info -> the union pointer */ p.u16(level); /* union discriminant */ if (level == 5) { /* PolicyAccountDomainInformation */ if (level == 3 || level == 5) { /* PolicyPrimaryDomain- / AccountDomainInformation: * both lsa_DomainInfo (name + domain SID) */ p.align(4); pushLsaString(p, di.netbios, false); p.ref(true); /* sid pointer */ Loading @@ -452,7 +453,7 @@ DispatchResult lsarpcDispatch(uint16_t opnum, const std::vector<uint8_t> &stub, /* unsupported level: return a null pointer + invalid info class */ p.buf.clear(); p.ref(false); p.u32(0xC00000DD); /* STATUS_INVALID_INFO_CLASS */ p.u32(0xC0000003); /* STATUS_INVALID_INFO_CLASS */ r.stub = p.buf; return r; } Loading Loading
plugin/ad/ad.cpp +2 −4 Changes for plugin/ad/ad.cpp: 2 added lines, 4 removed lines. Original line number Diff line number Diff line Loading @@ -1544,10 +1544,8 @@ std::vector<uint8_t> ActiveDirectory::handleBindRequest(const uint8_t *content, std::vector<uint8_t> creds; if (spnego) { std::vector<uint8_t> krb5oid = gss::firstMechOid(session.gssMechTypes); std::vector<uint8_t> mic = gss::getMic(session.gssKey, session.gssEtype, true, session.sendSeq++, session.gssMechTypes, session.gssAccSubkey); creds = gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, &krb5oid, krb5reply, mic); /* the client's preferred mech: no mechListMIC (see smb.cpp) */ creds = gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, &krb5oid, krb5reply, {}); /* GSS-SPNEGO has no RFC 4752 layer negotiation: the bind is * done, and the client signs or seals the following PDUs as * it asked for in its authenticator (MS-ADTS 5.1.1.1.2) */ Loading
plugin/smb/smb.cpp +28 −5 Changes for plugin/smb/smb.cpp: 28 added lines, 5 removed lines. Original line number Diff line number Diff line Loading @@ -166,7 +166,9 @@ void RpcPipe::handlePdu(const std::vector<uint8_t> &pdu) { << " iface=" << _name << std::endl; _boundAbstract = b.contexts[0].abstractSyntax; _bound = true; resp = rpc::buildBindAck(b, "\\PIPE\\" + _name); 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); }); } else { resp = rpc::buildBindNak(h.callId); } Loading Loading @@ -752,16 +754,30 @@ SmbServer::Reply SmbServer::sessionSetup(Conn &c, const smb::Smb2Header &req, } std::vector<uint8_t> outTok; if (spnego) { /* we take the client's preferred mech (its optimistic token), so no * mechListMIC (RFC 4178 5): with one, Windows wants to send its own * MIC in another leg and fails the context (LsaSrv event 40962) */ std::vector<uint8_t> oid = gss::firstMechOid(mechTypes); auto m = gss::getMic(res.sessionKey, res.sessionEtype, true, res.acceptorSeq, mechTypes, res.acceptorSubkey); outTok = gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, &oid, krb5reply, m); outTok = gss::buildSpnegoResp(gss::ACCEPT_COMPLETED, &oid, krb5reply, {}); } else { outTok = krb5reply; } s.keys = smb::deriveKeys(c.dialect, res.sessionKey, c.dialect >= smb::SMB_3_1_1 ? s.preauth : std::vector<uint8_t>()); s.haveKeys = true; reply.pdu = respond(ST_SUCCESS, 0, outTok); { /* TEMPORARY interop diagnosis: the full token exchange */ static const char *hx = "0123456789abcdef"; auto hex = [](const std::vector<uint8_t> &b) { std::string o; for (uint8_t x : b) { o += hx[x >> 4]; o += hx[x & 15]; } return o; }; std::vector<uint8_t> sk(res.sessionKey.begin(), res.sessionKey.end()); std::cerr << "[SMB] KRB in (" << tok.size() << "B): " << hex(tok) << "\n" << "[SMB] KRB out (" << outTok.size() << "B): " << hex(outTok) << "\n" << "[SMB] KRB dialect=0x" << std::hex << c.dialect << std::dec << " sesskey=" << hex(sk) << " preauth=" << hex(s.preauth) << std::endl; } reply.signKey = s.keys.signing; return reply; } Loading Loading @@ -910,6 +926,9 @@ std::vector<uint8_t> SmbServer::create(Conn &c, Session &s, const smb::Smb2Heade if (nameLen && nameOff >= 64 && size_t(nameOff-64)+nameLen <= blen) name = rpc::fromUtf16(body + (nameOff-64), nameLen); const Tree &tree = s.trees[req.treeId]; std::cerr << "[SMB] CREATE req share=" << int(tree.kind) << " name='" << name << "' access=0x" << std::hex << access << " disp=" << disposition << " opts=0x" << options << std::dec << " hdrflags=0x" << std::hex << req.flags << std::dec << std::endl; /* TEMPORARY */ Open o; o.sessionId = s.id; Loading Loading @@ -941,8 +960,11 @@ std::vector<uint8_t> SmbServer::create(Conn &c, Session &s, const smb::Smb2Heade if (tree.kind == SHARE_IPC) { std::string pname = lowerAscii(name); if (!pname.empty() && pname[0] == '\\') pname.erase(0, 1); /* protected_storage (DPAPI backup keys) and ntsvcs (PnP) exist on a * DC; we bind-reject their interfaces, so clients fail fast */ if (pname != "lsarpc" && pname != "samr" && pname != "netlogon" && pname != "lsass" && pname != "srvsvc" && pname != "wkssvc") pname != "lsass" && pname != "srvsvc" && pname != "wkssvc" && pname != "protected_storage" && pname != "ntsvcs") return errorResponse(req, ST_NAME_NOT_FOUND); uint64_t fid = c.nextFid++; o.pipe = new RpcPipe(pname); Loading Loading @@ -1186,6 +1208,7 @@ std::vector<uint8_t> SmbServer::dfsReferral(const uint8_t *in, size_t inLen, boo reqName = rpc::fromUtf16(in + 10, nlen); } while (!reqName.empty() && reqName.back() == '\0') reqName.pop_back(); std::cerr << "[SMB] DFS referral request '" << reqName << "' ex=" << ex << std::endl; /* TEMPORARY */ std::vector<std::string> parts; { std::string cur; Loading
src/rpc/dcerpc.cpp +14 −3 Changes for src/rpc/dcerpc.cpp: 14 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -153,7 +153,8 @@ static void appendAuthTrailer(std::vector<uint8_t> &pdu, const std::vector<uint8 } 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::vector<uint8_t> &authToken, uint8_t authType, uint8_t authLevel, const std::function<bool(const SyntaxId &)> &knownInterface) { std::vector<uint8_t> body; wr16(body, bind.maxXmitFrag ? bind.maxXmitFrag : 4280); wr16(body, bind.maxRecvFrag ? bind.maxRecvFrag : 4280); Loading @@ -172,11 +173,21 @@ std::vector<uint8_t> buildBindAck(const BindPdu &bind, const std::string &sec, bool ok = false; for (auto &ts : c.transferSyntaxes) if (sameSyntax(ts, ndr)) { ok = true; break; } wr16(body, ok ? 0 : 2); /* 0 = acceptance, 2 = provider_rejection */ wr16(body, 0); /* reason */ /* an interface this endpoint doesn't serve: provider_rejection with * reason abstract_syntax_not_supported, so the client fails fast * with RPC_S_UNKNOWN_IF instead of retrying */ bool known = !knownInterface || knownInterface(c.abstractSyntax); if (ok && known) { wr16(body, 0); /* acceptance */ wr16(body, 0); body.insert(body.end(), ndr.uuid, ndr.uuid + 16); wr16(body, ndr.versionMajor); wr16(body, ndr.versionMinor); } else { wr16(body, 2); /* provider_rejection */ wr16(body, known ? 2 : 1); /* transfer syntaxes / abstract syntax not supported */ for (int i = 0; i < 20; ++i) body.push_back(0); /* null syntax */ } } uint16_t authLen = uint16_t(authToken.size()); std::vector<uint8_t> pdu = header(PDU_BIND_ACK, PFC_FIRST_FRAG | PFC_LAST_FRAG, bind.callId, 0, authLen); Loading
src/rpc/dcerpc.h +3 −1 Changes for src/rpc/dcerpc.h: 3 added lines, 1 removed line. Original line number Diff line number Diff line #pragma once #include <cstdint> #include <functional> #include <map> #include <string> #include <vector> Loading Loading @@ -88,7 +89,8 @@ namespace fenrirds { * transfer syntaxes include NDR), echoing the secondary address. */ 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); uint8_t authType = 0, uint8_t authLevel = 0, const std::function<bool(const SyntaxId &)> &knownInterface = nullptr); std::vector<uint8_t> buildBindNak(uint32_t callId); /* build a RESPONSE carrying the NDR stub (single fragment) */ Loading
src/rpc/lsarpc.cpp +3 −2 Changes for src/rpc/lsarpc.cpp: 3 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -431,7 +431,8 @@ DispatchResult lsarpcDispatch(uint16_t opnum, const std::vector<uint8_t> &stub, NdrPush p; p.ref(true); /* [out,ref] **info -> the union pointer */ p.u16(level); /* union discriminant */ if (level == 5) { /* PolicyAccountDomainInformation */ if (level == 3 || level == 5) { /* PolicyPrimaryDomain- / AccountDomainInformation: * both lsa_DomainInfo (name + domain SID) */ p.align(4); pushLsaString(p, di.netbios, false); p.ref(true); /* sid pointer */ Loading @@ -452,7 +453,7 @@ DispatchResult lsarpcDispatch(uint16_t opnum, const std::vector<uint8_t> &stub, /* unsupported level: return a null pointer + invalid info class */ p.buf.clear(); p.ref(false); p.u32(0xC00000DD); /* STATUS_INVALID_INFO_CLASS */ p.u32(0xC0000003); /* STATUS_INVALID_INFO_CLASS */ r.stub = p.buf; return r; } Loading