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(state
, 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 (NT_STATUS_IS_OK(status
) && state
->type
== SID_NAME_UNKNOWN
) {
113 status
= NT_STATUS_NONE_MAPPED
;
115 if (tevent_req_nterror(req
, status
)) {
119 subreq
= wb_gettoken_send(state
, state
->ev
, &state
->sid
, true);
120 if (tevent_req_nomem(subreq
, req
)) {
123 tevent_req_set_callback(subreq
, winbindd_getgroups_gettoken_done
, req
);
126 static void winbindd_getgroups_gettoken_done(struct tevent_req
*subreq
)
128 struct tevent_req
*req
= tevent_req_callback_data(
129 subreq
, struct tevent_req
);
130 struct winbindd_getgroups_state
*state
= tevent_req_data(
131 req
, struct winbindd_getgroups_state
);
134 status
= wb_gettoken_recv(subreq
, state
, &state
->num_sids
,
137 if (tevent_req_nterror(req
, status
)) {
142 * Convert the group SIDs to gids. state->sids[0] contains the user
143 * sid. If the idmap backend uses ID_TYPE_BOTH, we might need the
144 * the id of the user sid in the list of group sids, so map the
148 subreq
= wb_sids2xids_send(state
, state
->ev
,
149 state
->sids
, state
->num_sids
);
150 if (tevent_req_nomem(subreq
, req
)) {
153 tevent_req_set_callback(subreq
, winbindd_getgroups_sid2gid_done
, req
);
156 static void winbindd_getgroups_sid2gid_done(struct tevent_req
*subreq
)
158 struct tevent_req
*req
= tevent_req_callback_data(
159 subreq
, struct tevent_req
);
160 struct winbindd_getgroups_state
*state
= tevent_req_data(
161 req
, struct winbindd_getgroups_state
);
166 xids
= talloc_array(state
, struct unixid
, state
->num_sids
);
167 if (tevent_req_nomem(xids
, req
)) {
170 for (i
=0; i
< state
->num_sids
; i
++) {
171 xids
[i
].type
= ID_TYPE_NOT_SPECIFIED
;
172 xids
[i
].id
= UINT32_MAX
;
175 status
= wb_sids2xids_recv(subreq
, xids
, state
->num_sids
);
177 if (NT_STATUS_EQUAL(status
, NT_STATUS_NONE_MAPPED
) ||
178 NT_STATUS_EQUAL(status
, STATUS_SOME_UNMAPPED
))
180 status
= NT_STATUS_OK
;
182 if (tevent_req_nterror(req
, status
)) {
186 state
->gids
= talloc_array(state
, gid_t
, state
->num_sids
);
187 if (tevent_req_nomem(state
->gids
, req
)) {
192 for (i
=0; i
< state
->num_sids
; i
++) {
193 bool include_gid
= false;
194 const char *debug_missing
= NULL
;
196 switch (xids
[i
].type
) {
197 case ID_TYPE_NOT_SPECIFIED
:
198 debug_missing
= "not specified";
202 debug_missing
= "uid";
209 case ID_TYPE_WB_REQUIRE_TYPE
:
211 * these are internal between winbindd
214 smb_panic(__location__
);
219 struct dom_sid_buf sidbuf
;
221 if (debug_missing
== NULL
) {
225 D_WARNING("WARNING: skipping unix id (%"PRIu32
") for sid %s "
226 "from group list because the idmap type "
228 "This might be a security problem when ACLs "
229 "contain DENY ACEs!\n",
230 (unsigned)xids
[i
].id
,
231 dom_sid_str_buf(&state
->sids
[i
], &sidbuf
),
236 state
->gids
[state
->num_gids
] = (gid_t
)xids
[i
].id
;
237 state
->num_gids
+= 1;
241 * This should not fail, as it does not do any reallocation,
242 * just updating the talloc size.
244 state
->gids
= talloc_realloc(state
, state
->gids
, gid_t
, state
->num_gids
);
245 if (tevent_req_nomem(state
->gids
, req
)) {
249 tevent_req_done(req
);
252 NTSTATUS
winbindd_getgroups_recv(struct tevent_req
*req
,
253 struct winbindd_response
*response
)
255 struct winbindd_getgroups_state
*state
= tevent_req_data(
256 req
, struct winbindd_getgroups_state
);
260 if (tevent_req_is_nterror(req
, &status
)) {
261 struct dom_sid_buf buf
;
262 D_WARNING("Could not convert sid %s: %s\n",
263 dom_sid_str_buf(&state
->sid
, &buf
),
268 response
->data
.num_entries
= state
->num_gids
;
270 D_NOTICE("Winbind external command GETGROUPS end.\n"
271 "Received %"PRIu32
" entries.\n",
272 response
->data
.num_entries
);
273 if (CHECK_DEBUGLVL(DBGLVL_NOTICE
)) {
274 for (i
= 0; i
< state
->num_gids
; i
++) {
275 D_NOTICE("%"PRIu32
": GID %u\n", i
, state
->gids
[i
]);
279 if (state
->num_gids
> 0) {
280 response
->extra_data
.data
= talloc_move(response
,
282 response
->length
+= state
->num_gids
* sizeof(gid_t
);