s3: Export WINBINDD_SIDS_TO_XIDS via the winbind pipe
authorVolker Lendecke <vl@samba.org>
Wed, 23 Mar 2011 17:29:45 +0000 (18:29 +0100)
committerJeremy Allison <jra@samba.org>
Wed, 13 Apr 2011 21:13:25 +0000 (14:13 -0700)
Signed-off-by: Jeremy Allison <jra@samba.org>
source3/Makefile.in
source3/winbindd/winbindd.c
source3/winbindd/winbindd_proto.h
source3/winbindd/winbindd_sids_to_xids.c [new file with mode: 0644]

index 8211edc79ae7b01a0ef21d3bbc43fb9fe46845de..0e748054175e0de010ed0ab058a1cb5688cd3c18 100644 (file)
@@ -1380,6 +1380,7 @@ WINBINDD_OBJ1 = \
                winbindd/winbindd_sid_to_gid.o \
                winbindd/winbindd_uid_to_sid.o \
                winbindd/winbindd_gid_to_sid.o \
+               winbindd/winbindd_sids_to_xids.o \
                winbindd/winbindd_allocate_uid.o \
                winbindd/winbindd_allocate_gid.o \
                winbindd/winbindd_getpwsid.o \
index 330f9008317d81e1cbfcd8efd2e301ceb922b07d..c5a124a628fae7f6c6efb5a6d87f2b1d05fa413e 100644 (file)
@@ -483,6 +483,8 @@ static struct winbindd_async_dispatch_table async_nonpriv_table[] = {
          winbindd_uid_to_sid_send, winbindd_uid_to_sid_recv },
        { WINBINDD_GID_TO_SID, "GID_TO_SID",
          winbindd_gid_to_sid_send, winbindd_gid_to_sid_recv },
+       { WINBINDD_SIDS_TO_XIDS, "SIDS_TO_XIDS",
+         winbindd_sids_to_xids_send, winbindd_sids_to_xids_recv },
        { WINBINDD_GETPWSID, "GETPWSID",
          winbindd_getpwsid_send, winbindd_getpwsid_recv },
        { WINBINDD_GETPWNAM, "GETPWNAM",
index ae5130748ded180a7c4a243aba1edbffe05187cd..5b48a99a8fbcbb812bb25ace8c6b57366a20e229 100644 (file)
@@ -855,6 +855,14 @@ NTSTATUS wb_lookupsids_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
                            struct lsa_RefDomainList **domains,
                            struct lsa_TransNameArray **names);
 
+struct tevent_req *winbindd_sids_to_xids_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct winbindd_cli_state *cli,
+                                             struct winbindd_request *request);
+NTSTATUS winbindd_sids_to_xids_recv(struct tevent_req *req,
+                                   struct winbindd_response *response);
+
+
 /* The following definitions come from winbindd/winbindd_samr.c  */
 
 NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx,
