Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : async implementation of WINBINDD_GETGROUPS
4 : Copyright (C) Volker Lendecke 2009
5 :
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.
10 :
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.
15 :
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/>.
18 : */
19 :
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"
24 :
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;
36 : };
37 :
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);
41 :
42 1970 : 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)
46 : {
47 0 : struct tevent_req *req, *subreq;
48 0 : struct winbindd_getgroups_state *state;
49 0 : char *domuser, *mapped_user;
50 0 : NTSTATUS status;
51 0 : bool ok;
52 :
53 1970 : req = tevent_req_create(mem_ctx, &state,
54 : struct winbindd_getgroups_state);
55 1970 : if (req == NULL) {
56 0 : return NULL;
57 : }
58 1970 : state->ev = ev;
59 :
60 : /* Ensure null termination */
61 1970 : request->data.username[sizeof(request->data.username)-1]='\0';
62 :
63 1970 : 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);
68 :
69 1970 : domuser = request->data.username;
70 :
71 1970 : status = normalize_name_unmap(state, domuser, &mapped_user);
72 :
73 1970 : if (NT_STATUS_IS_OK(status)
74 1970 : || NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
75 : /* normalize_name_unmapped did something */
76 0 : domuser = mapped_user;
77 : }
78 :
79 1970 : ok = parse_domain_user(state, domuser,
80 1970 : &state->namespace,
81 1970 : &state->domname,
82 1970 : &state->username);
83 1970 : if (!ok) {
84 0 : D_WARNING("Could not parse domain user: %s\n", domuser);
85 0 : tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
86 0 : return tevent_req_post(req, ev);
87 : }
88 :
89 1970 : subreq = wb_lookupname_send(state, ev,
90 1970 : state->namespace,
91 1970 : state->domname,
92 1970 : state->username,
93 : LOOKUP_NAME_NO_NSS);
94 1970 : if (tevent_req_nomem(subreq, req)) {
95 0 : return tevent_req_post(req, ev);
96 : }
97 1970 : tevent_req_set_callback(subreq, winbindd_getgroups_lookupname_done,
98 : req);
99 1970 : return req;
100 : }
101 :
102 1970 : static void winbindd_getgroups_lookupname_done(struct tevent_req *subreq)
103 : {
104 1970 : struct tevent_req *req = tevent_req_callback_data(
105 : subreq, struct tevent_req);
106 1970 : struct winbindd_getgroups_state *state = tevent_req_data(
107 : req, struct winbindd_getgroups_state);
108 0 : NTSTATUS status;
109 :
110 1970 : status = wb_lookupname_recv(subreq, &state->sid, &state->type);
111 1970 : TALLOC_FREE(subreq);
112 1970 : if (tevent_req_nterror(req, status)) {
113 0 : return;
114 : }
115 :
116 1970 : subreq = wb_gettoken_send(state, state->ev, &state->sid, true);
117 1970 : if (tevent_req_nomem(subreq, req)) {
118 0 : return;
119 : }
120 1970 : tevent_req_set_callback(subreq, winbindd_getgroups_gettoken_done, req);
121 : }
122 :
123 1970 : static void winbindd_getgroups_gettoken_done(struct tevent_req *subreq)
124 : {
125 1970 : struct tevent_req *req = tevent_req_callback_data(
126 : subreq, struct tevent_req);
127 1970 : struct winbindd_getgroups_state *state = tevent_req_data(
128 : req, struct winbindd_getgroups_state);
129 0 : NTSTATUS status;
130 :
131 1970 : status = wb_gettoken_recv(subreq, state, &state->num_sids,
132 : &state->sids);
133 1970 : TALLOC_FREE(subreq);
134 1970 : if (tevent_req_nterror(req, status)) {
135 0 : return;
136 : }
137 :
138 : /*
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.
143 : */
144 :
145 1970 : subreq = wb_sids2xids_send(state, state->ev,
146 1970 : state->sids, state->num_sids);
147 1970 : if (tevent_req_nomem(subreq, req)) {
148 0 : return;
149 : }
150 1970 : tevent_req_set_callback(subreq, winbindd_getgroups_sid2gid_done, req);
151 : }
152 :
153 1970 : static void winbindd_getgroups_sid2gid_done(struct tevent_req *subreq)
154 : {
155 1970 : struct tevent_req *req = tevent_req_callback_data(
156 : subreq, struct tevent_req);
157 1970 : struct winbindd_getgroups_state *state = tevent_req_data(
158 : req, struct winbindd_getgroups_state);
159 0 : NTSTATUS status;
160 0 : struct unixid *xids;
161 0 : uint32_t i;
162 :
163 1970 : xids = talloc_array(state, struct unixid, state->num_sids);
164 1970 : if (tevent_req_nomem(xids, req)) {
165 0 : return;
166 : }
167 7041 : for (i=0; i < state->num_sids; i++) {
168 5071 : xids[i].type = ID_TYPE_NOT_SPECIFIED;
169 5071 : xids[i].id = UINT32_MAX;
170 : }
171 :
172 1970 : status = wb_sids2xids_recv(subreq, xids, state->num_sids);
173 1970 : TALLOC_FREE(subreq);
174 1970 : if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) ||
175 1970 : NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
176 : {
177 0 : status = NT_STATUS_OK;
178 : }
179 1970 : if (tevent_req_nterror(req, status)) {
180 0 : return;
181 : }
182 :
183 1970 : state->gids = talloc_array(state, gid_t, state->num_sids);
184 1970 : if (tevent_req_nomem(state->gids, req)) {
185 0 : return;
186 : }
187 1970 : state->num_gids = 0;
188 :
189 7041 : for (i=0; i < state->num_sids; i++) {
190 5071 : bool include_gid = false;
191 5071 : const char *debug_missing = NULL;
192 :
193 5071 : switch (xids[i].type) {
194 14 : case ID_TYPE_NOT_SPECIFIED:
195 14 : debug_missing = "not specified";
196 14 : break;
197 1970 : case ID_TYPE_UID:
198 1970 : if (i != 0) {
199 0 : debug_missing = "uid";
200 : }
201 1970 : break;
202 3087 : case ID_TYPE_GID:
203 : case ID_TYPE_BOTH:
204 3087 : include_gid = true;
205 3087 : break;
206 0 : case ID_TYPE_WB_REQUIRE_TYPE:
207 : /*
208 : * these are internal between winbindd
209 : * parent and child.
210 : */
211 0 : smb_panic(__location__);
212 0 : break;
213 : }
214 :
215 5071 : if (!include_gid) {
216 0 : struct dom_sid_buf sidbuf;
217 :
218 1984 : if (debug_missing == NULL) {
219 1970 : continue;
220 : }
221 :
222 14 : 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 14 : continue;
231 : }
232 :
233 3087 : state->gids[state->num_gids] = (gid_t)xids[i].id;
234 3087 : state->num_gids += 1;
235 : }
236 :
237 : /*
238 : * This should not fail, as it does not do any reallocation,
239 : * just updating the talloc size.
240 : */
241 1970 : state->gids = talloc_realloc(state, state->gids, gid_t, state->num_gids);
242 1970 : if (tevent_req_nomem(state->gids, req)) {
243 0 : return;
244 : }
245 :
246 1970 : tevent_req_done(req);
247 : }
248 :
249 1970 : NTSTATUS winbindd_getgroups_recv(struct tevent_req *req,
250 : struct winbindd_response *response)
251 : {
252 1970 : struct winbindd_getgroups_state *state = tevent_req_data(
253 : req, struct winbindd_getgroups_state);
254 0 : NTSTATUS status;
255 0 : uint32_t i;
256 :
257 1970 : if (tevent_req_is_nterror(req, &status)) {
258 0 : struct dom_sid_buf buf;
259 0 : D_WARNING("Could not convert sid %s: %s\n",
260 : dom_sid_str_buf(&state->sid, &buf),
261 : nt_errstr(status));
262 0 : return status;
263 : }
264 :
265 1970 : response->data.num_entries = state->num_gids;
266 :
267 1970 : D_NOTICE("Winbind external command GETGROUPS end.\n"
268 : "Received %"PRIu32" entries.\n",
269 : response->data.num_entries);
270 1970 : if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
271 0 : for (i = 0; i < state->num_gids; i++) {
272 0 : D_NOTICE("%"PRIu32": GID %u\n", i, state->gids[i]);
273 : }
274 : }
275 :
276 1970 : if (state->num_gids > 0) {
277 1970 : response->extra_data.data = talloc_move(response,
278 : &state->gids);
279 1970 : response->length += state->num_gids * sizeof(gid_t);
280 : }
281 :
282 1970 : return NT_STATUS_OK;
283 : }
|