From 7f98021c93d56eab9e500603e28106da5a135304 Mon Sep 17 00:00:00 2001 From: Lily Ballard Date: Sun, 2 Feb 2025 22:29:19 -0800 Subject: [PATCH] daemon: test group membership better on macOS macOS uses opendirectory for users and groups, which supports nested groups and groups with synthesized membership. This means that asking for a group's users isn't sufficient to test for group membership. With this change, groups like `@localaccounts` or `@_developer` will work in `trusted-users` and `allowed-users`. Fixes https://github.com/NixOS/nix/issues/5885 Change-Id: I3b0783ce7cec303de5aba32c8e5ac0f976112c72 --- doc/manual/rl-next/darwin-group-membership.md | 12 +++++++++++ lix/nix/daemon.cc | 20 +++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 doc/manual/rl-next/darwin-group-membership.md diff --git a/doc/manual/rl-next/darwin-group-membership.md b/doc/manual/rl-next/darwin-group-membership.md new file mode 100644 index 000000000..2ff24435a --- /dev/null +++ b/doc/manual/rl-next/darwin-group-membership.md @@ -0,0 +1,12 @@ +--- +synopsis: "Test group membership better on macOS" +issues: [gh#5885] +cls: [2566] +category: Fixes +credits: [lilyball] +--- + +`nix-daemon` will now test group membership better on macOS for `trusted-users` and `allowed-users`. +It not only fetches the peer gid (which fixes `@staff`) but it also asks opendirectory for group +membership checks instead of just using the group database, which means nested groups (like `@_developer`) +and groups with synthesized membership (like `@localaccounts`) will work. diff --git a/lix/nix/daemon.cc b/lix/nix/daemon.cc index e511a8938..2caf5ee58 100644 --- a/lix/nix/daemon.cc +++ b/lix/nix/daemon.cc @@ -36,6 +36,9 @@ #if __APPLE__ || __FreeBSD__ #include #endif +#if __APPLE__ +#include +#endif namespace nix { @@ -110,10 +113,23 @@ static void setSigChldAction(bool autoReap) * * @param group Group the user might be a member of. */ -static bool matchUser(std::string_view user, const struct group & gr) +static bool matchUser(const std::string & user, const struct group & gr) { for (char * * mem = gr.gr_mem; *mem; mem++) - if (user == std::string_view(*mem)) return true; + if (user == *mem) return true; +#if __APPLE__ + // FIXME: we should probably pipe the uid through these functions + // instead of converting the username back into the uid + if (auto pw = getpwnam(user.c_str())) { + uuid_t uuid, gruuid; + if (!mbr_uid_to_uuid(pw->pw_uid, uuid) && !mbr_gid_to_uuid(gr.gr_gid, gruuid)) { + int ismember = 0; + if (!mbr_check_membership(uuid, gruuid, &ismember)) { + return !!ismember; + } + } + } +#endif return false; }