2 Unix SMB/CIFS implementation.
3 async implementation of WINBINDD_SIDS_TO_XIDS
4 Copyright (C) Volker Lendecke 2011
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/>.
22 #include "../libcli/security/security.h"
23 #include "librpc/gen_ndr/ndr_wbint_c.h"
24 #include "idmap_cache.h"
26 struct winbindd_sids_to_xids_state
{
27 struct tevent_context
*ev
;
31 struct id_map
*cached
;
33 struct dom_sid
*non_cached
;
34 uint32_t num_non_cached
;
36 struct lsa_RefDomainList
*domains
;
37 struct lsa_TransNameArray
*names
;
39 struct wbint_TransIDArray ids
;
42 static bool winbindd_sids_to_xids_in_cache(struct dom_sid
*sid
,
44 static void winbindd_sids_to_xids_lookupsids_done(struct tevent_req
*subreq
);
45 static void winbindd_sids_to_xids_done(struct tevent_req
*subreq
);
47 struct tevent_req
*winbindd_sids_to_xids_send(TALLOC_CTX
*mem_ctx
,
48 struct tevent_context
*ev
,
49 struct winbindd_cli_state
*cli
,
50 struct winbindd_request
*request
)
52 struct tevent_req
*req
, *subreq
;
53 struct winbindd_sids_to_xids_state
*state
;
56 req
= tevent_req_create(mem_ctx
, &state
,
57 struct winbindd_sids_to_xids_state
);
63 DEBUG(3, ("sids_to_xids\n"));
65 if (request
->extra_len
== 0) {
67 return tevent_req_post(req
, ev
);
69 if (request
->extra_data
.data
[request
->extra_len
-1] != '\0') {
70 DEBUG(10, ("Got invalid sids list\n"));
71 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
72 return tevent_req_post(req
, ev
);
74 if (!parse_sidlist(state
, request
->extra_data
.data
,
75 &state
->sids
, &state
->num_sids
)) {
76 DEBUG(10, ("parse_sidlist failed\n"));
77 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
78 return tevent_req_post(req
, ev
);
81 DEBUG(10, ("num_sids: %d\n", (int)state
->num_sids
));
83 state
->cached
= TALLOC_ZERO_ARRAY(state
, struct id_map
,
85 if (tevent_req_nomem(state
->cached
, req
)) {
86 return tevent_req_post(req
, ev
);
88 state
->non_cached
= TALLOC_ARRAY(state
, struct dom_sid
,
90 if (tevent_req_nomem(state
->non_cached
, req
)) {
91 return tevent_req_post(req
, ev
);
94 for (i
=0; i
<state
->num_sids
; i
++) {
96 DEBUG(10, ("SID %d: %s\n", (int)i
,
97 sid_string_dbg(&state
->sids
[i
])));
99 if (winbindd_sids_to_xids_in_cache(&state
->sids
[i
],
100 &state
->cached
[i
])) {
103 sid_copy(&state
->non_cached
[state
->num_non_cached
],
105 state
->num_non_cached
+= 1;
108 if (state
->num_non_cached
== 0) {
109 tevent_req_done(req
);
110 return tevent_req_post(req
, ev
);
113 subreq
= wb_lookupsids_send(state
, ev
, state
->non_cached
,
114 state
->num_non_cached
);
115 if (tevent_req_nomem(subreq
, req
)) {
116 return tevent_req_post(req
, ev
);
118 tevent_req_set_callback(subreq
, winbindd_sids_to_xids_lookupsids_done
,
123 static bool winbindd_sids_to_xids_in_cache(struct dom_sid
*sid
,
126 bool is_online
= is_domain_online(find_our_domain());
127 gid_t gid
= (gid_t
)-1;
128 bool gid_expired
= false;
129 bool gid_cached
= false;
130 bool gid_negative
= false;
131 uid_t uid
= (uid_t
)-1;
132 bool uid_expired
= false;
133 bool uid_cached
= false;
134 bool uid_negative
= false;
136 if (!winbindd_use_idmap_cache()) {
141 * SIDS_TO_XIDS is primarily used to resolve the user's group
142 * sids. So we check groups before users.
144 gid_cached
= idmap_cache_find_sid2gid(sid
, &gid
, &gid_expired
);
148 if (gid_cached
&& !gid_expired
) {
149 if (gid
!= (gid_t
)-1) {
152 map
->xid
.type
= ID_TYPE_GID
;
153 map
->status
= ID_MAPPED
;
158 uid_cached
= idmap_cache_find_sid2uid(sid
, &uid
, &uid_expired
);
162 if (uid_cached
&& !uid_expired
) {
163 if (uid
!= (uid_t
)-1) {
166 map
->xid
.type
= ID_TYPE_UID
;
167 map
->status
= ID_MAPPED
;
174 * Here we know that we only have negative
177 * All valid cases already returned to the caller.
180 if (gid_negative
&& uid_negative
) {
182 map
->xid
.id
= UINT32_MAX
;
183 map
->xid
.type
= ID_TYPE_NOT_SPECIFIED
;
184 map
->status
= ID_MAPPED
;
190 map
->xid
.id
= gid
; /* this is (gid_t)-1 */
191 map
->xid
.type
= ID_TYPE_GID
;
192 map
->status
= ID_MAPPED
;
198 map
->xid
.id
= uid
; /* this is (uid_t)-1 */
199 map
->xid
.type
= ID_TYPE_UID
;
200 map
->status
= ID_MAPPED
;
207 static void winbindd_sids_to_xids_lookupsids_done(struct tevent_req
*subreq
)
209 struct tevent_req
*req
= tevent_req_callback_data(
210 subreq
, struct tevent_req
);
211 struct winbindd_sids_to_xids_state
*state
= tevent_req_data(
212 req
, struct winbindd_sids_to_xids_state
);
213 struct winbindd_child
*child
;
217 status
= wb_lookupsids_recv(subreq
, state
, &state
->domains
,
220 if (tevent_req_nterror(req
, status
)) {
224 state
->ids
.num_ids
= state
->num_non_cached
;
225 state
->ids
.ids
= TALLOC_ARRAY(state
, struct wbint_TransID
,
226 state
->num_non_cached
);
227 if (tevent_req_nomem(state
->ids
.ids
, req
)) {
231 for (i
=0; i
<state
->num_non_cached
; i
++) {
232 struct lsa_TranslatedName
*n
= &state
->names
->names
[i
];
233 struct wbint_TransID
*t
= &state
->ids
.ids
[i
];
235 switch (n
->sid_type
) {
237 case SID_NAME_COMPUTER
:
238 t
->type
= WBC_ID_TYPE_UID
;
240 case SID_NAME_DOM_GRP
:
242 case SID_NAME_WKN_GRP
:
243 t
->type
= WBC_ID_TYPE_GID
;
246 t
->type
= WBC_ID_TYPE_NOT_SPECIFIED
;
249 t
->domain_index
= n
->sid_index
;
250 sid_peek_rid(&state
->non_cached
[i
], &t
->rid
);
251 t
->unix_id
= (uint64_t)-1;
254 child
= idmap_child();
256 subreq
= dcerpc_wbint_Sids2UnixIDs_send(
257 state
, state
->ev
, child
->binding_handle
, state
->domains
,
259 if (tevent_req_nomem(subreq
, req
)) {
262 tevent_req_set_callback(subreq
, winbindd_sids_to_xids_done
, req
);
265 static void winbindd_sids_to_xids_done(struct tevent_req
*subreq
)
267 struct tevent_req
*req
= tevent_req_callback_data(
268 subreq
, struct tevent_req
);
269 struct winbindd_sids_to_xids_state
*state
= tevent_req_data(
270 req
, struct winbindd_sids_to_xids_state
);
271 NTSTATUS status
, result
;
273 status
= dcerpc_wbint_Sids2UnixIDs_recv(subreq
, state
, &result
);
275 if (any_nt_status_not_ok(status
, result
, &status
)) {
276 tevent_req_nterror(req
, status
);
279 tevent_req_done(req
);
282 NTSTATUS
winbindd_sids_to_xids_recv(struct tevent_req
*req
,
283 struct winbindd_response
*response
)
285 struct winbindd_sids_to_xids_state
*state
= tevent_req_data(
286 req
, struct winbindd_sids_to_xids_state
);
289 uint32_t i
, num_non_cached
;
291 if (tevent_req_is_nterror(req
, &status
)) {
292 DEBUG(5, ("wb_sids_to_xids failed: %s\n", nt_errstr(status
)));
296 result
= talloc_strdup(response
, "");
297 if (result
== NULL
) {
298 return NT_STATUS_NO_MEMORY
;
303 for (i
=0; i
<state
->num_sids
; i
++) {
305 uint32_t unix_id
= UINT32_MAX
;
308 if (state
->cached
[i
].sid
!= NULL
) {
309 unix_id
= state
->cached
[i
].xid
.id
;
311 switch (state
->cached
[i
].xid
.type
) {
323 unix_id
= state
->ids
.ids
[num_non_cached
].unix_id
;
325 switch(state
->ids
.ids
[num_non_cached
].type
) {
326 case WBC_ID_TYPE_UID
:
328 idmap_cache_set_sid2uid(
329 &state
->non_cached
[num_non_cached
],
332 case WBC_ID_TYPE_GID
:
334 idmap_cache_set_sid2gid(
335 &state
->non_cached
[num_non_cached
],
345 if (unix_id
== UINT32_MAX
) {
350 result
= talloc_asprintf_append_buffer(
351 result
, "%c%lu\n", type
,
352 (unsigned long)unix_id
);
354 result
= talloc_asprintf_append_buffer(result
, "\n");
356 if (result
== NULL
) {
357 return NT_STATUS_NO_MEMORY
;
361 response
->extra_data
.data
= result
;
362 response
->length
+= talloc_get_size(result
);