2 Unix SMB/CIFS implementation.
4 Copyright (C) Volker Lendecke 2011
5 Copyright (C) Michael Adam 2012
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "../libcli/security/security.h"
24 #include "idmap_cache.h"
25 #include "librpc/gen_ndr/ndr_winbind_c.h"
26 #include "librpc/gen_ndr/ndr_netlogon.h"
29 struct wb_sids2xids_state
{
30 struct tevent_context
*ev
;
32 const struct wb_parent_idmap_config
*cfg
;
37 struct wbint_TransIDArray all_ids
;
39 /* Used to translated the idx back into all_ids.ids[idx] */
42 uint32_t lookup_count
;
43 struct dom_sid
*lookup_sids
;
45 struct wbint_TransIDArray map_ids_in
;
46 struct wbint_TransIDArray map_ids_out
;
49 * Domain array to use for the idmap call. The output from
50 * lookupsids cannot be used directly since for migrated
51 * objects the returned domain SID can be different than the
52 * original one. The new domain SID cannot be combined with
53 * the RID from the previous domain.
55 * The proper way would be asking for the correct RID in the
56 * new domain, but this approach avoids id mappings for
59 struct lsa_RefDomainList idmap_doms
;
62 struct lsa_RefDomainList idmap_dom
;
66 static void wb_sids2xids_idmap_setup_done(struct tevent_req
*subreq
);
67 static bool wb_sids2xids_in_cache(struct dom_sid
*sid
, struct id_map
*map
);
68 static void wb_sids2xids_lookupsids_done(struct tevent_req
*subreq
);
69 static void wb_sids2xids_done(struct tevent_req
*subreq
);
70 static void wb_sids2xids_gotdc(struct tevent_req
*subreq
);
71 static void wb_sids2xids_next_sids2unix(struct tevent_req
*req
);
72 static enum id_type
lsa_SidType_to_id_type(const enum lsa_SidType sid_type
);
74 struct tevent_req
*wb_sids2xids_send(TALLOC_CTX
*mem_ctx
,
75 struct tevent_context
*ev
,
76 const struct dom_sid
*sids
,
77 const uint32_t num_sids
)
79 struct tevent_req
*req
, *subreq
;
80 struct wb_sids2xids_state
*state
;
82 uint32_t num_valid
= 0;
84 req
= tevent_req_create(mem_ctx
, &state
,
85 struct wb_sids2xids_state
);
90 D_INFO("WB command sids2xids start.\n"
91 "Resolving %"PRIu32
" SID(s).\n", num_sids
);
95 state
->num_sids
= num_sids
;
97 state
->sids
= talloc_zero_array(state
, struct dom_sid
, num_sids
);
98 if (tevent_req_nomem(state
->sids
, req
)) {
99 return tevent_req_post(req
, ev
);
102 for (i
= 0; i
< num_sids
; i
++) {
103 sid_copy(&state
->sids
[i
], &sids
[i
]);
106 state
->all_ids
.num_ids
= num_sids
;
107 state
->all_ids
.ids
= talloc_zero_array(state
, struct wbint_TransID
, num_sids
);
108 if (tevent_req_nomem(state
->all_ids
.ids
, req
)) {
109 return tevent_req_post(req
, ev
);
112 state
->tmp_idx
= talloc_zero_array(state
, uint32_t, num_sids
);
113 if (tevent_req_nomem(state
->tmp_idx
, req
)) {
114 return tevent_req_post(req
, ev
);
117 state
->lookup_sids
= talloc_zero_array(state
, struct dom_sid
, num_sids
);
118 if (tevent_req_nomem(state
->lookup_sids
, req
)) {
119 return tevent_req_post(req
, ev
);
122 state
->map_ids_in
.ids
= talloc_zero_array(state
, struct wbint_TransID
, num_sids
);
123 if (tevent_req_nomem(state
->map_ids_in
.ids
, req
)) {
124 return tevent_req_post(req
, ev
);
128 * Extract those sids that can not be resolved from cache
129 * into a separate list to be handed to id mapping, keeping
132 for (i
=0; i
<state
->num_sids
; i
++) {
133 struct wbint_TransID
*cur_id
= &state
->all_ids
.ids
[i
];
134 struct dom_sid domain_sid
;
135 struct dom_sid_buf buf
;
136 struct id_map map
= { .status
= ID_UNMAPPED
, };
140 sid_copy(&domain_sid
, &state
->sids
[i
]);
141 sid_split_rid(&domain_sid
, &rid
);
144 * Start with an invalid entry.
146 *cur_id
= (struct wbint_TransID
) {
147 .type_hint
= ID_TYPE_NOT_SPECIFIED
,
148 .domain_index
= UINT32_MAX
- 1, /* invalid */
152 .type
= ID_TYPE_NOT_SPECIFIED
,
156 D_DEBUG("%"PRIu32
": SID %s\n",
157 i
, dom_sid_str_buf(&state
->sids
[i
], &buf
));
159 in_cache
= wb_sids2xids_in_cache(&state
->sids
[i
], &map
);
162 * We used to ignore map.status and just rely
165 * Lets keep this logic for now...
168 cur_id
->xid
= map
.xid
;
169 cur_id
->domain_index
= UINT32_MAX
; /* this marks it as filled entry */
175 D_DEBUG("Found %"PRIu32
" (out of %"PRIu32
") SID(s) in cache.\n",
176 num_valid
, num_sids
);
177 if (num_valid
== num_sids
) {
178 tevent_req_done(req
);
179 return tevent_req_post(req
, ev
);
182 subreq
= wb_parent_idmap_setup_send(state
, state
->ev
);
183 if (tevent_req_nomem(subreq
, req
)) {
184 return tevent_req_post(req
, ev
);
186 tevent_req_set_callback(subreq
, wb_sids2xids_idmap_setup_done
, req
);
190 static void wb_sids2xids_idmap_setup_done(struct tevent_req
*subreq
)
192 struct tevent_req
*req
= tevent_req_callback_data(
193 subreq
, struct tevent_req
);
194 struct wb_sids2xids_state
*state
= tevent_req_data(
195 req
, struct wb_sids2xids_state
);
199 status
= wb_parent_idmap_setup_recv(subreq
, &state
->cfg
);
201 if (tevent_req_nterror(req
, status
)) {
202 D_WARNING("Failed with %s.\n", nt_errstr(status
));
205 SMB_ASSERT(state
->cfg
->num_doms
> 0);
206 D_DEBUG("We will loop over %"PRIu32
" SID(s) (skipping those already resolved via cache) and over %"PRIu32
" domain(s).\n",
208 state
->cfg
->num_doms
);
211 * Now we build a list with all domain
212 * with non cached entries
214 for (i
=0; i
<state
->num_sids
; i
++) {
215 struct wbint_TransID
*t
= &state
->all_ids
.ids
[i
];
216 struct dom_sid domain_sid
;
217 const char *domain_name
= NULL
;
221 struct dom_sid_buf buf0
, buf1
;
223 D_DEBUG("%"PRIu32
": Processing SID %s\n",
225 dom_sid_str_buf(&state
->sids
[i
], &buf0
));
226 if (t
->domain_index
== UINT32_MAX
) {
227 /* ignore already filled entries */
228 D_DEBUG("%"PRIu32
": Ignoring already resolved SID %s\n",
230 dom_sid_str_buf(&state
->sids
[i
], &buf0
));
234 sid_copy(&domain_sid
, &state
->sids
[i
]);
235 sid_split_rid(&domain_sid
, &rid
);
236 D_DEBUG("%"PRIu32
": Split SID %s into domain SID %s and RID %"PRIu32
"\n",
238 dom_sid_str_buf(&state
->sids
[i
], &buf0
),
239 dom_sid_str_buf(&domain_sid
, &buf1
),
242 if (t
->type_hint
== ID_TYPE_NOT_SPECIFIED
) {
243 const char *tmp_name
= NULL
;
244 enum lsa_SidType sid_type
= SID_NAME_USE_NONE
;
245 const struct dom_sid
*tmp_authority_sid
= NULL
;
246 const char *tmp_authority_name
= NULL
;
249 * Try to get a type hint from for predefined sids
251 status
= dom_sid_lookup_predefined_sid(&state
->sids
[i
],
255 &tmp_authority_name
);
256 if (NT_STATUS_IS_OK(status
)) {
257 t
->type_hint
= lsa_SidType_to_id_type(sid_type
);
258 D_DEBUG("Got a type hint: %d from predefined SID.\n",
263 D_DEBUG("Looping over %"PRIu32
" domain(s) to find domain SID %s.\n",
264 state
->cfg
->num_doms
,
265 dom_sid_str_buf(&domain_sid
, &buf0
));
266 for (di
= 0; di
< state
->cfg
->num_doms
; di
++) {
267 struct wb_parent_idmap_config_dom
*dom
=
268 &state
->cfg
->doms
[di
];
271 match
= dom_sid_equal(&domain_sid
, &dom
->sid
);
276 domain_name
= dom
->name
;
277 D_DEBUG("Found domain '%s'.\n", domain_name
);
280 if (domain_name
== NULL
) {
281 struct winbindd_domain
*wb_domain
= NULL
;
283 D_DEBUG("Could not find a domain for domain SID %s. Trying to fill the domain name from list of known domains.\n",
284 dom_sid_str_buf(&domain_sid
, &buf0
));
286 * Try to fill the name if we already know it
288 wb_domain
= find_domain_from_sid_noinit(&state
->sids
[i
]);
289 if (wb_domain
!= NULL
) {
290 domain_name
= wb_domain
->name
;
291 D_DEBUG("Found domain '%s' in list of known domains.\n", domain_name
);
294 if (domain_name
== NULL
) {
296 D_DEBUG("Not found domain in list of known domains, setting empty domain name.\n");
299 if (t
->type_hint
== ID_TYPE_NOT_SPECIFIED
) {
300 if (domain_name
[0] != '\0') {
302 * We know the domain, we indicate this
303 * by passing ID_TYPE_BOTH as a hint
305 * Maybe that's already enough for the backend
307 t
->type_hint
= ID_TYPE_BOTH
;
308 D_DEBUG("Setting type hint ID_TYPE_BOTH for domain '%s'.\n", domain_name
);
312 domain_index
= init_lsa_ref_domain_list(state
,
316 if (domain_index
== -1) {
320 t
->domain_index
= domain_index
;
324 * We defer lookupsids because it requires domain controller
327 * First we ask the idmap child without explicit type hints.
328 * In most cases mappings already exist in the backend and
329 * a type_hint is not needed.
331 wb_sids2xids_next_sids2unix(req
);
334 static bool wb_sids2xids_in_cache(struct dom_sid
*sid
, struct id_map
*map
)
339 if (!winbindd_use_idmap_cache()) {
342 if (idmap_cache_find_sid2unixid(sid
, &id
, &expired
)) {
343 if (expired
&& is_domain_online(find_our_domain())) {
348 map
->status
= ID_MAPPED
;
354 static void wb_sids2xids_lookupsids_done(struct tevent_req
*subreq
)
356 struct tevent_req
*req
= tevent_req_callback_data(
357 subreq
, struct tevent_req
);
358 struct wb_sids2xids_state
*state
= tevent_req_data(
359 req
, struct wb_sids2xids_state
);
360 struct lsa_RefDomainList
*domains
= NULL
;
361 struct lsa_TransNameArray
*names
= NULL
;
365 status
= wb_lookupsids_recv(subreq
, state
, &domains
, &names
);
367 if (tevent_req_nterror(req
, status
)) {
368 D_WARNING("Failed with %s.\n", nt_errstr(status
));
372 if (domains
== NULL
) {
373 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
374 D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
379 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
380 D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
384 for (li
= 0; li
< state
->lookup_count
; li
++) {
385 struct lsa_TranslatedName
*n
= &names
->names
[li
];
386 uint32_t ai
= state
->tmp_idx
[li
];
387 struct wbint_TransID
*t
= &state
->all_ids
.ids
[ai
];
388 enum id_type type_hint
;
390 type_hint
= lsa_SidType_to_id_type(n
->sid_type
);
391 if (type_hint
!= ID_TYPE_NOT_SPECIFIED
) {
393 * We know it's a valid user or group.
395 t
->type_hint
= type_hint
;
399 if (n
->sid_index
== UINT32_MAX
) {
401 * The domain is not known, there's
402 * no point to try mapping again.
403 * mark is done and add a negative cache
406 t
->domain_index
= UINT32_MAX
; /* mark as valid */
407 idmap_cache_set_sid2unixid(&state
->sids
[ai
], &t
->xid
);
411 if (n
->sid_index
>= domains
->count
) {
412 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
413 D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
417 if (domains
->domains
[n
->sid_index
].name
.string
== NULL
) {
418 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
419 D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
422 if (domains
->domains
[n
->sid_index
].sid
== NULL
) {
423 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
424 D_WARNING("Failed with NT_STATUS_INTERNAL_ERROR.\n");
428 if (t
->type_hint
!= ID_TYPE_NOT_SPECIFIED
) {
430 * We already tried with a type hint there's
431 * no point to try mapping again with ID_TYPE_BOTH.
433 * Mark is done and add a negative cache entry.
435 t
->domain_index
= UINT32_MAX
; /* mark as valid */
436 idmap_cache_set_sid2unixid(&state
->sids
[ai
], &t
->xid
);
441 * We only know the domain exists, but the user doesn't
443 t
->type_hint
= ID_TYPE_BOTH
;
447 TALLOC_FREE(domains
);
450 * Now that we have type_hints for the remaining sids,
451 * we need to restart with the first domain.
453 state
->dom_index
= 0;
454 wb_sids2xids_next_sids2unix(req
);
457 static void wb_sids2xids_next_sids2unix(struct tevent_req
*req
)
459 struct wb_sids2xids_state
*state
= tevent_req_data(
460 req
, struct wb_sids2xids_state
);
461 struct tevent_req
*subreq
= NULL
;
462 struct dcerpc_binding_handle
*child_binding_handle
= NULL
;
463 const struct wbint_TransIDArray
*src
= NULL
;
464 struct wbint_TransIDArray
*dst
= NULL
;
468 state
->tried_dclookup
= false;
470 D_DEBUG("Processing next domain (dom_index=%"PRIu32
", idmap_doms.count=%"PRIu32
", lookup_count=%"PRIu32
").\n",
472 state
->idmap_doms
.count
,
473 state
->lookup_count
);
474 if (state
->dom_index
== state
->idmap_doms
.count
) {
475 if (state
->lookup_count
!= 0) {
477 * We already called wb_lookupsids_send()
478 * before, so we're done.
480 D_DEBUG("We already called wb_lookupsids_send() before, so we're done.\n");
481 tevent_req_done(req
);
485 for (si
=0; si
< state
->num_sids
; si
++) {
486 struct wbint_TransID
*t
= &state
->all_ids
.ids
[si
];
488 if (t
->domain_index
== UINT32_MAX
) {
489 /* ignore already filled entries */
493 state
->tmp_idx
[state
->lookup_count
] = si
;
494 sid_copy(&state
->lookup_sids
[state
->lookup_count
],
496 state
->lookup_count
+= 1;
499 D_DEBUG("Prepared %"PRIu32
" SID(s) for lookup wb_lookupsids_send().\n",
500 state
->lookup_count
);
501 if (state
->lookup_count
== 0) {
503 * no wb_lookupsids_send() needed...
505 tevent_req_done(req
);
509 subreq
= wb_lookupsids_send(state
,
512 state
->lookup_count
);
513 if (tevent_req_nomem(subreq
, req
)) {
516 tevent_req_set_callback(subreq
, wb_sids2xids_lookupsids_done
, req
);
520 src
= &state
->all_ids
;
521 dst
= &state
->map_ids_in
;
524 for (si
=0; si
< src
->num_ids
; si
++) {
525 if (src
->ids
[si
].domain_index
!= state
->dom_index
) {
529 state
->tmp_idx
[dst
->num_ids
] = si
;
530 dst
->ids
[dst
->num_ids
] = src
->ids
[si
];
531 dst
->ids
[dst
->num_ids
].domain_index
= 0;
535 if (dst
->num_ids
== 0) {
536 state
->dom_index
+= 1;
537 D_DEBUG("Go to next domain.\n");
541 state
->idmap_dom
= (struct lsa_RefDomainList
) {
543 .domains
= &state
->idmap_doms
.domains
[state
->dom_index
],
548 * dcerpc_wbint_Sids2UnixIDs_send/recv will
549 * allocate a new array for the response
550 * and overwrite _ids->ids pointer.
552 * So we better make a temporary copy
553 * of state->map_ids_in (which contains the request array)
554 * into state->map_ids_out.
556 * That makes it possible to reuse the pre-allocated
557 * state->map_ids_in.ids array.
559 state
->map_ids_out
= state
->map_ids_in
;
560 child_binding_handle
= idmap_child_handle();
561 subreq
= dcerpc_wbint_Sids2UnixIDs_send(
562 state
, state
->ev
, child_binding_handle
, &state
->idmap_dom
,
563 &state
->map_ids_out
);
564 if (tevent_req_nomem(subreq
, req
)) {
567 tevent_req_set_callback(subreq
, wb_sids2xids_done
, req
);
570 static enum id_type
lsa_SidType_to_id_type(const enum lsa_SidType sid_type
)
575 case SID_NAME_COMPUTER
:
579 case SID_NAME_DOM_GRP
:
581 case SID_NAME_WKN_GRP
:
585 type
= ID_TYPE_NOT_SPECIFIED
;
592 static void wb_sids2xids_done(struct tevent_req
*subreq
)
594 struct tevent_req
*req
= tevent_req_callback_data(
595 subreq
, struct tevent_req
);
596 struct wb_sids2xids_state
*state
= tevent_req_data(
597 req
, struct wb_sids2xids_state
);
598 NTSTATUS status
, result
;
599 const struct wbint_TransIDArray
*src
= NULL
;
600 struct wbint_TransIDArray
*dst
= NULL
;
603 status
= dcerpc_wbint_Sids2UnixIDs_recv(subreq
, state
, &result
);
606 if (tevent_req_nterror(req
, status
)) {
607 D_WARNING("Failed with %s.\n", nt_errstr(status
));
611 if (NT_STATUS_EQUAL(result
, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND
) &&
612 !state
->tried_dclookup
) {
614 struct lsa_DomainInfo
*d
;
616 D_DEBUG("Domain controller not found. Calling wb_dsgetdcname_send() to get it.\n");
617 d
= &state
->idmap_doms
.domains
[state
->dom_index
];
619 subreq
= wb_dsgetdcname_send(
620 state
, state
->ev
, d
->name
.string
, NULL
, NULL
,
622 if (tevent_req_nomem(subreq
, req
)) {
625 tevent_req_set_callback(subreq
, wb_sids2xids_gotdc
, req
);
629 src
= &state
->map_ids_out
;
630 dst
= &state
->all_ids
;
632 if (any_nt_status_not_ok(status
, result
, &status
)) {
633 D_DEBUG("Either status %s or result %s is not ok. Report SIDs as not mapped.\n",
637 * All we can do here is to report "not mapped"
639 src
= &state
->map_ids_in
;
640 for (si
=0; si
< src
->num_ids
; si
++) {
641 src
->ids
[si
].xid
.type
= ID_TYPE_NOT_SPECIFIED
;
645 if (src
->num_ids
!= state
->map_ids_in
.num_ids
) {
646 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
647 D_WARNING("Number of mapped SIDs does not match. Failing with NT_STATUS_INTERNAL_ERROR.\n");
651 for (si
=0; si
< src
->num_ids
; si
++) {
652 uint32_t di
= state
->tmp_idx
[si
];
654 if (src
->ids
[si
].xid
.type
== ID_TYPE_WB_REQUIRE_TYPE
) {
655 if (state
->lookup_count
== 0) {
656 D_DEBUG("The backend asks for more information (a type_hint), we'll do a lookupsids later.\n");
658 * The backend asks for more information
659 * (a type_hint), we'll do a lookupsids
666 * lookupsids was not able to provide a type_hint that
667 * satisfied the backend.
669 * Make sure we don't expose ID_TYPE_WB_REQUIRE_TYPE
670 * outside of winbindd!
672 D_DEBUG("lookupsids was not able to provide a type_hint that satisfied the backend. Make sure we don't expose ID_TYPE_WB_REQUIRE_TYPE outside of winbindd!\n");
673 src
->ids
[si
].xid
.type
= ID_TYPE_NOT_SPECIFIED
;
676 if (src
->ids
[si
].xid
.type
!= ID_TYPE_NOT_SPECIFIED
) {
677 dst
->ids
[di
].xid
= src
->ids
[si
].xid
;
678 D_DEBUG("%"PRIu32
": Setting XID %"PRIu32
"\n",
679 si
, src
->ids
[si
].xid
.id
);
681 dst
->ids
[di
].domain_index
= UINT32_MAX
; /* mark as valid */
682 idmap_cache_set_sid2unixid(&state
->sids
[di
], &dst
->ids
[di
].xid
);
685 state
->map_ids_in
.num_ids
= 0;
686 if (NT_STATUS_IS_OK(status
)) {
688 * If we got a valid response, we expect
689 * state->map_ids_out.ids to be a new allocated
690 * array, which we want to free early.
692 SMB_ASSERT(state
->map_ids_out
.ids
!= state
->map_ids_in
.ids
);
693 TALLOC_FREE(state
->map_ids_out
.ids
);
695 state
->map_ids_out
= (struct wbint_TransIDArray
) { .num_ids
= 0, };
697 state
->dom_index
+= 1;
699 wb_sids2xids_next_sids2unix(req
);
702 static void wb_sids2xids_gotdc(struct tevent_req
*subreq
)
704 struct tevent_req
*req
= tevent_req_callback_data(
705 subreq
, struct tevent_req
);
706 struct wb_sids2xids_state
*state
= tevent_req_data(
707 req
, struct wb_sids2xids_state
);
708 struct dcerpc_binding_handle
*child_binding_handle
= NULL
;
709 struct netr_DsRGetDCNameInfo
*dcinfo
;
712 status
= wb_dsgetdcname_recv(subreq
, state
, &dcinfo
);
714 if (tevent_req_nterror(req
, status
)) {
715 D_WARNING("Failed with %s.\n", nt_errstr(status
));
719 state
->tried_dclookup
= true;
722 struct lsa_DomainInfo
*d
=
723 &state
->idmap_doms
.domains
[state
->dom_index
];
724 const char *dom_name
= d
->name
.string
;
726 status
= wb_dsgetdcname_gencache_set(dom_name
, dcinfo
);
727 if (tevent_req_nterror(req
, status
)) {
728 D_WARNING("Failed with %s.\n", nt_errstr(status
));
734 * dcerpc_wbint_Sids2UnixIDs_send/recv will
735 * allocate a new array for the response
736 * and overwrite _ids->ids pointer.
738 * So we better make a temporary copy
739 * of state->map_ids_in (which contains the request array)
740 * into state->map_ids_out.
742 * That makes it possible to reuse the pre-allocated
743 * state->map_ids_in.ids array.
745 state
->map_ids_out
= state
->map_ids_in
;
746 child_binding_handle
= idmap_child_handle();
747 subreq
= dcerpc_wbint_Sids2UnixIDs_send(
748 state
, state
->ev
, child_binding_handle
, &state
->idmap_dom
,
749 &state
->map_ids_out
);
750 if (tevent_req_nomem(subreq
, req
)) {
753 tevent_req_set_callback(subreq
, wb_sids2xids_done
, req
);
756 NTSTATUS
wb_sids2xids_recv(struct tevent_req
*req
,
757 struct unixid xids
[], uint32_t num_xids
)
759 struct wb_sids2xids_state
*state
= tevent_req_data(
760 req
, struct wb_sids2xids_state
);
764 if (tevent_req_is_nterror(req
, &status
)) {
765 D_WARNING("Failed with %s.\n", nt_errstr(status
));
769 if (num_xids
!= state
->num_sids
) {
770 D_WARNING("Error. We have resolved only %"PRIu32
" XID(s), but caller asked for %"PRIu32
".\n",
771 state
->num_sids
, num_xids
);
772 return NT_STATUS_INTERNAL_ERROR
;
775 D_INFO("WB command sids2xids end.\n");
776 for (i
=0; i
<state
->num_sids
; i
++) {
777 struct dom_sid_buf buf
;
778 xids
[i
] = state
->all_ids
.ids
[i
].xid
;
779 D_INFO("%"PRIu32
": Found XID %"PRIu32
" for SID %s\n",
782 dom_sid_str_buf(&state
->sids
[i
], &buf
));