Loading src/rpc.cpp +383 −2 Changes for src/rpc.cpp: 383 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -29,6 +29,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <algorithm> #include <cstring> #include <utility> #include <grp.h> #include <pwd.h> #if __has_include(<sys/uid_r.h>) #include <sys/uid_r.h> // systempp: UUID and GUID users #define SHAREDD_UID_R 1 #endif #include "adquery.h" namespace sharedd { Loading Loading @@ -226,6 +237,246 @@ struct ShareEntry { std::string remark; }; // ---- lsarpc: SIDs <-> names for Windows' security dialogs ---- enum : uint16_t { SidUser = 1, SidGroup = 2, SidDomain = 3, SidAlias = 4, SidWknGroup = 5, SidUnknown = 8 }; constexpr uint32_t StSomeNotMapped = 0x00000107, StNoneMapped = 0xC0000073; using Sid = std::vector<uint8_t>; // binary Sid makeSid(uint64_t auth, const std::vector<uint32_t> &subs) { Sid s = {1, (uint8_t)subs.size()}; for (int i = 5; i >= 0; --i) s.push_back((uint8_t)(auth >> (8 * i))); for (uint32_t v : subs) for (int i = 0; i < 4; ++i) s.push_back((uint8_t)(v >> (8 * i))); return s; } uint64_t sidAuth(const Sid &s) { uint64_t a = 0; for (int i = 2; i < 8; ++i) a = a << 8 | s[i]; return a; } std::vector<uint32_t> sidSubs(const Sid &s) { std::vector<uint32_t> out; for (size_t off = 8; off + 4 <= s.size(); off += 4) out.push_back(le32(&s[off])); return out; } Sid sidDomain(const Sid &s) { std::vector<uint32_t> subs = sidSubs(s); if (!subs.empty()) subs.pop_back(); return makeSid(sidAuth(s), subs); } struct Translation { uint16_t type = SidUnknown; std::string name; Sid sid; int dom = -1; }; struct RefDomains { std::vector<std::pair<std::string, Sid>> list; int add(const std::string &name, const Sid &sid) { for (size_t i = 0; i < list.size(); ++i) if (list[i].second == sid) return (int)i; list.push_back({name, sid}); return (int)list.size() - 1; } }; struct WellKnown { uint64_t auth; std::vector<uint32_t> subs; const char *domain, *name; uint16_t type; }; const std::vector<WellKnown> &wellKnown() { static const std::vector<WellKnown> w = { {1, {0}, "", "Everyone", SidWknGroup}, {3, {0}, "", "CREATOR OWNER", SidWknGroup}, {3, {1}, "", "CREATOR GROUP", SidWknGroup}, {5, {2}, "NT AUTHORITY", "NETWORK", SidWknGroup}, {5, {7}, "NT AUTHORITY", "ANONYMOUS LOGON", SidWknGroup}, {5, {11}, "NT AUTHORITY", "Authenticated Users", SidWknGroup}, {5, {18}, "NT AUTHORITY", "SYSTEM", SidWknGroup}, {5, {32, 544}, "BUILTIN", "Administrators", SidAlias}, {5, {32, 545}, "BUILTIN", "Users", SidAlias}, {5, {32, 546}, "BUILTIN", "Guests", SidAlias}, }; return w; } // FenrirOS' own SIDs (ksmbd): S-1-22-1-<uid>, S-1-22-2-<gid>, and // S-1-22-<kind>-w0-w1-w2-w3 for UUID (3 user, 4 group) and GUID (5, 6) ids constexpr const char *UnixUser = "Unix User", *UnixGroup = "Unix Group"; #ifdef SHAREDD_UID_R std::string idName(const uid_r_t &id, bool group) { if (group) { struct group_id_r *gr = getgrgid_id_r(&id); return gr && gr->gr_name ? gr->gr_name : std::string(); } struct passwd_id_r *pw = getpwuid_id_r(&id); return pw && pw->pw_name ? pw->pw_name : std::string(); } // a domain account's objectSid (adconnect SIDOF), empty if it has none Sid objectSid(const uid_r_t &id, bool group) { char buf[UID_R_STR_LEN]; std::string reply; if (!adconnectQuery(std::string("SIDOF ") + (group ? "g " : "u ") + uid_r_to_str(&id, buf), reply, 2)) return {}; std::vector<std::string> f = firstRecord(reply); unsigned char bin[SidBinSize]; if (f.size() != 4 || f[0] != "S" || !sidBinary(f[3], bin)) return {}; return Sid(bin, bin + 8 + 4 * bin[1]); } // the SID ksmbd shows for an id without an objectSid Sid nativeSid(const uid_r_t &id, bool group) { if (id.type == UID_R_LEGACY) return makeSid(22, {group ? 2u : 1u, id.legacy}); std::vector<uint32_t> subs = {(id.type == UID_R_UUID ? 3u : 5u) + (group ? 1u : 0u)}; for (int i = 0; i < 4; ++i) subs.push_back((uint32_t)id.uuid[4 * i] << 24 | (uint32_t)id.uuid[4 * i + 1] << 16 | (uint32_t)id.uuid[4 * i + 2] << 8 | id.uuid[4 * i + 3]); return makeSid(22, subs); } #endif Translation translateSid(const Sid &sid, const std::string &workgroup, RefDomains &doms) { Translation t; t.sid = sid; if (sid.size() < 8) return t; uint64_t auth = sidAuth(sid); std::vector<uint32_t> subs = sidSubs(sid); for (const WellKnown &w : wellKnown()) if (w.auth == auth && w.subs == subs) { t.type = w.type; t.name = w.name; t.dom = doms.add(w.domain, sidDomain(sid)); return t; } #ifdef SHAREDD_UID_R uid_r_t id; memset(&id, 0, sizeof id); bool group = false, have = false; if (auth == 22 && subs.size() == 2 && (subs[0] == 1 || subs[0] == 2)) { id = uid_r_legacy(subs[1]); group = subs[0] == 2; have = true; } else if (auth == 22 && subs.size() == 5 && subs[0] >= 3 && subs[0] <= 6) { id.type = subs[0] <= 4 ? UID_R_UUID : UID_R_GUID; for (int i = 0; i < 4; ++i) for (int k = 0; k < 4; ++k) id.uuid[4 * i + k] = (uint8_t)(subs[1 + i] >> (24 - 8 * k)); group = subs[0] == 4 || subs[0] == 6; have = true; } else if (auth == 5 && subs.size() >= 2 && subs[0] == 21) { // a domain account: its id from adconnect, its name from the user db std::string reply; std::string text = sidText(sid.data(), sid.size()); if (!text.empty() && adconnectQuery("IDOF " + text, reply, 2)) { std::vector<std::string> f = firstRecord(reply); if (f.size() == 4 && f[0] == "S" && uid_r_from_str(f[2].c_str(), &id) == 0) { group = f[1] == "g"; std::string name = idName(id, group); if (!name.empty()) { t.type = group ? SidGroup : SidUser; t.name = name; t.dom = doms.add(workgroup, sidDomain(sid)); } } } return t; } if (have) { std::string name = idName(id, group); if (!name.empty()) { t.type = group ? SidGroup : SidUser; t.name = name; t.dom = doms.add(group ? UnixGroup : UnixUser, sidDomain(sid)); } } #else (void)workgroup; #endif return t; } bool same(const std::string &a, const std::string &b) { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), [](char x, char y) { return tolower((unsigned char)x) == tolower((unsigned char)y); }); } Translation translateName(const std::string &full, const std::string &workgroup, RefDomains &doms) { Translation t; std::string dom, name = full; size_t bs = full.find('\\'); if (bs != std::string::npos) { dom = full.substr(0, bs); name = full.substr(bs + 1); } for (const WellKnown &w : wellKnown()) if (same(name, w.name) && (dom.empty() || same(dom, w.domain))) { t.type = w.type; t.name = w.name; t.sid = makeSid(w.auth, w.subs); t.dom = doms.add(w.domain, sidDomain(t.sid)); return t; } #ifdef SHAREDD_UID_R // "Unix User\x" only users, "Unix Group\x" only groups bool users = dom.empty() || !same(dom, UnixGroup), groups = dom.empty() || !same(dom, UnixUser); for (int pass = 0; pass < 2; ++pass) { bool group = pass == 1; if (group ? !groups : !users) continue; uid_r_t id; if (group) { struct group_id_r *gr = getgrnam_id_r(name.c_str()); if (!gr) continue; id = gr->gr_gid; } else { struct passwd_id_r *pw = getpwnam_id_r(name.c_str()); if (!pw) continue; id = pw->pw_uid; } Sid os = objectSid(id, group); t.type = group ? SidGroup : SidUser; t.name = name; t.sid = os.empty() ? nativeSid(id, group) : os; t.dom = doms.add(os.empty() ? (group ? UnixGroup : UnixUser) : workgroup, sidDomain(t.sid)); return t; } #else (void)workgroup; #endif return t; } void pushRefDomains(NdrOut &o, const RefDomains &doms) { o.ptr(true); o.u32((uint32_t)doms.list.size()); o.ptr(!doms.list.empty()); o.u32(32); // max_size if (doms.list.empty()) return; o.u32((uint32_t)doms.list.size()); for (auto &d : doms.list) { o.lsaString(d.first, true); o.ptr(true); } for (auto &d : doms.list) { o.lsaBuffer(d.first, true); o.sid(d.second); } } uint32_t mappedStatus(const std::vector<Translation> &t, uint32_t &mapped) { mapped = 0; for (auto &x : t) if (x.type != SidUnknown) ++mapped; return mapped == t.size() ? 0 : mapped ? StSomeNotMapped : StNoneMapped; } } // namespace std::string RpcPipes::hostName() const { Loading @@ -236,6 +487,7 @@ uint32_t RpcPipes::open(uint32_t handle, uint32_t method) { Pipe p; if (method & MethodSrvsvc) p.iface = Iface::Srvsvc; else if (method & MethodWkssvc) p.iface = Iface::Wkssvc; else if (method & MethodLsarpc) p.iface = Iface::Lsarpc; _pipes[handle] = p; return p.iface == Iface::None ? RpcNotImplemented : RpcOk; } Loading Loading @@ -312,7 +564,7 @@ void RpcPipes::bind(Pipe &p, const uint8_t *d, size_t len, bool alter) { uint32_t callId = le32(d + 12); p.maxRecv = std::min<uint16_t>(maxRecv ? maxRecv : MaxFrag, MaxFrag); if (!p.assocGroup) p.assocGroup = assoc ? assoc : 0x53f0; const Syntax &want = p.iface == Iface::Srvsvc ? Srvsvc : Wkssvc; const Syntax &want = p.iface == Iface::Srvsvc ? Srvsvc : p.iface == Iface::Lsarpc ? Lsarpc : Wkssvc; Bytes b; header(b, alter ? PtypeAlterResp : PtypeBindAck, FlagFirst | FlagLast, callId); Loading @@ -322,7 +574,8 @@ void RpcPipes::bind(Pipe &p, const uint8_t *d, size_t len, bool alter) { if (alter) { put16(b, 0); } else { std::string port = p.iface == Iface::Srvsvc ? "\\PIPE\\srvsvc" : "\\PIPE\\wkssvc"; std::string port = p.iface == Iface::Srvsvc ? "\\PIPE\\srvsvc" : p.iface == Iface::Lsarpc ? "\\PIPE\\lsass" : "\\PIPE\\wkssvc"; put16(b, (uint16_t)(port.size() + 1)); b.insert(b.end(), port.begin(), port.end()); b.push_back(0); Loading Loading @@ -406,6 +659,14 @@ RpcPipes::Bytes RpcPipes::call(Pipe &p, uint16_t opnum, const Bytes &stub, bool if (opnum == 21) return serverGetInfo(stub); } else if (p.iface == Iface::Wkssvc && opnum == 0) { return wkstaGetInfo(stub); } else if (p.iface == Iface::Lsarpc) { switch (opnum) { case 0: return lsaClose(); case 6: case 44: return lsaOpenPolicy(); case 15: case 57: case 76: return lsaLookupSids(opnum, stub, isFault); case 14: case 58: case 68: case 77: return lsaLookupNames(opnum, stub, isFault); } } isFault = true; return {}; Loading Loading @@ -572,4 +833,124 @@ RpcPipes::Bytes RpcPipes::wkstaGetInfo(const Bytes &in) { return w.b; } // ---- lsarpc ---- RpcPipes::Bytes RpcPipes::lsaOpenPolicy() { // the policy handle: nothing behind it, any non-null value does NdrOut o; o.u32(0); static uint32_t serial = 0; o.u32(++serial); for (int i = 0; i < 3; ++i) o.u32(0x53484152); // "SHAR" o.u32(0); // NT_STATUS_OK return o.b; } RpcPipes::Bytes RpcPipes::lsaClose() { NdrOut o; for (int i = 0; i < 5; ++i) o.u32(0); // the null handle o.u32(0); return o.b; } // LookupSids (15), LookupSids2 (57), LookupSids3 (76, no handle) RpcPipes::Bytes RpcPipes::lsaLookupSids(uint16_t opnum, const Bytes &stub, bool &isFault) { NdrIn in(stub); if (opnum != 76) in.skip(20); uint32_t num = in.u32(); std::vector<Sid> sids; if (in.u32()) { uint32_t max = in.u32(); if (max != num || max > 1000) in.ok = false; std::vector<uint32_t> ptrs; for (uint32_t i = 0; in.ok && i < max; ++i) ptrs.push_back(in.u32()); for (uint32_t p : ptrs) sids.push_back(p ? in.sid() : Sid()); } in.u32(); // [in,out] names: count if (in.u32()) in.ok = false; // and none in the request if (!in.ok) { isFault = true; return {}; } RefDomains doms; std::vector<Translation> out; for (const Sid &s : sids) out.push_back(translateSid(s, _workgroup, doms)); NdrOut o; pushRefDomains(o, doms); o.u32((uint32_t)out.size()); o.ptr(!out.empty()); if (!out.empty()) { o.u32((uint32_t)out.size()); for (auto &t : out) { o.u16(t.type); o.align(4); o.lsaString(t.name, false); o.u32(t.dom < 0 ? 0xffffffffu : (uint32_t)t.dom); if (opnum != 15) o.u32(0); // lsa_TranslatedName2: flags } for (auto &t : out) o.lsaBuffer(t.name, false); } uint32_t mapped, status = mappedStatus(out, mapped); o.u32(mapped); o.u32(status); return o.b; } // LookupNames (14), LookupNames2 (58), LookupNames3 (68), LookupNames4 (77, no handle) RpcPipes::Bytes RpcPipes::lsaLookupNames(uint16_t opnum, const Bytes &stub, bool &isFault) { NdrIn in(stub); if (opnum != 77) in.skip(20); uint32_t num = in.u32(); uint32_t max = in.u32(); if (max != num || max > 1000) in.ok = false; std::vector<uint32_t> ptrs; for (uint32_t i = 0; in.ok && i < num; ++i) { in.u16(); in.u16(); ptrs.push_back(in.u32()); } std::vector<std::string> names; for (uint32_t p : ptrs) { // a conformant varying string without NUL names.push_back(p ? in.str() : std::string()); } in.u32(); if (in.u32()) in.ok = false; if (!in.ok) { isFault = true; return {}; } RefDomains doms; std::vector<Translation> out; for (const std::string &n : names) out.push_back(translateName(n, _workgroup, doms)); bool v3 = opnum == 68 || opnum == 77; NdrOut o; pushRefDomains(o, doms); o.u32((uint32_t)out.size()); o.ptr(!out.empty()); if (!out.empty()) { o.u32((uint32_t)out.size()); for (auto &t : out) { o.u16(t.type); o.align(4); if (v3) o.ptr(t.type != SidUnknown); // lsa_TranslatedSid3: the whole SID else o.u32(t.type == SidUnknown || t.sid.size() < 12 ? 0xffffffffu : le32(&t.sid[t.sid.size() - 4])); o.u32(t.dom < 0 ? 0xffffffffu : (uint32_t)t.dom); if (opnum != 14) o.u32(0); } if (v3) for (auto &t : out) if (t.type != SidUnknown) o.sid(t.sid); } uint32_t mapped, status = mappedStatus(out, mapped); o.u32(mapped); o.u32(status); return o.b; } } // namespace sharedd src/rpc.h +6 −1 Changes for src/rpc.h: 6 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ public: uint32_t transceive(uint32_t handle, const uint8_t *data, size_t len, Bytes &out); private: enum class Iface { None, Srvsvc, Wkssvc }; enum class Iface { None, Srvsvc, Wkssvc, Lsarpc }; struct Pipe { Iface iface = Iface::None; uint16_t maxRecv = 4280; // the client's fragment size Loading @@ -93,6 +93,11 @@ private: Bytes shareGetInfo(const Bytes &in); Bytes serverGetInfo(const Bytes &in); Bytes wkstaGetInfo(const Bytes &in); // lsarpc: names for the SIDs of security descriptors and back Bytes lsaOpenPolicy(); Bytes lsaClose(); Bytes lsaLookupSids(uint16_t opnum, const Bytes &in, bool &fault); Bytes lsaLookupNames(uint16_t opnum, const Bytes &in, bool &fault); std::string hostName() const; Loading Loading
src/rpc.cpp +383 −2 Changes for src/rpc.cpp: 383 added lines, 2 removed lines. Original line number Diff line number Diff line Loading @@ -29,6 +29,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <algorithm> #include <cstring> #include <utility> #include <grp.h> #include <pwd.h> #if __has_include(<sys/uid_r.h>) #include <sys/uid_r.h> // systempp: UUID and GUID users #define SHAREDD_UID_R 1 #endif #include "adquery.h" namespace sharedd { Loading Loading @@ -226,6 +237,246 @@ struct ShareEntry { std::string remark; }; // ---- lsarpc: SIDs <-> names for Windows' security dialogs ---- enum : uint16_t { SidUser = 1, SidGroup = 2, SidDomain = 3, SidAlias = 4, SidWknGroup = 5, SidUnknown = 8 }; constexpr uint32_t StSomeNotMapped = 0x00000107, StNoneMapped = 0xC0000073; using Sid = std::vector<uint8_t>; // binary Sid makeSid(uint64_t auth, const std::vector<uint32_t> &subs) { Sid s = {1, (uint8_t)subs.size()}; for (int i = 5; i >= 0; --i) s.push_back((uint8_t)(auth >> (8 * i))); for (uint32_t v : subs) for (int i = 0; i < 4; ++i) s.push_back((uint8_t)(v >> (8 * i))); return s; } uint64_t sidAuth(const Sid &s) { uint64_t a = 0; for (int i = 2; i < 8; ++i) a = a << 8 | s[i]; return a; } std::vector<uint32_t> sidSubs(const Sid &s) { std::vector<uint32_t> out; for (size_t off = 8; off + 4 <= s.size(); off += 4) out.push_back(le32(&s[off])); return out; } Sid sidDomain(const Sid &s) { std::vector<uint32_t> subs = sidSubs(s); if (!subs.empty()) subs.pop_back(); return makeSid(sidAuth(s), subs); } struct Translation { uint16_t type = SidUnknown; std::string name; Sid sid; int dom = -1; }; struct RefDomains { std::vector<std::pair<std::string, Sid>> list; int add(const std::string &name, const Sid &sid) { for (size_t i = 0; i < list.size(); ++i) if (list[i].second == sid) return (int)i; list.push_back({name, sid}); return (int)list.size() - 1; } }; struct WellKnown { uint64_t auth; std::vector<uint32_t> subs; const char *domain, *name; uint16_t type; }; const std::vector<WellKnown> &wellKnown() { static const std::vector<WellKnown> w = { {1, {0}, "", "Everyone", SidWknGroup}, {3, {0}, "", "CREATOR OWNER", SidWknGroup}, {3, {1}, "", "CREATOR GROUP", SidWknGroup}, {5, {2}, "NT AUTHORITY", "NETWORK", SidWknGroup}, {5, {7}, "NT AUTHORITY", "ANONYMOUS LOGON", SidWknGroup}, {5, {11}, "NT AUTHORITY", "Authenticated Users", SidWknGroup}, {5, {18}, "NT AUTHORITY", "SYSTEM", SidWknGroup}, {5, {32, 544}, "BUILTIN", "Administrators", SidAlias}, {5, {32, 545}, "BUILTIN", "Users", SidAlias}, {5, {32, 546}, "BUILTIN", "Guests", SidAlias}, }; return w; } // FenrirOS' own SIDs (ksmbd): S-1-22-1-<uid>, S-1-22-2-<gid>, and // S-1-22-<kind>-w0-w1-w2-w3 for UUID (3 user, 4 group) and GUID (5, 6) ids constexpr const char *UnixUser = "Unix User", *UnixGroup = "Unix Group"; #ifdef SHAREDD_UID_R std::string idName(const uid_r_t &id, bool group) { if (group) { struct group_id_r *gr = getgrgid_id_r(&id); return gr && gr->gr_name ? gr->gr_name : std::string(); } struct passwd_id_r *pw = getpwuid_id_r(&id); return pw && pw->pw_name ? pw->pw_name : std::string(); } // a domain account's objectSid (adconnect SIDOF), empty if it has none Sid objectSid(const uid_r_t &id, bool group) { char buf[UID_R_STR_LEN]; std::string reply; if (!adconnectQuery(std::string("SIDOF ") + (group ? "g " : "u ") + uid_r_to_str(&id, buf), reply, 2)) return {}; std::vector<std::string> f = firstRecord(reply); unsigned char bin[SidBinSize]; if (f.size() != 4 || f[0] != "S" || !sidBinary(f[3], bin)) return {}; return Sid(bin, bin + 8 + 4 * bin[1]); } // the SID ksmbd shows for an id without an objectSid Sid nativeSid(const uid_r_t &id, bool group) { if (id.type == UID_R_LEGACY) return makeSid(22, {group ? 2u : 1u, id.legacy}); std::vector<uint32_t> subs = {(id.type == UID_R_UUID ? 3u : 5u) + (group ? 1u : 0u)}; for (int i = 0; i < 4; ++i) subs.push_back((uint32_t)id.uuid[4 * i] << 24 | (uint32_t)id.uuid[4 * i + 1] << 16 | (uint32_t)id.uuid[4 * i + 2] << 8 | id.uuid[4 * i + 3]); return makeSid(22, subs); } #endif Translation translateSid(const Sid &sid, const std::string &workgroup, RefDomains &doms) { Translation t; t.sid = sid; if (sid.size() < 8) return t; uint64_t auth = sidAuth(sid); std::vector<uint32_t> subs = sidSubs(sid); for (const WellKnown &w : wellKnown()) if (w.auth == auth && w.subs == subs) { t.type = w.type; t.name = w.name; t.dom = doms.add(w.domain, sidDomain(sid)); return t; } #ifdef SHAREDD_UID_R uid_r_t id; memset(&id, 0, sizeof id); bool group = false, have = false; if (auth == 22 && subs.size() == 2 && (subs[0] == 1 || subs[0] == 2)) { id = uid_r_legacy(subs[1]); group = subs[0] == 2; have = true; } else if (auth == 22 && subs.size() == 5 && subs[0] >= 3 && subs[0] <= 6) { id.type = subs[0] <= 4 ? UID_R_UUID : UID_R_GUID; for (int i = 0; i < 4; ++i) for (int k = 0; k < 4; ++k) id.uuid[4 * i + k] = (uint8_t)(subs[1 + i] >> (24 - 8 * k)); group = subs[0] == 4 || subs[0] == 6; have = true; } else if (auth == 5 && subs.size() >= 2 && subs[0] == 21) { // a domain account: its id from adconnect, its name from the user db std::string reply; std::string text = sidText(sid.data(), sid.size()); if (!text.empty() && adconnectQuery("IDOF " + text, reply, 2)) { std::vector<std::string> f = firstRecord(reply); if (f.size() == 4 && f[0] == "S" && uid_r_from_str(f[2].c_str(), &id) == 0) { group = f[1] == "g"; std::string name = idName(id, group); if (!name.empty()) { t.type = group ? SidGroup : SidUser; t.name = name; t.dom = doms.add(workgroup, sidDomain(sid)); } } } return t; } if (have) { std::string name = idName(id, group); if (!name.empty()) { t.type = group ? SidGroup : SidUser; t.name = name; t.dom = doms.add(group ? UnixGroup : UnixUser, sidDomain(sid)); } } #else (void)workgroup; #endif return t; } bool same(const std::string &a, const std::string &b) { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin(), [](char x, char y) { return tolower((unsigned char)x) == tolower((unsigned char)y); }); } Translation translateName(const std::string &full, const std::string &workgroup, RefDomains &doms) { Translation t; std::string dom, name = full; size_t bs = full.find('\\'); if (bs != std::string::npos) { dom = full.substr(0, bs); name = full.substr(bs + 1); } for (const WellKnown &w : wellKnown()) if (same(name, w.name) && (dom.empty() || same(dom, w.domain))) { t.type = w.type; t.name = w.name; t.sid = makeSid(w.auth, w.subs); t.dom = doms.add(w.domain, sidDomain(t.sid)); return t; } #ifdef SHAREDD_UID_R // "Unix User\x" only users, "Unix Group\x" only groups bool users = dom.empty() || !same(dom, UnixGroup), groups = dom.empty() || !same(dom, UnixUser); for (int pass = 0; pass < 2; ++pass) { bool group = pass == 1; if (group ? !groups : !users) continue; uid_r_t id; if (group) { struct group_id_r *gr = getgrnam_id_r(name.c_str()); if (!gr) continue; id = gr->gr_gid; } else { struct passwd_id_r *pw = getpwnam_id_r(name.c_str()); if (!pw) continue; id = pw->pw_uid; } Sid os = objectSid(id, group); t.type = group ? SidGroup : SidUser; t.name = name; t.sid = os.empty() ? nativeSid(id, group) : os; t.dom = doms.add(os.empty() ? (group ? UnixGroup : UnixUser) : workgroup, sidDomain(t.sid)); return t; } #else (void)workgroup; #endif return t; } void pushRefDomains(NdrOut &o, const RefDomains &doms) { o.ptr(true); o.u32((uint32_t)doms.list.size()); o.ptr(!doms.list.empty()); o.u32(32); // max_size if (doms.list.empty()) return; o.u32((uint32_t)doms.list.size()); for (auto &d : doms.list) { o.lsaString(d.first, true); o.ptr(true); } for (auto &d : doms.list) { o.lsaBuffer(d.first, true); o.sid(d.second); } } uint32_t mappedStatus(const std::vector<Translation> &t, uint32_t &mapped) { mapped = 0; for (auto &x : t) if (x.type != SidUnknown) ++mapped; return mapped == t.size() ? 0 : mapped ? StSomeNotMapped : StNoneMapped; } } // namespace std::string RpcPipes::hostName() const { Loading @@ -236,6 +487,7 @@ uint32_t RpcPipes::open(uint32_t handle, uint32_t method) { Pipe p; if (method & MethodSrvsvc) p.iface = Iface::Srvsvc; else if (method & MethodWkssvc) p.iface = Iface::Wkssvc; else if (method & MethodLsarpc) p.iface = Iface::Lsarpc; _pipes[handle] = p; return p.iface == Iface::None ? RpcNotImplemented : RpcOk; } Loading Loading @@ -312,7 +564,7 @@ void RpcPipes::bind(Pipe &p, const uint8_t *d, size_t len, bool alter) { uint32_t callId = le32(d + 12); p.maxRecv = std::min<uint16_t>(maxRecv ? maxRecv : MaxFrag, MaxFrag); if (!p.assocGroup) p.assocGroup = assoc ? assoc : 0x53f0; const Syntax &want = p.iface == Iface::Srvsvc ? Srvsvc : Wkssvc; const Syntax &want = p.iface == Iface::Srvsvc ? Srvsvc : p.iface == Iface::Lsarpc ? Lsarpc : Wkssvc; Bytes b; header(b, alter ? PtypeAlterResp : PtypeBindAck, FlagFirst | FlagLast, callId); Loading @@ -322,7 +574,8 @@ void RpcPipes::bind(Pipe &p, const uint8_t *d, size_t len, bool alter) { if (alter) { put16(b, 0); } else { std::string port = p.iface == Iface::Srvsvc ? "\\PIPE\\srvsvc" : "\\PIPE\\wkssvc"; std::string port = p.iface == Iface::Srvsvc ? "\\PIPE\\srvsvc" : p.iface == Iface::Lsarpc ? "\\PIPE\\lsass" : "\\PIPE\\wkssvc"; put16(b, (uint16_t)(port.size() + 1)); b.insert(b.end(), port.begin(), port.end()); b.push_back(0); Loading Loading @@ -406,6 +659,14 @@ RpcPipes::Bytes RpcPipes::call(Pipe &p, uint16_t opnum, const Bytes &stub, bool if (opnum == 21) return serverGetInfo(stub); } else if (p.iface == Iface::Wkssvc && opnum == 0) { return wkstaGetInfo(stub); } else if (p.iface == Iface::Lsarpc) { switch (opnum) { case 0: return lsaClose(); case 6: case 44: return lsaOpenPolicy(); case 15: case 57: case 76: return lsaLookupSids(opnum, stub, isFault); case 14: case 58: case 68: case 77: return lsaLookupNames(opnum, stub, isFault); } } isFault = true; return {}; Loading Loading @@ -572,4 +833,124 @@ RpcPipes::Bytes RpcPipes::wkstaGetInfo(const Bytes &in) { return w.b; } // ---- lsarpc ---- RpcPipes::Bytes RpcPipes::lsaOpenPolicy() { // the policy handle: nothing behind it, any non-null value does NdrOut o; o.u32(0); static uint32_t serial = 0; o.u32(++serial); for (int i = 0; i < 3; ++i) o.u32(0x53484152); // "SHAR" o.u32(0); // NT_STATUS_OK return o.b; } RpcPipes::Bytes RpcPipes::lsaClose() { NdrOut o; for (int i = 0; i < 5; ++i) o.u32(0); // the null handle o.u32(0); return o.b; } // LookupSids (15), LookupSids2 (57), LookupSids3 (76, no handle) RpcPipes::Bytes RpcPipes::lsaLookupSids(uint16_t opnum, const Bytes &stub, bool &isFault) { NdrIn in(stub); if (opnum != 76) in.skip(20); uint32_t num = in.u32(); std::vector<Sid> sids; if (in.u32()) { uint32_t max = in.u32(); if (max != num || max > 1000) in.ok = false; std::vector<uint32_t> ptrs; for (uint32_t i = 0; in.ok && i < max; ++i) ptrs.push_back(in.u32()); for (uint32_t p : ptrs) sids.push_back(p ? in.sid() : Sid()); } in.u32(); // [in,out] names: count if (in.u32()) in.ok = false; // and none in the request if (!in.ok) { isFault = true; return {}; } RefDomains doms; std::vector<Translation> out; for (const Sid &s : sids) out.push_back(translateSid(s, _workgroup, doms)); NdrOut o; pushRefDomains(o, doms); o.u32((uint32_t)out.size()); o.ptr(!out.empty()); if (!out.empty()) { o.u32((uint32_t)out.size()); for (auto &t : out) { o.u16(t.type); o.align(4); o.lsaString(t.name, false); o.u32(t.dom < 0 ? 0xffffffffu : (uint32_t)t.dom); if (opnum != 15) o.u32(0); // lsa_TranslatedName2: flags } for (auto &t : out) o.lsaBuffer(t.name, false); } uint32_t mapped, status = mappedStatus(out, mapped); o.u32(mapped); o.u32(status); return o.b; } // LookupNames (14), LookupNames2 (58), LookupNames3 (68), LookupNames4 (77, no handle) RpcPipes::Bytes RpcPipes::lsaLookupNames(uint16_t opnum, const Bytes &stub, bool &isFault) { NdrIn in(stub); if (opnum != 77) in.skip(20); uint32_t num = in.u32(); uint32_t max = in.u32(); if (max != num || max > 1000) in.ok = false; std::vector<uint32_t> ptrs; for (uint32_t i = 0; in.ok && i < num; ++i) { in.u16(); in.u16(); ptrs.push_back(in.u32()); } std::vector<std::string> names; for (uint32_t p : ptrs) { // a conformant varying string without NUL names.push_back(p ? in.str() : std::string()); } in.u32(); if (in.u32()) in.ok = false; if (!in.ok) { isFault = true; return {}; } RefDomains doms; std::vector<Translation> out; for (const std::string &n : names) out.push_back(translateName(n, _workgroup, doms)); bool v3 = opnum == 68 || opnum == 77; NdrOut o; pushRefDomains(o, doms); o.u32((uint32_t)out.size()); o.ptr(!out.empty()); if (!out.empty()) { o.u32((uint32_t)out.size()); for (auto &t : out) { o.u16(t.type); o.align(4); if (v3) o.ptr(t.type != SidUnknown); // lsa_TranslatedSid3: the whole SID else o.u32(t.type == SidUnknown || t.sid.size() < 12 ? 0xffffffffu : le32(&t.sid[t.sid.size() - 4])); o.u32(t.dom < 0 ? 0xffffffffu : (uint32_t)t.dom); if (opnum != 14) o.u32(0); } if (v3) for (auto &t : out) if (t.type != SidUnknown) o.sid(t.sid); } uint32_t mapped, status = mappedStatus(out, mapped); o.u32(mapped); o.u32(status); return o.b; } } // namespace sharedd
src/rpc.h +6 −1 Changes for src/rpc.h: 6 added lines, 1 removed line. Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ public: uint32_t transceive(uint32_t handle, const uint8_t *data, size_t len, Bytes &out); private: enum class Iface { None, Srvsvc, Wkssvc }; enum class Iface { None, Srvsvc, Wkssvc, Lsarpc }; struct Pipe { Iface iface = Iface::None; uint16_t maxRecv = 4280; // the client's fragment size Loading @@ -93,6 +93,11 @@ private: Bytes shareGetInfo(const Bytes &in); Bytes serverGetInfo(const Bytes &in); Bytes wkstaGetInfo(const Bytes &in); // lsarpc: names for the SIDs of security descriptors and back Bytes lsaOpenPolicy(); Bytes lsaClose(); Bytes lsaLookupSids(uint16_t opnum, const Bytes &in, bool &fault); Bytes lsaLookupNames(uint16_t opnum, const Bytes &in, bool &fault); std::string hostName() const; Loading