diff --git a/source3/winbindd/winbindd_sids_to_xids.c b/source3/winbindd/winbindd_sids_to_xids.c
new file mode 100644 (file)
index 0000000..7dc95af
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+   Unix SMB/CIFS implementation.
+   async implementation of WINBINDD_SIDS_TO_XIDS
+   Copyright (C) Volker Lendecke 2011
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "winbindd.h"
+#include "../libcli/security/security.h"
+#include "librpc/gen_ndr/ndr_wbint_c.h"
+#include "idmap_cache.h"
+
+struct winbindd_sids_to_xids_state {
+       struct tevent_context *ev;
+       struct dom_sid *sids;
+       uint32_t num_sids;
+
+       struct id_map *cached;
+
+       struct dom_sid *non_cached;
+       uint32_t num_non_cached;
+
+       struct lsa_RefDomainList *domains;
+       struct lsa_TransNameArray *names;
+
+       struct wbint_TransIDArray ids;
+};
+
+static bool winbindd_sids_to_xids_in_cache(struct dom_sid *sid,
+                                          struct id_map *map);
+static void winbindd_sids_to_xids_lookupsids_done(struct tevent_req *subreq);
+static void winbindd_sids_to_xids_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_sids_to_xids_send(TALLOC_CTX *mem_ctx,
+                                             struct tevent_context *ev,
+                                             struct winbindd_cli_state *cli,
+                                             struct winbindd_request *request)
+{
+       struct tevent_req *req, *subreq;
+       struct winbindd_sids_to_xids_state *state;
+       uint32_t i;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct winbindd_sids_to_xids_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+
+       DEBUG(3, ("sids_to_xids\n"));
+
+       if (request->extra_len == 0) {
+               tevent_req_done(req);
+               return tevent_req_post(req, ev);
+       }
+       if (request->extra_data.data[request->extra_len-1] != '\0') {
+               DEBUG(10, ("Got invalid sids list\n"));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+       if (!parse_sidlist(state, request->extra_data.data,
+                          &state->sids, &state->num_sids)) {
+               DEBUG(10, ("parse_sidlist failed\n"));
+               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+               return tevent_req_post(req, ev);
+       }
+
+       state->cached = TALLOC_ZERO_ARRAY(state, struct id_map,
+                                         state->num_sids);
+       if (tevent_req_nomem(state->cached, req)) {
+               return tevent_req_post(req, ev);
+       }
+       state->non_cached = TALLOC_ARRAY(state, struct dom_sid,
+                                        state->num_sids);
+       if (tevent_req_nomem(state->non_cached, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       for (i=0; i<state->num_sids; i++) {
+               if (winbindd_sids_to_xids_in_cache(&state->sids[i],
+                                                  &state->cached[i])) {
+                       continue;
+               }
+               sid_copy(&state->non_cached[state->num_non_cached],
+                        &state->sids[i]);
+               state->num_non_cached += 1;
+       }
+
+        if (state->num_non_cached == 0) {
+                tevent_req_done(req);
+                return tevent_req_post(req, ev);
+        }
+
+       subreq = wb_lookupsids_send(state, ev, state->non_cached,
+                                   state->num_non_cached);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, winbindd_sids_to_xids_lookupsids_done,
+                               req);
+       return req;
+}
+
+static bool winbindd_sids_to_xids_in_cache(struct dom_sid *sid,
+                                          struct id_map *map)
+{
+       uid_t uid;
+       gid_t gid;
+       bool expired;
+
+       if (!winbindd_use_idmap_cache()) {
+               return false;
+       }
+       /*
+        * SIDS_TO_XIDS is primarily used to resolve the user's group
+        * sids. So we check groups before users.
+        */
+       if (idmap_cache_find_sid2gid(sid, &gid, &expired)) {
+               if (expired && is_domain_offline(find_our_domain())) {
+                       return false;
+               }
+               map->sid = sid;
+               map->xid.id = gid;
+               map->xid.type = ID_TYPE_GID;
+               map->status = ID_MAPPED;
+               return true;
+       }
+       if (idmap_cache_find_sid2uid(sid, &uid, &expired)) {
+               if (expired && is_domain_online(find_our_domain())) {
+                       return false;
+               }
+               map->sid = sid;
+               map->xid.id = uid;
+               map->xid.type = ID_TYPE_UID;
+               map->status = ID_MAPPED;
+               return true;
+       }
+       return false;
+}
+
+
+static void winbindd_sids_to_xids_lookupsids_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct winbindd_sids_to_xids_state *state = tevent_req_data(
+               req, struct winbindd_sids_to_xids_state);
+       struct winbindd_child *child;
+       NTSTATUS status;
+       int i;
+
+       status = wb_lookupsids_recv(subreq, state, &state->domains,
+                                   &state->names);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       state->ids.num_ids = state->num_non_cached;
+       state->ids.ids = TALLOC_ARRAY(state, struct wbint_TransID,
+                                     state->num_non_cached);
+       if (tevent_req_nomem(state->ids.ids, req)) {
+               return;
+       }
+
+       for (i=0; i<state->num_non_cached; i++) {
+               struct lsa_TranslatedName *n = &state->names->names[i];
+               struct wbint_TransID *t = &state->ids.ids[i];
+
+               switch (n->sid_type) {
+               case SID_NAME_USER:
+               case SID_NAME_COMPUTER:
+                       t->type = WBC_ID_TYPE_UID;
+                       break;
+               case SID_NAME_DOM_GRP:
+               case SID_NAME_ALIAS:
+               case SID_NAME_WKN_GRP:
+                       t->type = WBC_ID_TYPE_GID;
+                       break;
+               default:
+                       t->type = WBC_ID_TYPE_NOT_SPECIFIED;
+                       break;
+               };
+               t->domain_index = n->sid_index;
+               sid_peek_rid(&state->sids[i], &t->rid);
+               t->unix_id = (uint64_t)-1;
+       }
+
+       child = idmap_child();
+
+       subreq = dcerpc_wbint_Sids2UnixIDs_send(
+               state, state->ev, child->binding_handle, state->domains,
+               &state->ids);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, winbindd_sids_to_xids_done, req);
+}
+
+static void winbindd_sids_to_xids_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct winbindd_sids_to_xids_state *state = tevent_req_data(
+               req, struct winbindd_sids_to_xids_state);
+       NTSTATUS status, result;
+
+       status = dcerpc_wbint_Sids2UnixIDs_recv(subreq, state, &result);
+       TALLOC_FREE(subreq);
+       if (any_nt_status_not_ok(status, result, &status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+       tevent_req_done(req);
+}
+
+NTSTATUS winbindd_sids_to_xids_recv(struct tevent_req *req,
+                                   struct winbindd_response *response)
+{
+       struct winbindd_sids_to_xids_state *state = tevent_req_data(
+               req, struct winbindd_sids_to_xids_state);
+       NTSTATUS status;
+       char *result;
+       uint32_t i, num_non_cached;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               DEBUG(5, ("wb_sids_to_xids failed: %s\n", nt_errstr(status)));
+               return status;
+       }
+
+       result = talloc_strdup(response, "");
+       if (result == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       num_non_cached = 0;
+
+       for (i=0; i<state->num_sids; i++) {
+               char type;
+               uint64_t unix_id = (uint64_t)-1;
+               bool found = true;
+
+               if (state->cached[i].sid != NULL) {
+                       unix_id = state->cached[i].xid.id;
+                       if (state->cached[i].xid.type == ID_TYPE_UID) {
+                               type = 'U';
+                       } else {
+                               type = 'G';
+                       }
+               } else {
+                       unix_id = state->ids.ids[num_non_cached].unix_id;
+                       switch(state->ids.ids[num_non_cached].type) {
+                       case WBC_ID_TYPE_UID:
+                               type = 'U';
+                               idmap_cache_set_sid2uid(
+                                       &state->non_cached[num_non_cached],
+                                       unix_id);
+                               break;
+                       case WBC_ID_TYPE_GID:
+                               type = 'G';
+                               idmap_cache_set_sid2gid(
+                                       &state->non_cached[num_non_cached],
+                                       unix_id);
+                               break;
+                       default:
+                               found = false;
+                       }
+                       num_non_cached += 1;
+               }
+
+               if (found) {
+                       result = talloc_asprintf_append_buffer(
+                               result, "%c%lu\n", type,
+                               (unsigned long)unix_id);
+               } else {
+                       result = talloc_asprintf_append_buffer(result, "\n");
+               }
+               if (result == NULL) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       response->extra_data.data = result;
+       response->length += talloc_get_size(result);
+       return NT_STATUS_OK;
+}