From 3ffebee3de4aa313027779bc98cb6326fa17be85 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 23 Jan 2018 13:19:37 +0100 Subject: [PATCH] winbindd: implement wb_irpc_lsa_{LookupNames4,LookupSids3}() This will be used by the LSA Server on an AD DC to request remote views from trusts. In future we should implement wb_lookupnames_send/recv similar to wb_lookupsids_send/recv, but for now using wb_lookupname_send/recv in a loop works as a first step. We also need to make use of req->in.level and req->in.client_revision once we want to support more than one domain within our own forest. Bug: https://bugzilla.samba.org/show_bug.cgi?id=13286 Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme --- source3/winbindd/winbindd_irpc.c | 408 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) diff --git a/source3/winbindd/winbindd_irpc.c b/source3/winbindd/winbindd_irpc.c index 4101469ad99..e03312ec7af 100644 --- a/source3/winbindd/winbindd_irpc.c +++ b/source3/winbindd/winbindd_irpc.c @@ -25,6 +25,10 @@ #include "librpc/gen_ndr/ndr_winbind_c.h" #include "source4/lib/messaging/irpc.h" #include "librpc/gen_ndr/ndr_winbind.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "libcli/security/dom_sid.h" +#include "passdb/lookup_sid.h" /* only for LOOKUP_NAME_NO_NSS flag */ struct wb_irpc_forward_state { struct irpc_message *msg; @@ -330,6 +334,398 @@ static NTSTATUS wb_irpc_SendToSam(struct irpc_message *msg, domain, IRPC_CALL_TIMEOUT); } +struct wb_irpc_lsa_LookupSids3_state { + struct irpc_message *msg; + struct lsa_LookupSids3 *req; +}; + +static void wb_irpc_lsa_LookupSids3_done(struct tevent_req *subreq); + +static NTSTATUS wb_irpc_lsa_LookupSids3_call(struct irpc_message *msg, + struct lsa_LookupSids3 *req) +{ + struct wb_irpc_lsa_LookupSids3_state *state = NULL; + struct tevent_req *subreq = NULL; + struct dom_sid *sids = NULL; + uint32_t i; + + state = talloc_zero(msg, struct wb_irpc_lsa_LookupSids3_state); + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->msg = msg; + state->req = req; + + state->req->out.domains = talloc_zero(state->msg, + struct lsa_RefDomainList *); + if (state->req->out.domains == NULL) { + return NT_STATUS_NO_MEMORY; + } + state->req->out.names = talloc_zero(state->msg, + struct lsa_TransNameArray2); + if (state->req->out.names == NULL) { + return NT_STATUS_NO_MEMORY; + } + state->req->out.count = talloc_zero(state->msg, uint32_t); + if (state->req->out.count == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->req->out.names->names = talloc_zero_array(state->msg, + struct lsa_TranslatedName2, + req->in.sids->num_sids); + if (state->req->out.names->names == NULL) { + return NT_STATUS_NO_MEMORY; + } + + sids = talloc_zero_array(state, struct dom_sid, + req->in.sids->num_sids); + if (sids == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; i < req->in.sids->num_sids; i++) { + if (req->in.sids->sids[i].sid == NULL) { + return NT_STATUS_REQUEST_NOT_ACCEPTED; + } + + sids[i] = *req->in.sids->sids[i].sid; + } + + subreq = wb_lookupsids_send(msg, + server_event_context(), + sids, req->in.sids->num_sids); + if (subreq == NULL) { + return NT_STATUS_NO_MEMORY; + } + tevent_req_set_callback(subreq, wb_irpc_lsa_LookupSids3_done, state); + msg->defer_reply = true; + + return NT_STATUS_OK; +} + +static void wb_irpc_lsa_LookupSids3_done(struct tevent_req *subreq) +{ + struct wb_irpc_lsa_LookupSids3_state *state = + tevent_req_callback_data(subreq, + struct wb_irpc_lsa_LookupSids3_state); + struct lsa_RefDomainList *domains = NULL; + struct lsa_TransNameArray *names = NULL; + NTSTATUS status; + uint32_t i; + + status = wb_lookupsids_recv(subreq, state->msg, + &domains, &names); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("RPC callback failed for %s - %s\n", + __func__, nt_errstr(status))); + irpc_send_reply(state->msg, status); + return; + } + + if (names->count > state->req->in.sids->num_sids) { + status = NT_STATUS_INTERNAL_ERROR; + DEBUG(0,("RPC callback failed for %s - %s\n", + __func__, nt_errstr(status))); + irpc_send_reply(state->msg, status); + return; + } + + *state->req->out.domains = domains; + for (i = 0; i < names->count; i++) { + struct lsa_TranslatedName2 *n2 = + &state->req->out.names->names[i]; + + n2->sid_type = names->names[i].sid_type; + n2->name = names->names[i].name; + n2->sid_index = names->names[i].sid_index; + n2->unknown = 0; + + if (n2->sid_type != SID_NAME_UNKNOWN) { + (*state->req->out.count)++; + } + } + state->req->out.names->count = names->count; + + if (*state->req->out.count == 0) { + state->req->out.result = NT_STATUS_NONE_MAPPED; + } else if (*state->req->out.count != names->count) { + state->req->out.result = NT_STATUS_SOME_NOT_MAPPED; + } else { + state->req->out.result = NT_STATUS_OK; + } + + irpc_send_reply(state->msg, NT_STATUS_OK); + return; +} + +struct wb_irpc_lsa_LookupNames4_name { + void *state; + uint32_t idx; + const char *domain; + char *name; + struct dom_sid sid; + enum lsa_SidType type; + struct dom_sid *authority_sid; +}; + +struct wb_irpc_lsa_LookupNames4_state { + struct irpc_message *msg; + struct lsa_LookupNames4 *req; + struct wb_irpc_lsa_LookupNames4_name *names; + uint32_t num_pending; + uint32_t num_domain_sids; + struct dom_sid *domain_sids; +}; + +static void wb_irpc_lsa_LookupNames4_done(struct tevent_req *subreq); + +static NTSTATUS wb_irpc_lsa_LookupNames4_call(struct irpc_message *msg, + struct lsa_LookupNames4 *req) +{ + struct wb_irpc_lsa_LookupNames4_state *state = NULL; + struct tevent_req *subreq = NULL; + uint32_t i; + + + state = talloc_zero(msg, struct wb_irpc_lsa_LookupNames4_state); + if (state == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->msg = msg; + state->req = req; + + state->req->out.domains = talloc_zero(state->msg, + struct lsa_RefDomainList *); + if (state->req->out.domains == NULL) { + return NT_STATUS_NO_MEMORY; + } + state->req->out.sids = talloc_zero(state->msg, + struct lsa_TransSidArray3); + if (state->req->out.sids == NULL) { + return NT_STATUS_NO_MEMORY; + } + state->req->out.count = talloc_zero(state->msg, uint32_t); + if (state->req->out.count == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->req->out.sids->sids = talloc_zero_array(state->msg, + struct lsa_TranslatedSid3, + req->in.num_names); + if (state->req->out.sids->sids == NULL) { + return NT_STATUS_NO_MEMORY; + } + + state->names = talloc_zero_array(state, + struct wb_irpc_lsa_LookupNames4_name, + req->in.num_names); + if (state->names == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; i < req->in.num_names; i++) { + struct wb_irpc_lsa_LookupNames4_name *nstate = + &state->names[i]; + char *p = NULL; + + if (req->in.names[i].string == NULL) { + DBG_ERR("%s: name[%s] NT_STATUS_REQUEST_NOT_ACCEPTED.\n", + __location__, req->in.names[i].string); + return NT_STATUS_REQUEST_NOT_ACCEPTED; + } + nstate->state = state; + nstate->idx = i; + nstate->name = talloc_strdup(state->names, + req->in.names[i].string); + if (nstate->name == NULL) { + return NT_STATUS_NO_MEMORY; + } + nstate->type = SID_NAME_UNKNOWN; + + /* cope with the name being a fully qualified name */ + p = strchr(nstate->name, '\\'); + if (p != NULL) { + *p = 0; + nstate->domain = nstate->name; + nstate->name = p+1; + } else if ((p = strchr(nstate->name, '@')) != NULL) { + /* upn */ + nstate->domain = p + 1; + *p = 0; + } else { + /* + * TODO: select the domain based on + * req->in.level and req->in.client_revision + * + * For now we don't allow this. + */ + DBG_ERR("%s: name[%s] NT_STATUS_REQUEST_NOT_ACCEPTED.\n", + __location__, nstate->name); + return NT_STATUS_REQUEST_NOT_ACCEPTED; + } + + subreq = wb_lookupname_send(msg, + server_event_context(), + nstate->domain, + nstate->name, + LOOKUP_NAME_NO_NSS); + if (subreq == NULL) { + return NT_STATUS_NO_MEMORY; + } + tevent_req_set_callback(subreq, + wb_irpc_lsa_LookupNames4_done, + nstate); + state->num_pending++; + } + + msg->defer_reply = true; + + return NT_STATUS_OK; +} + +static void wb_irpc_lsa_LookupNames4_domains_done(struct tevent_req *subreq); + +static void wb_irpc_lsa_LookupNames4_done(struct tevent_req *subreq) +{ + struct wb_irpc_lsa_LookupNames4_name *nstate = + (struct wb_irpc_lsa_LookupNames4_name *) + tevent_req_callback_data_void(subreq); + struct wb_irpc_lsa_LookupNames4_state *state = + talloc_get_type_abort(nstate->state, + struct wb_irpc_lsa_LookupNames4_state); + NTSTATUS status; + + SMB_ASSERT(state->num_pending > 0); + state->num_pending--; + status = wb_lookupname_recv(subreq, &nstate->sid, &nstate->type); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("RPC callback failed for %s - %s\n", + __func__, nt_errstr(status))); + irpc_send_reply(state->msg, status); + return; + } + + status = dom_sid_split_rid(state, &nstate->sid, + &nstate->authority_sid, NULL); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("dom_sid_split_rid(%s) failed - %s\n", + sid_string_dbg(&nstate->sid), nt_errstr(status)); + irpc_send_reply(state->msg, status); + return; + } + + status = add_sid_to_array_unique(state, + nstate->authority_sid, + &state->domain_sids, + &state->num_domain_sids); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("add_sid_to_array_unique(%s) failed - %s\n", + sid_string_dbg(nstate->authority_sid), nt_errstr(status)); + irpc_send_reply(state->msg, status); + return; + } + + if (state->num_pending > 0) { + /* + * wait for more... + */ + return; + } + + /* + * Now resolve all domains back to a name + * to get a good lsa_RefDomainList + */ + subreq = wb_lookupsids_send(state, + server_event_context(), + state->domain_sids, + state->num_domain_sids); + if (subreq == NULL) { + status = NT_STATUS_NO_MEMORY; + DBG_ERR("wb_lookupsids_send - %s\n", + nt_errstr(status)); + irpc_send_reply(state->msg, status); + return; + } + tevent_req_set_callback(subreq, + wb_irpc_lsa_LookupNames4_domains_done, + state); + + return; +} + +static void wb_irpc_lsa_LookupNames4_domains_done(struct tevent_req *subreq) +{ + struct wb_irpc_lsa_LookupNames4_state *state = + tevent_req_callback_data(subreq, + struct wb_irpc_lsa_LookupNames4_state); + struct lsa_RefDomainList *domains = NULL; + struct lsa_TransNameArray *names = NULL; + NTSTATUS status; + uint32_t i; + + status = wb_lookupsids_recv(subreq, state->msg, + &domains, &names); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("RPC callback failed for %s - %s\n", + __func__, nt_errstr(status))); + irpc_send_reply(state->msg, status); + return; + } + + *state->req->out.domains = domains; + for (i = 0; i < state->req->in.num_names; i++) { + struct wb_irpc_lsa_LookupNames4_name *nstate = + &state->names[i]; + struct lsa_TranslatedSid3 *s3 = + &state->req->out.sids->sids[i]; + uint32_t di; + + s3->sid_type = nstate->type; + if (s3->sid_type != SID_NAME_UNKNOWN) { + s3->sid = &nstate->sid; + } else { + s3->sid = NULL; + } + s3->sid_index = UINT32_MAX; + for (di = 0; di < domains->count; di++) { + bool match; + + if (domains->domains[di].sid == NULL) { + continue; + } + + match = dom_sid_equal(nstate->authority_sid, + domains->domains[di].sid); + if (match) { + s3->sid_index = di; + break; + } + } + if (s3->sid_type != SID_NAME_UNKNOWN) { + (*state->req->out.count)++; + } + } + state->req->out.sids->count = state->req->in.num_names; + + if (*state->req->out.count == 0) { + state->req->out.result = NT_STATUS_NONE_MAPPED; + } else if (*state->req->out.count != state->req->in.num_names) { + state->req->out.result = NT_STATUS_SOME_NOT_MAPPED; + } else { + state->req->out.result = NT_STATUS_OK; + } + + irpc_send_reply(state->msg, NT_STATUS_OK); + return; +} + NTSTATUS wb_irpc_register(void) { NTSTATUS status; @@ -361,6 +757,18 @@ NTSTATUS wb_irpc_register(void) if (!NT_STATUS_IS_OK(status)) { return status; } + status = IRPC_REGISTER(winbind_imessaging_context(), + lsarpc, LSA_LOOKUPSIDS3, + wb_irpc_lsa_LookupSids3_call, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = IRPC_REGISTER(winbind_imessaging_context(), + lsarpc, LSA_LOOKUPNAMES4, + wb_irpc_lsa_LookupNames4_call, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } return NT_STATUS_OK; } -- 2.11.4.GIT