From ad215705aad9facb824aff08b72cc06ae8c1b0a8 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Mon, 6 Feb 2023 18:58:25 +0100 Subject: [PATCH] winbind:varlink: Implement memberships by user MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit $> 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 Reviewed-by: Andreas Schneider --- source3/winbindd/winbindd_varlink.c | 8 + source3/winbindd/winbindd_varlink.h | 6 + .../winbindd_varlink_getmemberships.c | 235 ++++++++++++++++++ 3 files changed, 249 insertions(+) diff --git a/source3/winbindd/winbindd_varlink.c b/source3/winbindd/winbindd_varlink.c index 94a47c22aa5..fe613329d53 100644 --- a/source3/winbindd/winbindd_varlink.c +++ b/source3/winbindd/winbindd_varlink.c @@ -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)) { diff --git a/source3/winbindd/winbindd_varlink.h b/source3/winbindd/winbindd_varlink.h index b7353a5d2ae..87903002770 100644 --- a/source3/winbindd/winbindd_varlink.h +++ b/source3/winbindd/winbindd_varlink.h @@ -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); diff --git a/source3/winbindd/winbindd_varlink_getmemberships.c b/source3/winbindd/winbindd_varlink_getmemberships.c index 123a95b1555..46d79a11f22 100644 --- a/source3/winbindd/winbindd_varlink_getmemberships.c +++ b/source3/winbindd/winbindd_varlink_getmemberships.c @@ -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; +} -- 2.47.2