winbindd: implement wb_irpc_lsa_{LookupNames4,LookupSids3}()
authorStefan Metzmacher <metze@samba.org>
Tue, 23 Jan 2018 12:19:37 +0000 (13:19 +0100)
committerRalph Boehme <slow@samba.org>
Wed, 21 Feb 2018 13:19:19 +0000 (14:19 +0100)
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 <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/winbindd/winbindd_irpc.c

index 4101469ad99350e47142667e177e524b4fe67871..e03312ec7af7080204b732d98d96f23126d95476 100644 (file)
 #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;
 }