From c36e0abffdfc09fc4ee48decdca73295884b23a0 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Fri, 13 Apr 2018 16:20:16 +0200 Subject: [PATCH 1/3] Revert "winbind: Remove wbint_QueryUser" This reverts commit 5b2d74bd1116ef182b4a2a58cb8949ae8f10638f. Signed-off-by: Samuel Cabrero (cherry picked from commit 622fa7c65140fb8a83f84305dae051c62d97e822) --- librpc/idl/winbind.idl | 5 +++++ source3/winbindd/winbindd_dual_srv.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/librpc/idl/winbind.idl b/librpc/idl/winbind.idl index ab9af2d1509..eb860b1358f 100644 --- a/librpc/idl/winbind.idl +++ b/librpc/idl/winbind.idl @@ -85,6 +85,11 @@ interface winbind dom_sid group_sid; } wbint_userinfo; + NTSTATUS wbint_QueryUser( + [in] dom_sid *sid, + [out] wbint_userinfo *info + ); + NTSTATUS wbint_GetNssInfo( [in,out] wbint_userinfo *info ); diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c index df380955a00..570af2b2e39 100644 --- a/source3/winbindd/winbindd_dual_srv.c +++ b/source3/winbindd/winbindd_dual_srv.c @@ -269,6 +269,21 @@ NTSTATUS _wbint_AllocateGid(struct pipes_struct *p, struct wbint_AllocateGid *r) return NT_STATUS_OK; } +NTSTATUS _wbint_QueryUser(struct pipes_struct *p, struct wbint_QueryUser *r) +{ + struct winbindd_domain *domain = wb_child_domain(); + NTSTATUS status; + + if (domain == NULL) { + return NT_STATUS_REQUEST_NOT_ACCEPTED; + } + + status = wb_cache_query_user(domain, p->mem_ctx, r->in.sid, + r->out.info); + reset_cm_connection_on_error(domain, status); + return status; +} + NTSTATUS _wbint_GetNssInfo(struct pipes_struct *p, struct wbint_GetNssInfo *r) { struct idmap_domain *domain; -- 2.20.1 From e075937d4a8919c1b5d1dca43b68be5a53e70098 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Fri, 13 Apr 2018 16:42:42 +0200 Subject: [PATCH 2/3] Revert "winbind: Remove rpc_query_user" This reverts commit a8ab48ee193f68217e7c53b71bf6c57d2d15f8d7. Signed-off-by: Samuel Cabrero (cherry picked from commit f77dd0e7a96f79bd25f5883a06c5cc0c35bd65c9) --- source3/winbindd/winbindd_rpc.c | 76 +++++++++++++++++++++++++++++++++ source3/winbindd/winbindd_rpc.h | 8 ++++ 2 files changed, 84 insertions(+) diff --git a/source3/winbindd/winbindd_rpc.c b/source3/winbindd/winbindd_rpc.c index 9fb22400c1d..eb31ce2d520 100644 --- a/source3/winbindd/winbindd_rpc.c +++ b/source3/winbindd/winbindd_rpc.c @@ -438,6 +438,82 @@ NTSTATUS rpc_rids_to_names(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } +/* Lookup user information from a rid or username. */ +NTSTATUS rpc_query_user(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *samr_pipe, + struct policy_handle *samr_policy, + const struct dom_sid *domain_sid, + const struct dom_sid *user_sid, + struct wbint_userinfo *user_info) +{ + struct policy_handle user_policy; + union samr_UserInfo *info = NULL; + uint32_t user_rid; + NTSTATUS status, result; + struct dcerpc_binding_handle *b = samr_pipe->binding_handle; + + if (!sid_peek_check_rid(domain_sid, user_sid, &user_rid)) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* Get user handle */ + status = dcerpc_samr_OpenUser(b, + mem_ctx, + samr_policy, + SEC_FLAG_MAXIMUM_ALLOWED, + user_rid, + &user_policy, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + /* Get user info */ + status = dcerpc_samr_QueryUserInfo(b, + mem_ctx, + &user_policy, + 0x15, + &info, + &result); + { + NTSTATUS _result; + dcerpc_samr_Close(b, mem_ctx, &user_policy, &_result); + } + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + sid_compose(&user_info->user_sid, domain_sid, user_rid); + sid_compose(&user_info->group_sid, domain_sid, + info->info21.primary_gid); + + user_info->acct_name = talloc_strdup(user_info, + info->info21.account_name.string); + if (user_info->acct_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + user_info->full_name = talloc_strdup(user_info, + info->info21.full_name.string); + if ((info->info21.full_name.string != NULL) && + (user_info->full_name == NULL)) + { + return NT_STATUS_NO_MEMORY; + } + + user_info->homedir = NULL; + user_info->shell = NULL; + user_info->primary_gid = (gid_t)-1; + + return NT_STATUS_OK; +} + /* Lookup groups a user is a member of. */ NTSTATUS rpc_lookup_usergroups(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *samr_pipe, diff --git a/source3/winbindd/winbindd_rpc.h b/source3/winbindd/winbindd_rpc.h index 162f1ef3329..ee0f5432c64 100644 --- a/source3/winbindd/winbindd_rpc.h +++ b/source3/winbindd/winbindd_rpc.h @@ -78,6 +78,14 @@ NTSTATUS rpc_rids_to_names(TALLOC_CTX *mem_ctx, char ***pnames, enum lsa_SidType **ptypes); +/* Lookup user information from a rid or username. */ +NTSTATUS rpc_query_user(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *samr_pipe, + struct policy_handle *samr_policy, + const struct dom_sid *domain_sid, + const struct dom_sid *user_sid, + struct wbint_userinfo *user_info); + /* Lookup groups a user is a member of. */ NTSTATUS rpc_lookup_usergroups(TALLOC_CTX *mem_ctx, struct rpc_pipe_client *samr_pipe, -- 2.20.1 From c8131e7e20f9a0acfb734a3eb4e4c7c2e2915976 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Mon, 16 Apr 2018 10:33:17 +0200 Subject: [PATCH 3/3] s3: winbind: Call SAMR QueryUserInfo if no valid netlogon cache entry In the way to 4.6.0 a lot of code was pulled out from winbind with the premise that returned user info is correct only for users who logged on [1] (user and group enumeration, user group membership and user information). Part of these changes were reverted due to user complains in the RC stage [2], but the bits related to query user information were lost. When there isn't a valid netlogon cache entry wb_queryuser defaults the user primary group sid to -513 (Domain users), and will become correct once there is a valid netlogon cache entry. In an effort to return as accurate as possible information, try to obtain the missing fields calling SAMR QueryUserInfo when it cannot be extracted from netlogon cache. This new behavior requires to open a SAMR pipe to a DC in the domain the user belongs to, and as RPC to domain controllers outside winbind's primary domain has to be avoided, it is disabled by default. To enable, set "winbind: query user rpc fallback = yes". [1] https://lists.samba.org/archive/samba-technical/2016-November/116972.html [2] https://lists.samba.org/archive/samba-technical/2017-March/119066.html Signed-off-by: Samuel Cabrero (cherry picked from commit 0b64dce0b9ce3366b7df338145f36ac245c77e81) --- source3/winbindd/wb_queryuser.c | 137 ++++++++++++++++++++++++--- source3/winbindd/winbindd_dual_srv.c | 19 +++- 2 files changed, 142 insertions(+), 14 deletions(-) diff --git a/source3/winbindd/wb_queryuser.c b/source3/winbindd/wb_queryuser.c index 69b4c8dad5a..82355c716e5 100644 --- a/source3/winbindd/wb_queryuser.c +++ b/source3/winbindd/wb_queryuser.c @@ -25,13 +25,18 @@ struct wb_queryuser_state { struct tevent_context *ev; - struct wbint_userinfo *info; bool tried_dclookup; + bool cached_samlogon; + /* Returned to caller */ + struct wbint_userinfo *info; + /* Returned from SAMR QueryUserInfo */ + struct wbint_userinfo *samr_info; }; static void wb_queryuser_got_uid(struct tevent_req *subreq); static void wb_queryuser_got_domain(struct tevent_req *subreq); static void wb_queryuser_got_dc(struct tevent_req *subreq); +static void wb_queryuser_got_nssinfo(struct tevent_req *subreq); static void wb_queryuser_got_gid(struct tevent_req *subreq); static void wb_queryuser_got_group_name(struct tevent_req *subreq); static void wb_queryuser_done(struct tevent_req *subreq); @@ -89,8 +94,8 @@ static void wb_queryuser_got_uid(struct tevent_req *subreq) req, struct wb_queryuser_state); struct wbint_userinfo *info = state->info; struct netr_SamInfo3 *info3; - struct winbindd_child *child; struct unixid xid; + struct winbindd_child *child; NTSTATUS status; status = wb_sids2xids_recv(subreq, &xid, 1); @@ -127,7 +132,6 @@ static void wb_queryuser_got_uid(struct tevent_req *subreq) info3 = netsamlogon_cache_get(state, &info->user_sid); if (info3 != NULL) { - sid_compose(&info->group_sid, info3->base.domain_sid, info3->base.primary_gid); info->acct_name = talloc_move( @@ -139,6 +143,7 @@ static void wb_queryuser_got_uid(struct tevent_req *subreq) state, &info3->base.logon_domain.string); TALLOC_FREE(info3); + state->cached_samlogon = true; } if (info->domain_name == NULL) { @@ -150,6 +155,39 @@ static void wb_queryuser_got_uid(struct tevent_req *subreq) return; } + if (!state->cached_samlogon && + lp_parm_bool(-1, "winbindd", "query user rpc fallback", false)) { + struct winbindd_domain *domain; + + /* If not found in cache, call SAMR QueryUserInfo */ + domain = find_domain_from_name(info->domain_name); + if (domain == NULL) { + DEBUG(5, ("Could not find domain for '%s'\n", + info->domain_name)); + tevent_req_nterror(req, NT_STATUS_NONE_MAPPED); + return; + } + + state->samr_info = talloc_zero(state, struct wbint_userinfo); + if (tevent_req_nomem(state->samr_info, req)) { + return; + } + + subreq = dcerpc_wbint_QueryUser_send( + state, state->ev, + dom_child_handle(domain), + &info->user_sid, state->samr_info); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, wb_queryuser_done, req); + return; + } + + /* The user information should be correct at this point, even from + * the netlogon cache or retrived calling SAMR QueryUserInfo. Now + * let the idmap backend override the info. */ + child = idmap_child(); subreq = dcerpc_wbint_GetNssInfo_send( @@ -157,7 +195,7 @@ static void wb_queryuser_got_uid(struct tevent_req *subreq) if (tevent_req_nomem(subreq, req)) { return; } - tevent_req_set_callback(subreq, wb_queryuser_done, req); + tevent_req_set_callback(subreq, wb_queryuser_got_nssinfo, req); } static void wb_queryuser_got_domain(struct tevent_req *subreq) @@ -184,6 +222,38 @@ static void wb_queryuser_got_domain(struct tevent_req *subreq) return; } + if (!state->cached_samlogon && + lp_parm_bool(-1, "winbindd", "query user rpc fallback", false)) { + struct winbindd_domain *domain; + + /* If not found in cache, call SAMR QueryUserInfo */ + domain = find_domain_from_name(info->domain_name); + if (domain == NULL) { + DEBUG(5, ("Could not find domain for '%s'\n", + info->domain_name)); + tevent_req_nterror(req, NT_STATUS_NONE_MAPPED); + return; + } + + state->samr_info = talloc_zero(state, struct wbint_userinfo); + if (tevent_req_nomem(state->samr_info, req)) { + return; + } + + subreq = dcerpc_wbint_QueryUser_send( + state, state->ev, + dom_child_handle(domain), + &info->user_sid, state->samr_info); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, wb_queryuser_done, req); + return; + } + + /* The user information should be correct at this point from + * the netlogon cache, let the idmap backend override the info. */ + child = idmap_child(); subreq = dcerpc_wbint_GetNssInfo_send( @@ -191,10 +261,51 @@ static void wb_queryuser_got_domain(struct tevent_req *subreq) if (tevent_req_nomem(subreq, req)) { return; } - tevent_req_set_callback(subreq, wb_queryuser_done, req); + tevent_req_set_callback(subreq, wb_queryuser_got_nssinfo, req); } static void wb_queryuser_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct wb_queryuser_state *state = tevent_req_data( + req, struct wb_queryuser_state); + struct winbindd_child *child; + NTSTATUS status, result; + + status = dcerpc_wbint_QueryUser_recv(subreq, state, &result); + TALLOC_FREE(subreq); + + if (tevent_req_nterror(req, status)) { + return; + } + + /* Ignore errors and let the idmap backend obtain the info. Otherwise + * the defaults will be returned */ + if (NT_STATUS_IS_OK(result)) { + sid_copy(&state->info->group_sid, + &state->samr_info->group_sid); + state->info->acct_name = talloc_strdup( + state->info, state->samr_info->acct_name); + state->info->full_name = talloc_strdup( + state->info, state->samr_info->full_name); + } + + /* The user information should be correct at this point, retrived + * calling SAMR QueryUserInfo. Now let the idmap backend override + * the info. */ + + child = idmap_child(); + + subreq = dcerpc_wbint_GetNssInfo_send( + state, state->ev, child->binding_handle, state->info); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, wb_queryuser_got_nssinfo, req); +} + +static void wb_queryuser_got_nssinfo(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data( subreq, struct tevent_req); @@ -219,13 +330,17 @@ static void wb_queryuser_done(struct tevent_req *subreq) } tevent_req_set_callback(subreq, wb_queryuser_got_dc, req); return; + } else if (!NT_STATUS_EQUAL(result, NT_STATUS_OK) && + !NT_STATUS_EQUAL(result, NT_STATUS_REQUEST_NOT_ACCEPTED)) + { + /* If the idmap backend returns NT_STATUS_REQUEST_NOT_ACCEPTED + * ignore, means it does not override the current values */ + tevent_req_nterror(req, result); + return; } - /* - * Ignore failure in "result" here. We'll try to fill in stuff - * that misses further down. - */ - + /* info->group_sid should be correct, even from the netlogon cache + * or obtained calling SAMR QueryUser */ if (state->info->primary_gid == (gid_t)-1) { subreq = wb_sids2xids_send( state, state->ev, &info->group_sid, 1); @@ -280,7 +395,7 @@ static void wb_queryuser_got_dc(struct tevent_req *subreq) if (tevent_req_nomem(subreq, req)) { return; } - tevent_req_set_callback(subreq, wb_queryuser_done, req); + tevent_req_set_callback(subreq, wb_queryuser_got_nssinfo, req); } static void wb_queryuser_got_gid(struct tevent_req *subreq) diff --git a/source3/winbindd/winbindd_dual_srv.c b/source3/winbindd/winbindd_dual_srv.c index 570af2b2e39..31f072a681c 100644 --- a/source3/winbindd/winbindd_dual_srv.c +++ b/source3/winbindd/winbindd_dual_srv.c @@ -23,6 +23,7 @@ #include "includes.h" #include "winbindd/winbindd.h" #include "winbindd/winbindd_proto.h" +#include "winbindd/winbindd_rpc.h" #include "rpc_client/cli_pipe.h" #include "ntdomain.h" #include "librpc/gen_ndr/srv_winbind.h" @@ -272,15 +273,27 @@ NTSTATUS _wbint_AllocateGid(struct pipes_struct *p, struct wbint_AllocateGid *r) NTSTATUS _wbint_QueryUser(struct pipes_struct *p, struct wbint_QueryUser *r) { struct winbindd_domain *domain = wb_child_domain(); + struct rpc_pipe_client *samr_pipe; + struct policy_handle samr_domain_handle; NTSTATUS status; if (domain == NULL) { return NT_STATUS_REQUEST_NOT_ACCEPTED; } - status = wb_cache_query_user(domain, p->mem_ctx, r->in.sid, - r->out.info); - reset_cm_connection_on_error(domain, status); + status = cm_connect_sam(domain, p->mem_ctx, false, + &samr_pipe, &samr_domain_handle); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("could not open handle to SAMR pipe: %s\n", + nt_errstr(status))); + return status; + } + + status = rpc_query_user(p->mem_ctx, samr_pipe, + &samr_domain_handle, + &domain->sid, r->in.sid, + r->out.info); + return status; } -- 2.20.1