2 Unix SMB/CIFS implementation.
3 async implementation of WINBINDD_GETGROUPS
4 Copyright (C) Volker Lendecke 2009
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 "passdb/lookup_sid.h" /* only for LOOKUP_NAME_NO_NSS flag */
23 #include "libcli/security/dom_sid.h"
25 struct winbindd_getgroups_state
{
26 struct tevent_context
*ev
;
31 enum lsa_SidType type
;
38 static void winbindd_getgroups_lookupname_done(struct tevent_req
*subreq
);
39 static void winbindd_getgroups_gettoken_done(struct tevent_req
*subreq
);
40 static void winbindd_getgroups_sid2gid_done(struct tevent_req
*subreq
);
42 struct tevent_req
*winbindd_getgroups_send(TALLOC_CTX
*mem_ctx
,
43 struct tevent_context
*ev
,
44 struct winbindd_cli_state
*cli
,
45 struct winbindd_request
*request
)
47 struct tevent_req
*req
, *subreq
;
48 struct winbindd_getgroups_state
*state
;
49 char *domuser
, *mapped_user
;
53 req
= tevent_req_create(mem_ctx
, &state
,
54 struct winbindd_getgroups_state
);
60 /* Ensure null termination */
61 request
->data
.username
[sizeof(request
->data
.username
)-1]='\0';
63 D_NOTICE("[%s (%u)] Winbind external command GETGROUPS start.\n"
64 "Searching groups for username '%s'.\n",
66 (unsigned int)cli
->pid
,
67 request
->data
.username
);
69 domuser
= request
->data
.username
;
71 status
= normalize_name_unmap(state
, domuser
, &mapped_user
);
73 if (NT_STATUS_IS_OK(status
)
74 || NT_STATUS_EQUAL(status
, NT_STATUS_FILE_RENAMED
)) {
75 /* normalize_name_unmapped did something */
76 domuser
= mapped_user
;
79 ok
= parse_domain_user(domuser
,
84 D_WARNING("Could not parse domain user: %s\n", domuser
);
85 tevent_req_nterror(req
, NT_STATUS_INVALID_PARAMETER
);
86 return tevent_req_post(req
, ev
);
89 subreq
= wb_lookupname_send(state
, ev
,
94 if (tevent_req_nomem(subreq
, req
)) {
95 return tevent_req_post(req
, ev
);
97 tevent_req_set_callback(subreq
, winbindd_getgroups_lookupname_done
,
102 static void winbindd_getgroups_lookupname_done(struct tevent_req
*subreq
)
104 struct tevent_req
*req
= tevent_req_callback_data(
105 subreq
, struct tevent_req
);
106 struct winbindd_getgroups_state
*state
= tevent_req_data(
107 req
, struct winbindd_getgroups_state
);
110 status
= wb_lookupname_recv(subreq
, &state
->sid
, &state
->type
);
112 if (tevent_req_nterror(req
, status
)) {
116 subreq
= wb_gettoken_send(state
, state
->ev
, &state
->sid
, true);
117 if (tevent_req_nomem(subreq
, req
)) {
120 tevent_req_set_callback(subreq
, winbindd_getgroups_gettoken_done
, req
);
123 static void winbindd_getgroups_gettoken_done(struct tevent_req
*subreq
)
125 struct tevent_req
*req
= tevent_req_callback_data(
126 subreq
, struct tevent_req
);
127 struct winbindd_getgroups_state
*state
= tevent_req_data(
128 req
, struct winbindd_getgroups_state
);
131 status
= wb_gettoken_recv(subreq
, state
, &state
->num_sids
,
134 if (tevent_req_nterror(req
, status
)) {
139 * Convert the group SIDs to gids. state->sids[0] contains the user
140 * sid. If the idmap backend uses ID_TYPE_BOTH, we might need the
141 * the id of the user sid in the list of group sids, so map the
145 subreq
= wb_sids2xids_send(state
, state
->ev
,
146 state
->sids
, state
->num_sids
);
147 if (tevent_req_nomem(subreq
, req
)) {
150 tevent_req_set_callback(subreq
, winbindd_getgroups_sid2gid_done
, req
);
153 static void winbindd_getgroups_sid2gid_done(struct tevent_req
*subreq
)
155 struct tevent_req
*req
= tevent_req_callback_data(
156 subreq
, struct tevent_req
);
157 struct winbindd_getgroups_state
*state
= tevent_req_data(
158 req
, struct winbindd_getgroups_state
);
163 xids
= talloc_array(state
, struct unixid
, state
->num_sids
);
164 if (tevent_req_nomem(xids
, req
)) {
167 for (i
=0; i
< state
->num_sids
; i
++) {
168 xids
[i
].type
= ID_TYPE_NOT_SPECIFIED
;
169 xids
[i
].id
= UINT32_MAX
;
172 status
= wb_sids2xids_recv(subreq
, xids
, state
->num_sids
);
174 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
) ||
175 NT_STATUS_EQUAL(status
, STATUS_SOME_UNMAPPED
))
177 status
= NT_STATUS_OK
;
179 if (tevent_req_nterror(req
, status
)) {
183 state
->gids
= talloc_array(state
, gid_t
, state
->num_sids
);
184 if (tevent_req_nomem(state
->gids
, req
)) {
189 for (i
=0; i
< state
->num_sids
; i
++) {
190 bool include_gid
= false;
191 const char *debug_missing
= NULL
;
193 switch (xids
[i
].type
) {
194 case ID_TYPE_NOT_SPECIFIED
:
195 debug_missing
= "not specified";
199 debug_missing
= "uid";
206 case ID_TYPE_WB_REQUIRE_TYPE
:
208 * these are internal between winbindd
211 smb_panic(__location__
);
216 struct dom_sid_buf sidbuf
;
218 if (debug_missing
== NULL
) {
222 D_WARNING("WARNING: skipping unix id (%"PRIu32
") for sid %s "
223 "from group list because the idmap type "
225 "This might be a security problem when ACLs "
226 "contain DENY ACEs!\n",
227 (unsigned)xids
[i
].id
,
228 dom_sid_str_buf(&state
->sids
[i
], &sidbuf
),
233 state
->gids
[state
->num_gids
] = (gid_t
)xids
[i
].id
;
234 state
->num_gids
+= 1;
238 * This should not fail, as it does not do any reallocation,
239 * just updating the talloc size.
241 state
->gids
= talloc_realloc(state
, state
->gids
, gid_t
, state
->num_gids
);
242 if (tevent_req_nomem(state
->gids
, req
)) {
246 tevent_req_done(req
);
249 NTSTATUS
winbindd_getgroups_recv(struct tevent_req
*req
,
250 struct winbindd_response
*response
)
252 struct winbindd_getgroups_state
*state
= tevent_req_data(
253 req
, struct winbindd_getgroups_state
);
257 if (tevent_req_is_nterror(req
, &status
)) {
258 struct dom_sid_buf buf
;
259 D_WARNING("Could not convert sid %s: %s\n",
260 dom_sid_str_buf(&state
->sid
, &buf
),
265 response
->data
.num_entries
= state
->num_gids
;
267 D_NOTICE("Winbind external command GETGROUPS end.\n"
268 "Received %"PRIu32
" entries.\n",
269 response
->data
.num_entries
);
270 if (CHECK_DEBUGLVL(DBGLVL_NOTICE
)) {
271 for (i
= 0; i
< state
->num_gids
; i
++) {
272 D_NOTICE("%"PRIu32
": GID %u\n", i
, state
->gids
[i
]);
276 if (state
->num_gids
> 0) {
277 response
->extra_data
.data
= talloc_move(response
,
279 response
->length
+= state
->num_gids
* sizeof(gid_t
);