]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
winbind:varlink: Implement memberships by user
authorSamuel Cabrero <scabrero@samba.org>
Mon, 6 Feb 2023 17:58:25 +0000 (18:58 +0100)
committerAndreas Schneider <asn@cryptomilk.org>
Thu, 20 Feb 2025 08:07:32 +0000 (08:07 +0000)
$> userdbctl -s org.samba.winbind groups-of-user AFOREST+user1
Enabled services: org.samba.winbind
USER          GROUP
AFOREST+user1 AFOREST+domain users
AFOREST+user1 AFOREST+user1

2 memberships listed.

$> SYSTEMD_LOG_LEVEL=7 getent -sinitgroups:systemd initgroups "AFOREST+domain users"
varlink: Setting state idle-client
/run/systemd/userdb/org.samba.winbind: Sending message: {"method":"io.systemd.UserDatabase.GetMemberships","parameters":{"userName":"AFOREST+domain users","service":"org.samba.winbind"},"more":true}
/run/systemd/userdb/org.samba.winbind: Changing state idle-client → awaiting-reply-more
/run/systemd/userdb/org.samba.winbind: New incoming message: {"continues":true,"parameters":{"groupName":"AFOREST+domain users","userName":"AFOREST+domain users"}}
/run/systemd/userdb/org.samba.winbind: Changing state awaiting-reply-more → processing-reply
/run/systemd/userdb/org.samba.winbind: Changing state processing-reply → awaiting-reply-more
Failed to connect to /run/systemd/userdb/io.systemd.Multiplexer: No such file or directory
Unable to connect to /run/systemd/userdb/io.systemd.Multiplexer: No such file or directory
varlink: Setting state idle-client
/run/systemd/userdb/org.samba.winbind: Sending message: {"method":"io.systemd.UserDatabase.GetGroupRecord","parameters":{"groupName":"AFOREST+domain users","service":"org.samba.winbind"}}
/run/systemd/userdb/org.samba.winbind: Changing state idle-client → awaiting-reply
/run/systemd/userdb/org.samba.winbind: New incoming message: {"parameters":{"incomplete":false,"record":{"gid":20513,"groupName":"AFOREST+domain users","members":["AFOREST+administrator","AFOREST+user1","AFOREST+krbtgt"],"service":"org.samba.winbind"}}}
/run/systemd/userdb/org.samba.winbind: Changing state awaiting-reply → processing-reply
/run/systemd/userdb/org.samba.winbind: Changing state processing-reply → idle-client
/run/systemd/userdb/org.samba.winbind: New incoming message: {"parameters":{"groupName":"AFOREST+domain users","userName":"AFOREST+domain users"}}
/run/systemd/userdb/org.samba.winbind: Changing state awaiting-reply-more → processing-reply
/run/systemd/userdb/org.samba.winbind: Changing state processing-reply → idle-client
Failed to connect to /run/systemd/userdb/io.systemd.Multiplexer: No such file or directory
Unable to connect to /run/systemd/userdb/io.systemd.Multiplexer: No such file or directory
varlink: Setting state idle-client
/run/systemd/userdb/org.samba.winbind: Sending message: {"method":"io.systemd.UserDatabase.GetGroupRecord","parameters":{"groupName":"AFOREST+domain users","service":"org.samba.winbind"}}
/run/systemd/userdb/org.samba.winbind: Changing state idle-client → awaiting-reply
/run/systemd/userdb/org.samba.winbind: New incoming message: {"parameters":{"incomplete":false,"record":{"gid":20513,"groupName":"AFOREST+domain users","members":["AFOREST+administrator","AFOREST+user1","AFOREST+krbtgt"],"service":"org.samba.winbind"}}}
/run/systemd/userdb/org.samba.winbind: Changing state awaiting-reply → processing-reply
/run/systemd/userdb/org.samba.winbind: Changing state processing-reply → idle-client
AFOREST+domain users  20513 20513

Signed-off-by: Samuel Cabrero <scabrero@samba.org>
Reviewed-by: Andreas Schneider <asn@samba.org>
source3/winbindd/winbindd_varlink.c
source3/winbindd/winbindd_varlink.h
source3/winbindd/winbindd_varlink_getmemberships.c

index 94a47c22aa58881dfa97d8a1b62a3652af9fb961..fe613329d5381a7d9bd58963fb290e61a2ac7096 100644 (file)
@@ -400,6 +400,14 @@ static long io_systemd_getmemberships(VarlinkService *service,
                                                     call,
                                                     flags,
                                                     parm_service);
