summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/Makefile.in1
-rw-r--r--source3/winbindd/winbindd.c2
-rw-r--r--source3/winbindd/winbindd_proto.h8
-rw-r--r--source3/winbindd/winbindd_sids_to_xids.c300
4 files changed, 311 insertions, 0 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in
index 8211edc79a..0e74805417 100644
--- a/source3/Makefile.in
+++ b/source3/Makefile.in
@@ -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 \
diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c
index 330f900831..c5a124a628 100644
--- a/source3/winbindd/winbindd.c
+++ b/source3/winbindd/winbindd.c
@@ -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",
diff --git a/source3/winbindd/winbindd_proto.h b/source3/winbindd/winbindd_proto.h
index ae5130748d..5b48a99a8f 100644
--- a/source3/winbindd/winbindd_proto.h
+++ b/source3/winbindd/winbindd_proto.h
@@ -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
index 0000000000..7dc95af592
--- /dev/null
+++ b/source3/winbindd/winbindd_sids_to_xids.c
@@ -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;
+}