ctdb-tests: Update statd-callout tests to handle both modes
[samba4-gss.git] / source3 / winbindd / winbindd_getgroups.c
blob5f901d62d5302c6f77e163d861a3440cf19b0c53
1 /*
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/>.
20 #include "includes.h"
21 #include "winbindd.h"
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;
27 char *namespace;
28 char *domname;
29 char *username;
30 struct dom_sid sid;
31 enum lsa_SidType type;
32 uint32_t num_sids;
33 struct dom_sid *sids;
34 uint32_t num_gids;
35 gid_t *gids;
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;
50 NTSTATUS status;
51 bool ok;
53 req = tevent_req_create(mem_ctx, &state,
54 struct winbindd_getgroups_state);
55 if (req == NULL) {
56 return NULL;
58 state->ev = ev;
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",
65 cli->client_name,
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,
80 &state->namespace,
81 &state->domname,
82 &state->username);
83 if (!ok) {
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,
90 state->namespace,
91 state->domname,
92 state->username,
93 LOOKUP_NAME_NO_NSS);
94 if (tevent_req_nomem(subreq, req)) {
95 return tevent_req_post(req, ev);
97 tevent_req_set_callback(subreq, winbindd_getgroups_lookupname_done,
98 req);
99 return req;
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);
108 NTSTATUS status;
110 status = wb_lookupname_recv(subreq, &state->sid, &state->type);
111 TALLOC_FREE(subreq);
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)) {
116 return;
119 subreq = wb_gettoken_send(state, state->ev, &state->sid, true);
120 if (tevent_req_nomem(subreq, req)) {
121 return;
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);
132 NTSTATUS status;
134 status = wb_gettoken_recv(subreq, state, &state->num_sids,
135 &state->sids);
136 TALLOC_FREE(subreq);
137 if (tevent_req_nterror(req, status)) {
138 return;
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
145 * complete token.
148 subreq = wb_sids2xids_send(state, state->ev,
149 state->sids, state->num_sids);
150 if (tevent_req_nomem(subreq, req)) {
151 return;
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);
162 NTSTATUS status;
163 struct unixid *xids;
164 uint32_t i;
166 xids = talloc_array(state, struct unixid, state->num_sids);
167 if (tevent_req_nomem(xids, req)) {
168 return;
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);
176 TALLOC_FREE(subreq);
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)) {
183 return;
186 state->gids = talloc_array(state, gid_t, state->num_sids);
187 if (tevent_req_nomem(state->gids, req)) {
188 return;
190 state->num_gids = 0;
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";
199 break;
200 case ID_TYPE_UID:
201 if (i != 0) {
202 debug_missing = "uid";
204 break;
205 case ID_TYPE_GID:
206 case ID_TYPE_BOTH:
207 include_gid = true;
208 break;
209 case ID_TYPE_WB_REQUIRE_TYPE:
211 * these are internal between winbindd
212 * parent and child.
214 smb_panic(__location__);
215 break;
218 if (!include_gid) {
219 struct dom_sid_buf sidbuf;
221 if (debug_missing == NULL) {
222 continue;
225 D_WARNING("WARNING: skipping unix id (%"PRIu32") for sid %s "
226 "from group list because the idmap type "
227 "is %s. "
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),
232 debug_missing);
233 continue;
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)) {
246 return;
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);
257 NTSTATUS status;
258 uint32_t i;
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),
264 nt_errstr(status));
265 return status;
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,
281 &state->gids);
282 response->length += state->num_gids * sizeof(gid_t);
285 return NT_STATUS_OK;