third_party/heimdal: Import lorikeet-heimdal-202407041740 (commit 42ba2a6e5dd1bc14a8b...
[Samba.git] / source3 / winbindd / winbindd_getgroups.c
blobc1c108e4155bec7b7581a7b899cc7d8d198848e8
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 (tevent_req_nterror(req, status)) {
113 return;
116 subreq = wb_gettoken_send(state, state->ev, &state->sid, true);
117 if (tevent_req_nomem(subreq, req)) {
118 return;
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);
129 NTSTATUS status;
131 status = wb_gettoken_recv(subreq, state, &state->num_sids,
132 &state->sids);
133 TALLOC_FREE(subreq);
134 if (tevent_req_nterror(req, status)) {
135 return;
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
142 * complete token.
145 subreq = wb_sids2xids_send(state, state->ev,
146 state->sids, state->num_sids);
147 if (tevent_req_nomem(subreq, req)) {
148 return;
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);
159 NTSTATUS status;
160 struct unixid *xids;
161 uint32_t i;
163 xids = talloc_array(state, struct unixid, state->num_sids);
164 if (tevent_req_nomem(xids, req)) {
165 return;
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);
173 TALLOC_FREE(subreq);
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)) {
180 return;
183 state->gids = talloc_array(state, gid_t, state->num_sids);
184 if (tevent_req_nomem(state->gids, req)) {
185 return;
187 state->num_gids = 0;
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";
196 break;
197 case ID_TYPE_UID:
198 if (i != 0) {
199 debug_missing = "uid";
201 break;
202 case ID_TYPE_GID:
203 case ID_TYPE_BOTH:
204 include_gid = true;
205 break;
206 case ID_TYPE_WB_REQUIRE_TYPE:
208 * these are internal between winbindd
209 * parent and child.
211 smb_panic(__location__);
212 break;
215 if (!include_gid) {
216 struct dom_sid_buf sidbuf;
218 if (debug_missing == NULL) {
219 continue;
222 D_WARNING("WARNING: skipping unix id (%"PRIu32") for sid %s "
223 "from group list because the idmap type "
224 "is %s. "
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),
229 debug_missing);
230 continue;
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)) {
243 return;
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);
254 NTSTATUS status;
255 uint32_t i;
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),
261 nt_errstr(status));
262 return status;
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,
278 &state->gids);
279 response->length += state->num_gids * sizeof(gid_t);
282 return NT_STATUS_OK;