+       } else if (parm_username != NULL && parm_groupname == NULL) {
+               /* List user groups */
+               status = wb_vl_memberships_by_user(state,
+                                                  state->ev_ctx,
+                                                  call,
+                                                  flags,
+                                                  parm_service,
+                                                  parm_username);
        }
 
        if (NT_STATUS_IS_ERR(status)) {
index b7353a5d2aeee21864ee37b1fd471fea826c1389..87903002770c46e9920beaa384d208157f3f139f 100644 (file)
@@ -99,6 +99,12 @@ NTSTATUS wb_vl_memberships_enumerate(TALLOC_CTX *mem_ctx,
                                     VarlinkCall *call,
                                     uint64_t flags,
                                     const char *service);
+NTSTATUS wb_vl_memberships_by_user(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev_ctx,
+                                  VarlinkCall *call,
+                                  uint64_t flags,
+                                  const char *service,
+                                  const char *user_name);
 
 bool winbind_setup_varlink(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx);
 
index 123a95b155565978f47d93b19f25ef115ad11b5e..46d79a11f225024e829eff1dab9cba6558b04c85 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "includes.h"
 #include "winbindd.h"
+#include "lib/util/string_wrappers.h"
 #include "winbindd_varlink.h"
 
 static void membership_reply(VarlinkCall *call,
@@ -394,3 +395,237 @@ fail:
        TALLOC_FREE(s);
        return status;
 }
+
+/******************************************************************************
+ * List user groups
+ *****************************************************************************/
+
+struct memberships_by_user_state {
+       struct winbindd_cli_state *fake_cli;
+       struct winbindd_request *fake_req;
+       struct tevent_context *ev_ctx;
+       VarlinkCall *call;
+       const char *username;
+
+       uint32_t num_gids;
+       gid_t *gids;
+};
+
+static int
+memberships_by_user_state_destructor(struct memberships_by_user_state *s)
+{
+       if (s->call != NULL) {
+               s->call = varlink_call_unref(s->call);
+       }
+
+       return 0;
+}
+
+static void memberships_by_user_getgrgid_done(struct tevent_req *req)
+{
+       struct memberships_by_user_state *s =
+               tevent_req_callback_data(req, struct memberships_by_user_state);
+       struct winbindd_response *response = NULL;
+       NTSTATUS status;
+
+       response = talloc_zero(s, struct winbindd_response);
+       if (response == NULL) {
+               DBG_ERR("No memory\n");
+               varlink_call_reply_error(
+                       s->call,
+                       WB_VL_REPLY_ERROR_SERVICE_NOT_AVAILABLE,
+                       NULL);
+               goto out;
+       }
+
+       status = winbindd_getgrgid_recv(req, response);
+       TALLOC_FREE(req);
+
+       if (NT_STATUS_IS_ERR(status)) {
+               DBG_ERR("winbindd_getgrgid failed: %s\n", nt_errstr(status));
+               varlink_call_reply_error(
+                       s->call,
+                       WB_VL_REPLY_ERROR_SERVICE_NOT_AVAILABLE,
+                       NULL);
+               goto out;
+       }
+
+       if (s->gids == NULL) {
+               /*
+                * We freed the array in the last winbindd_getgrgid_send
+                * call to flag it was the last one
+                */
+               membership_reply(s->call,
+                                s->username,
+                                response->data.gr.gr_name,
+                                false);
+               goto out;
+       }
+
+       membership_reply(s->call, s->username, response->data.gr.gr_name, true);
+       TALLOC_FREE(response);
+
+       s->num_gids--;
+
+       ZERO_STRUCTP(s->fake_req);
+       s->fake_req->cmd = WINBINDD_GETGRGID;
+       s->fake_req->data.uid = s->gids[s->num_gids - 1];
+
+       if (s->num_gids - 1 == 0) {
+               /* Flag this is the last winbindd_getgrgid_send call */
+               TALLOC_FREE(s->gids);
+       }
+
+       req = winbindd_getgrgid_send(s, s->ev_ctx, s->fake_cli, s->fake_req);
+       if (req == NULL) {
+               DBG_ERR("No memory\n");
+               varlink_call_reply_error(
+                       s->call,
+                       WB_VL_REPLY_ERROR_SERVICE_NOT_AVAILABLE,
+                       NULL);
+               goto out;
+       }
+       tevent_req_set_callback(req, memberships_by_user_getgrgid_done, s);
+       return;
+out:
+       TALLOC_FREE(s);
+}
+
+static void memberships_by_user_getgroups_done(struct tevent_req *req)
+{
+       struct memberships_by_user_state *s =
+               tevent_req_callback_data(req, struct memberships_by_user_state);
+       struct winbindd_response *response = NULL;
+       NTSTATUS status;
+
+       response = talloc_zero(s, struct winbindd_response);
+       if (response == NULL) {
+               DBG_ERR("No memory\n");
+               varlink_call_reply_error(
+                       s->call,
+                       WB_VL_REPLY_ERROR_SERVICE_NOT_AVAILABLE,
+                       NULL);
+               goto out;
+       }
+
+       status = winbindd_getgroups_recv(req, response);
+       TALLOC_FREE(req);
+
+       if (NT_STATUS_IS_ERR(status)) {
+               DBG_ERR("winbindd_getgroups failed: %s\n", nt_errstr(status));
+               varlink_call_reply_error(
+                       s->call,
+                       WB_VL_REPLY_ERROR_SERVICE_NOT_AVAILABLE,
+                       NULL);
+               goto out;
+       }
+
+       if (response->data.num_entries == 0) {
+               varlink_call_reply_error(s->call,
+                                        WB_VL_REPLY_ERROR_NO_RECORD_FOUND,
+                                        NULL);
+               goto out;
+       }
+
+       s->num_gids = response->data.num_entries;
+       s->gids = talloc_move(s, &response->extra_data.data);
+       TALLOC_FREE(response);
+
+       ZERO_STRUCTP(s->fake_req);
+       s->fake_req->cmd = WINBINDD_GETGRGID;
+       s->fake_req->data.uid = s->gids[s->num_gids - 1];
+
+       req = winbindd_getgrgid_send(s, s->ev_ctx, s->fake_cli, s->fake_req);
+       if (req == NULL) {
+               DBG_ERR("No memory\n");
+               varlink_call_reply_error(
+                       s->call,
+                       WB_VL_REPLY_ERROR_SERVICE_NOT_AVAILABLE,
+                       NULL);
+               goto out;
+       }
+       tevent_req_set_callback(req, memberships_by_user_getgrgid_done, s);
+       return;
+out:
+       TALLOC_FREE(s);
+}
+
+NTSTATUS wb_vl_memberships_by_user(TALLOC_CTX *mem_ctx,
+                                  struct tevent_context *ev_ctx,
+                                  VarlinkCall *call,
+                                  uint64_t flags,
+                                  const char *service,
+                                  const char *user_name)
+{
+       struct memberships_by_user_state *s = NULL;
+       struct tevent_req *req = NULL;
+       NTSTATUS status;
+
+       /* Check if group expansion is enabled */
+       if (!lp_winbind_expand_groups()) {
+               varlink_call_reply_error(
+                       call,
+                       WB_VL_REPLY_ERROR_ENUMERATION_NOT_SUPPORTED,
+                       NULL);
+               return NT_STATUS_OK;
+       }
+
+       /* Check more flag is set */
+       if (!(flags & VARLINK_CALL_MORE)) {
+               DBG_WARNING("Request without more flag set\n");
+               return NT_STATUS_INVALID_PARAMETER;
+       }
+
+       s = talloc_zero(mem_ctx, struct memberships_by_user_state);
+       if (s == NULL) {
+               DBG_ERR("No memory\n");
+               return NT_STATUS_NO_MEMORY;
+       }
+       talloc_set_destructor(s, memberships_by_user_state_destructor);
+
+       s->fake_cli = talloc_zero(s, struct winbindd_cli_state);
+       if (s->fake_cli == NULL) {
+               DBG_ERR("No memory\n");
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       s->fake_req = talloc_zero(s, struct winbindd_request);
+       if (s->fake_req == NULL) {
+               DBG_ERR("No memory\n");
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       s->username = talloc_strdup(s, user_name);
+       if (s->username == NULL) {
+               DBG_ERR("No memory\n");
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+
+       s->ev_ctx = ev_ctx;
+       s->call = varlink_call_ref(call);
+
+       status = wb_vl_fake_cli_state(call, service, s->fake_cli);
+       if (NT_STATUS_IS_ERR(status)) {
+               DBG_ERR("Failed to create fake winbindd_cli_state: %s\n",
+                       nt_errstr(status));
+               goto fail;
+       }
+
+       s->fake_req->cmd = WINBINDD_GETGROUPS;
+       fstrcpy(s->fake_req->data.username, user_name);
+       req = winbindd_getgroups_send(s, s->ev_ctx, s->fake_cli, s->fake_req);
+       if (req == NULL) {
+               DBG_ERR("No memory\n");
+               status = NT_STATUS_NO_MEMORY;
+               goto fail;
+       }
+       tevent_req_set_callback(req, memberships_by_user_getgroups_done, s);
+
+       return NT_STATUS_OK;
+fail:
+       TALLOC_FREE(s);
+       return status;
+}