idl: Remove unused KRB5_EDATA_NTSTATUS
[samba4-gss.git] / source3 / winbindd / wb_xids2sids.c
blob86bd7f9deab6a93b6affd63a6d1638c9f2252119
1 /*
2 * Unix SMB/CIFS implementation.
3 * async xids2sids
4 * Copyright (C) Volker Lendecke 2015
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "winbindd.h"
22 #include "../libcli/security/security.h"
23 #include "idmap_cache.h"
24 #include "librpc/gen_ndr/ndr_winbind_c.h"
25 #include "librpc/gen_ndr/ndr_netlogon.h"
26 #include "passdb/lookup_sid.h"
28 struct wb_xids2sids_dom_state {
29 struct tevent_context *ev;
30 struct unixid *all_xids;
31 const bool *cached;
32 size_t num_all_xids;
33 struct dom_sid *all_sids;
34 const struct wb_parent_idmap_config_dom *dom_map;
35 bool tried_dclookup;
37 size_t num_dom_xids;
38 struct unixid *dom_xids;
39 struct dom_sid *dom_sids;
42 static void wb_xids2sids_dom_done(struct tevent_req *subreq);
43 static void wb_xids2sids_dom_gotdc(struct tevent_req *subreq);
45 static struct tevent_req *wb_xids2sids_dom_send(
46 TALLOC_CTX *mem_ctx, struct tevent_context *ev,
47 const struct wb_parent_idmap_config_dom *dom_map,
48 struct unixid *xids,
49 const bool *cached,
50 size_t num_xids,
51 struct dom_sid *sids)
53 struct tevent_req *req, *subreq;
54 struct wb_xids2sids_dom_state *state;
55 struct dcerpc_binding_handle *child_binding_handle = NULL;
56 size_t i;
58 req = tevent_req_create(mem_ctx, &state,
59 struct wb_xids2sids_dom_state);
60 if (req == NULL) {
61 return NULL;
64 D_DEBUG("Searching for %zu xid(s) in domain %s.\n",
65 num_xids,
66 dom_map->name);
68 state->ev = ev;
69 state->all_xids = xids;
70 state->cached = cached;
71 state->num_all_xids = num_xids;
72 state->all_sids = sids;
73 state->dom_map = dom_map;
75 state->dom_xids = talloc_array(state, struct unixid, num_xids);
76 if (tevent_req_nomem(state->dom_xids, req)) {
77 return tevent_req_post(req, ev);
79 state->dom_sids = talloc_array(state, struct dom_sid, num_xids);
80 if (tevent_req_nomem(state->dom_sids, req)) {
81 return tevent_req_post(req, ev);
84 for (i=0; i<num_xids; i++) {
85 struct unixid id = state->all_xids[i];
87 if ((id.id < dom_map->low_id) || (id.id > dom_map->high_id)) {
88 /* out of range */
89 D_DEBUG("%zu: XID %"PRIu32" is out of range.\n",
90 i, id.id);
91 continue;
93 if (state->cached[i]) {
94 /* already found in cache */
95 D_DEBUG("%zu: XID %"PRIu32" is already found in cache.\n",
96 i, id.id);
97 continue;
99 if (!is_null_sid(&state->all_sids[i])) {
100 /* already mapped in a previously asked domain */
101 D_DEBUG("%zu: XID %"PRIu32" is already mapped in a previously asked domain.\n",
102 i, id.id);
103 continue;
105 D_DEBUG("%zu: XID %"PRIu32" will be looked up via dcerpc_wbint_UnixIDs2Sids_send().\n",
106 i, id.id);
107 state->dom_xids[state->num_dom_xids++] = id;
110 if (state->num_dom_xids == 0) {
111 tevent_req_done(req);
112 return tevent_req_post(req, ev);
115 child_binding_handle = idmap_child_handle();
116 subreq = dcerpc_wbint_UnixIDs2Sids_send(
117 state, ev, child_binding_handle, dom_map->name, dom_map->sid,
118 state->num_dom_xids, state->dom_xids, state->dom_sids);
119 if (tevent_req_nomem(subreq, req)) {
120 return tevent_req_post(req, ev);
122 tevent_req_set_callback(subreq, wb_xids2sids_dom_done, req);
123 return req;
126 static void wb_xids2sids_dom_done(struct tevent_req *subreq)
128 struct tevent_req *req = tevent_req_callback_data(
129 subreq, struct tevent_req);
130 struct wb_xids2sids_dom_state *state = tevent_req_data(
131 req, struct wb_xids2sids_dom_state);
132 const struct wb_parent_idmap_config_dom *dom_map = state->dom_map;
133 NTSTATUS status, result;
134 size_t i;
135 size_t dom_sid_idx;
137 status = dcerpc_wbint_UnixIDs2Sids_recv(subreq, state, &result);
138 TALLOC_FREE(subreq);
139 if (tevent_req_nterror(req, status)) {
140 return;
143 if (NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) &&
144 !state->tried_dclookup) {
146 subreq = wb_dsgetdcname_send(
147 state, state->ev, state->dom_map->name, NULL, NULL,
148 DS_RETURN_DNS_NAME);
149 if (tevent_req_nomem(subreq, req)) {
150 return;
152 tevent_req_set_callback(subreq, wb_xids2sids_dom_gotdc, req);
153 return;
156 if (!NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED) &&
157 tevent_req_nterror(req, result)) {
158 return;
161 dom_sid_idx = 0;
163 D_DEBUG("Processing response for %zu xid(s).\n", state->num_all_xids);
164 for (i=0; i<state->num_all_xids; i++) {
165 struct unixid *id = &state->all_xids[i];
166 struct dom_sid_buf buf;
168 if ((id->id < dom_map->low_id) || (id->id > dom_map->high_id)) {
169 /* out of range */
170 continue;
172 if (state->cached[i]) {
173 /* already found in cache */
174 continue;
176 if (!is_null_sid(&state->all_sids[i])) {
177 /* already mapped in a previously asked domain */
178 continue;
181 sid_copy(&state->all_sids[i], &state->dom_sids[dom_sid_idx]);
182 *id = state->dom_xids[dom_sid_idx];
183 D_DEBUG("%zu: XID %"PRIu32" mapped to SID %s.\n",
185 id->id,
186 dom_sid_str_buf(&state->all_sids[i], &buf));
188 dom_sid_idx += 1;
191 tevent_req_done(req);
194 static void wb_xids2sids_dom_gotdc(struct tevent_req *subreq)
196 struct tevent_req *req = tevent_req_callback_data(
197 subreq, struct tevent_req);
198 struct wb_xids2sids_dom_state *state = tevent_req_data(
199 req, struct wb_xids2sids_dom_state);
200 struct dcerpc_binding_handle *child_binding_handle = NULL;
201 struct netr_DsRGetDCNameInfo *dcinfo;
202 NTSTATUS status;
204 status = wb_dsgetdcname_recv(subreq, state, &dcinfo);
205 TALLOC_FREE(subreq);
206 if (tevent_req_nterror(req, status)) {
207 return;
210 state->tried_dclookup = true;
212 status = wb_dsgetdcname_gencache_set(state->dom_map->name, dcinfo);
213 if (tevent_req_nterror(req, status)) {
214 return;
217 child_binding_handle = idmap_child_handle();
218 subreq = dcerpc_wbint_UnixIDs2Sids_send(
219 state, state->ev, child_binding_handle, state->dom_map->name,
220 state->dom_map->sid, state->num_dom_xids,
221 state->dom_xids, state->dom_sids);
222 if (tevent_req_nomem(subreq, req)) {
223 return;
225 tevent_req_set_callback(subreq, wb_xids2sids_dom_done, req);
228 static NTSTATUS wb_xids2sids_dom_recv(struct tevent_req *req)
230 return tevent_req_simple_recv_ntstatus(req);
233 struct wb_xids2sids_state {
234 struct tevent_context *ev;
235 struct unixid *xids;
236 size_t num_xids;
237 struct dom_sid *sids;
238 bool *cached;
240 size_t dom_idx;
241 const struct wb_parent_idmap_config *cfg;
244 static void wb_xids2sids_idmap_setup_done(struct tevent_req *subreq);
245 static void wb_xids2sids_done(struct tevent_req *subreq);
247 struct tevent_req *wb_xids2sids_send(TALLOC_CTX *mem_ctx,
248 struct tevent_context *ev,
249 const struct unixid *xids,
250 uint32_t num_xids)
252 struct tevent_req *req, *subreq;
253 struct wb_xids2sids_state *state;
255 req = tevent_req_create(mem_ctx, &state,
256 struct wb_xids2sids_state);
257 if (req == NULL) {
258 return NULL;
261 D_INFO("WB command xids2sids start.\nLooking up %"PRIu32" XID(s).\n",
262 num_xids);
264 state->ev = ev;
265 state->num_xids = num_xids;
267 state->xids = talloc_array(state, struct unixid, num_xids);
268 if (tevent_req_nomem(state->xids, req)) {
269 return tevent_req_post(req, ev);
271 memcpy(state->xids, xids, num_xids * sizeof(struct unixid));
273 state->sids = talloc_zero_array(state, struct dom_sid, num_xids);
274 if (tevent_req_nomem(state->sids, req)) {
275 return tevent_req_post(req, ev);
278 state->cached = talloc_zero_array(state, bool, num_xids);
279 if (tevent_req_nomem(state->cached, req)) {
280 return tevent_req_post(req, ev);
283 if (winbindd_use_idmap_cache()) {
284 uint32_t i;
286 for (i=0; i<num_xids; i++) {
287 struct dom_sid sid = {0};
288 bool ok, expired = true;
290 ok = idmap_cache_find_xid2sid(
291 &xids[i], &sid, &expired);
292 if (ok && !expired) {
293 struct dom_sid_buf buf;
294 DBG_DEBUG("Found %cID in cache: %s\n",
295 xids[i].type == ID_TYPE_UID?'U':'G',
296 dom_sid_str_buf(&sid, &buf));
298 sid_copy(&state->sids[i], &sid);
299 state->cached[i] = true;
304 subreq = wb_parent_idmap_setup_send(state, state->ev);
305 if (tevent_req_nomem(subreq, req)) {
306 return tevent_req_post(req, ev);
308 tevent_req_set_callback(subreq, wb_xids2sids_idmap_setup_done, req);
309 return req;
312 static void wb_xids2sids_idmap_setup_done(struct tevent_req *subreq)
314 struct tevent_req *req = tevent_req_callback_data(
315 subreq, struct tevent_req);
316 struct wb_xids2sids_state *state = tevent_req_data(
317 req, struct wb_xids2sids_state);
318 NTSTATUS status;
320 status = wb_parent_idmap_setup_recv(subreq, &state->cfg);
321 TALLOC_FREE(subreq);
322 if (tevent_req_nterror(req, status)) {
323 return;
325 SMB_ASSERT(state->cfg->num_doms > 0);
327 subreq = wb_xids2sids_dom_send(
328 state, state->ev,
329 &state->cfg->doms[state->dom_idx],
330 state->xids, state->cached, state->num_xids, state->sids);
331 if (tevent_req_nomem(subreq, req)) {
332 return;
334 tevent_req_set_callback(subreq, wb_xids2sids_done, req);
335 return;
338 static void wb_xids2sids_done(struct tevent_req *subreq)
340 struct tevent_req *req = tevent_req_callback_data(
341 subreq, struct tevent_req);
342 struct wb_xids2sids_state *state = tevent_req_data(
343 req, struct wb_xids2sids_state);
344 size_t i;
345 NTSTATUS status;
347 status = wb_xids2sids_dom_recv(subreq);
348 TALLOC_FREE(subreq);
349 if (tevent_req_nterror(req, status)) {
350 return;
353 state->dom_idx += 1;
354 if (state->dom_idx < state->cfg->num_doms) {
355 const struct wb_parent_idmap_config_dom *dom_map =
356 &state->cfg->doms[state->dom_idx];
358 subreq = wb_xids2sids_dom_send(state,
359 state->ev,
360 dom_map,
361 state->xids,
362 state->cached,
363 state->num_xids,
364 state->sids);
365 if (tevent_req_nomem(subreq, req)) {
366 return;
368 tevent_req_set_callback(subreq, wb_xids2sids_done, req);
369 return;
373 for (i = 0; i < state->num_xids; i++) {
375 * Prime the cache after an xid2sid call. It's important that we
376 * use the xid value returned from the backend for the xid value
377 * passed to idmap_cache_set_sid2unixid(), not the input to
378 * wb_xids2sids_send: the input carries what was asked for,
379 * e.g. a ID_TYPE_UID. The result from the backend something the
380 * idmap child possibly changed to ID_TYPE_BOTH.
382 * And of course If the value was from the cache don't update
383 * the cache.
386 if (state->cached[i]) {
387 continue;
390 idmap_cache_set_sid2unixid(&state->sids[i], &state->xids[i]);
393 tevent_req_done(req);
394 return;
397 NTSTATUS wb_xids2sids_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
398 struct dom_sid **sids)
400 struct wb_xids2sids_state *state = tevent_req_data(
401 req, struct wb_xids2sids_state);
402 NTSTATUS status;
403 size_t i;
405 D_INFO("WB command xids2sids end.\n");
406 if (tevent_req_is_nterror(req, &status)) {
407 D_WARNING("wb_sids_to_xids failed: %s\n", nt_errstr(status));
408 return status;
411 *sids = talloc_move(mem_ctx, &state->sids);
412 if (CHECK_DEBUGLVL(DBGLVL_INFO)) {
413 for (i = 0; i < state->num_xids; i++) {
414 struct dom_sid_buf buf;
415 D_INFO("%zu: XID %"PRIu32" mapped to SID %s\n",
417 state->xids[i].id,
418 dom_sid_str_buf(&((*sids)[i]), &buf));
421 return NT_STATUS_OK;