From 4c3910fa86d80a730f0ed9d1a7dcdadc7cc3a09a Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Mon, 6 Feb 2023 18:24:15 +0100 Subject: [PATCH] winbind:varlink: Implement get user record by name and uid $> varlink call unix:/run/systemd/userdb/org.samba.winbind/io.systemd.UserDatabase.GetUserRecord "{\"service\":\"org.samba.winbind\",\"userName\":\"AFOREST+user1\",\"uid\":21105}" { "incomplete": false, "record": { "gid": 20513, "homeDirectory": "/home/AFOREST/user1", "service": "org.samba.winbind", "shell": "/bin/bash", "uid": 21105, "userName": "AFOREST+user1" } } Signed-off-by: Samuel Cabrero Reviewed-by: Andreas Schneider --- source3/winbindd/winbindd_varlink.c | 9 + source3/winbindd/winbindd_varlink.h | 7 + .../winbindd/winbindd_varlink_getuserrecord.c | 182 ++++++++++++++++++ 3 files changed, 198 insertions(+) diff --git a/source3/winbindd/winbindd_varlink.c b/source3/winbindd/winbindd_varlink.c index 742d0025868..20b2265b8b7 100644 --- a/source3/winbindd/winbindd_varlink.c +++ b/source3/winbindd/winbindd_varlink.c @@ -196,6 +196,15 @@ static long io_systemd_getuserrecord(VarlinkService *service, flags, parm_service, parm_name); + } else if (parm_name != NULL && parm_uid >= 0) { + /* getgrnam + getgrgid */ + status = wb_vl_user_by_name_and_uid(state, + state->ev_ctx, + call, + flags, + parm_service, + parm_name, + parm_uid); } if (NT_STATUS_IS_ERR(status)) { diff --git a/source3/winbindd/winbindd_varlink.h b/source3/winbindd/winbindd_varlink.h index ddf23b437df..21155173949 100644 --- a/source3/winbindd/winbindd_varlink.h +++ b/source3/winbindd/winbindd_varlink.h @@ -59,6 +59,13 @@ NTSTATUS wb_vl_user_by_name(TALLOC_CTX *mem_ctx, uint64_t flags, const char *service, const char *user_name); +NTSTATUS wb_vl_user_by_name_and_uid(TALLOC_CTX *mem_ctx, + struct tevent_context *ev_ctx, + VarlinkCall *call, + uint64_t flags, + const char *service, + const char *user_name, + int64_t gid); bool winbind_setup_varlink(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx); diff --git a/source3/winbindd/winbindd_varlink_getuserrecord.c b/source3/winbindd/winbindd_varlink_getuserrecord.c index 874d128672f..5ac4adb64b8 100644 --- a/source3/winbindd/winbindd_varlink_getuserrecord.c +++ b/source3/winbindd/winbindd_varlink_getuserrecord.c @@ -592,3 +592,185 @@ fail: TALLOC_FREE(s); return status; } + +/****************************************************************************** + * User by name and uid + * Search by name first, if found, check uid matches. + * If not found, search by uid and check name. + *****************************************************************************/ + +struct user_by_name_uid_state { + struct tevent_context *ev_ctx; + struct winbindd_request *fake_req; + struct winbindd_cli_state *fake_cli; + VarlinkCall *call; + const char *name; + uid_t uid; +}; + +static int user_by_name_uid_state_destructor(struct user_by_name_uid_state *s) +{ + if (s->call != NULL) { + s->call = varlink_call_unref(s->call); + } + + return 0; +} + +static void user_by_name_uid_getpwnamuid_done(struct tevent_req *req) +{ + struct user_by_name_uid_state *s = + tevent_req_callback_data(req, struct user_by_name_uid_state); + struct winbindd_response *response = NULL; + NTSTATUS status; + + /* winbindd_*_recv functions expect a talloc-allocated response */ + 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; + } + + switch (s->fake_req->cmd) { + case WINBINDD_GETPWNAM: + status = winbindd_getpwnam_recv(req, response); + break; + case WINBINDD_GETPWUID: + status = winbindd_getpwuid_recv(req, response); + break; + default: + varlink_call_reply_error( + s->call, + WB_VL_REPLY_ERROR_SERVICE_NOT_AVAILABLE, + NULL); + TALLOC_FREE(req); + goto out; + } + TALLOC_FREE(req); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) { + /* + * If name search did not find the user, fall back to uid. + * If uid search fails too no record found will be returned. + */ + if (s->fake_req->cmd == WINBINDD_GETPWNAM) { + ZERO_STRUCTP(s->fake_req); + s->fake_req->cmd = WINBINDD_GETPWUID; + s->fake_req->data.uid = s->uid; + req = winbindd_getpwuid_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, + user_by_name_uid_getpwnamuid_done, + s); + return; + } + varlink_call_reply_error(s->call, + WB_VL_REPLY_ERROR_NO_RECORD_FOUND, + NULL); + goto out; + } else if (NT_STATUS_IS_ERR(status)) { + DBG_ERR("winbindd_getpw[nam|sid] 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.pw.pw_uid != s->uid || + !strequal(response->data.pw.pw_name, s->name)) { + varlink_call_reply_error( + s->call, + WB_VL_REPLY_ERROR_CONFLICTING_RECORD_FOUND, + NULL); + goto out; + } + + user_record_reply(s->call, &response->data.pw, false); +out: + TALLOC_FREE(s); +} + +NTSTATUS wb_vl_user_by_name_and_uid(TALLOC_CTX *mem_ctx, + struct tevent_context *ev_ctx, + VarlinkCall *call, + uint64_t flags, + const char *service, + const char *user_name, + int64_t uid) +{ + struct user_by_name_uid_state *s = NULL; + struct tevent_req *req = NULL; + NTSTATUS status; + + s = talloc_zero(mem_ctx, struct user_by_name_uid_state); + if (s == NULL) { + DBG_ERR("No memory\n"); + return NT_STATUS_NO_MEMORY; + } + talloc_set_destructor(s, user_by_name_uid_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->name = talloc_strdup(s, user_name); + if (s->name == NULL) { + DBG_ERR("No memory\n"); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + s->ev_ctx = ev_ctx; + s->uid = uid; + 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_GETPWNAM; + fstrcpy(s->fake_req->data.username, user_name); + + req = winbindd_getpwnam_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, user_by_name_uid_getpwnamuid_done, s); + + return NT_STATUS_OK; +fail: + TALLOC_FREE(s); + return status; +} -- 2.47.2