39a8c3556cff6a53e5eb5a25a1d348b8ed011fcb
[nivanova/samba-autobuild/.git] / source3 / winbindd / winbindd_getgroups.c
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         fstring namespace;
28         fstring domname;
29         fstring username;
30         struct dom_sid sid;
31         enum lsa_SidType type;
32         int num_sids;
33         struct dom_sid *sids;
34         int 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 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         struct tevent_req *req, *subreq;
48         struct winbindd_getgroups_state *state;
49         char *domuser, *mapped_user;
50         NTSTATUS status;
51         bool ok;
52
53         req = tevent_req_create(mem_ctx, &state,
54                                 struct winbindd_getgroups_state);
55         if (req == NULL) {
56                 return NULL;
57         }
58         state->ev = ev;
59
60         /* Ensure null termination */
61         request->data.username[sizeof(request->data.username)-1]='\0';
62
63         DBG_NOTICE("[%s (%u)] getgroups %s\n",
64                    cli->client_name,
65                    (unsigned int)cli->pid,
66                    request->data.username);
67
68         domuser = request->data.username;
69
70         status = normalize_name_unmap(state, domuser, &mapped_user);
71
72         if (NT_STATUS_IS_OK(status)
73             || NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
74                 /* normalize_name_unmapped did something */
75                 domuser = mapped_user;
76         }
77
78         ok = parse_domain_user(domuser,
79                                state->namespace,
80                                state->domname,
81                                state->username);
82         if (!ok) {
83                 DEBUG(5, ("Could not parse domain user: %s\n", domuser));
84                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
85                 return tevent_req_post(req, ev);
86         }
87
88         subreq = wb_lookupname_send(state, ev,
89                                     state->namespace,
90                                     state->domname,
91                                     state->username,
92                                     LOOKUP_NAME_NO_NSS);
93         if (tevent_req_nomem(subreq, req)) {
94                 return tevent_req_post(req, ev);
95         }
96         tevent_req_set_callback(subreq, winbindd_getgroups_lookupname_done,
97                                 req);
98         return req;
99 }
100
101 static void winbindd_getgroups_lookupname_done(struct tevent_req *subreq)
102 {
103         struct tevent_req *req = tevent_req_callback_data(
104                 subreq, struct tevent_req);
105         struct winbindd_getgroups_state *state = tevent_req_data(
106                 req, struct winbindd_getgroups_state);
107         NTSTATUS status;
108
109         status = wb_lookupname_recv(subreq, &state->sid, &state->type);
110         TALLOC_FREE(subreq);
111         if (tevent_req_nterror(req, status)) {
112                 return;
113         }
114
115         subreq = wb_gettoken_send(state, state->ev, &state->sid, true);
116         if (tevent_req_nomem(subreq, req)) {
117                 return;
118         }
119         tevent_req_set_callback(subreq, winbindd_getgroups_gettoken_done, req);
120 }
121
122 static void winbindd_getgroups_gettoken_done(struct tevent_req *subreq)
123 {
124         struct tevent_req *req = tevent_req_callback_data(
125                 subreq, struct tevent_req);
126         struct winbindd_getgroups_state *state = tevent_req_data(
127                 req, struct winbindd_getgroups_state);
128         NTSTATUS status;
129
130         status = wb_gettoken_recv(subreq, state, &state->num_sids,
131                                   &state->sids);
132         TALLOC_FREE(subreq);
133         if (tevent_req_nterror(req, status)) {
134                 return;
135         }
136
137         /*
138          * Convert the group SIDs to gids. state->sids[0] contains the user
139          * sid. If the idmap backend uses ID_TYPE_BOTH, we might need the
140          * the id of the user sid in the list of group sids, so map the
141          * complete token.
142          */
143
144         subreq = wb_sids2xids_send(state, state->ev,
145                                    state->sids, state->num_sids);
146         if (tevent_req_nomem(subreq, req)) {
147                 return;
148         }
149         tevent_req_set_callback(subreq, winbindd_getgroups_sid2gid_done, req);
150 }
151
152 static void winbindd_getgroups_sid2gid_done(struct tevent_req *subreq)
153 {
154         struct tevent_req *req = tevent_req_callback_data(
155                 subreq, struct tevent_req);
156         struct winbindd_getgroups_state *state = tevent_req_data(
157                 req, struct winbindd_getgroups_state);
158         NTSTATUS status;
159         struct unixid *xids;
160         int i;
161
162         xids = talloc_array(state, struct unixid, state->num_sids);
163         if (tevent_req_nomem(xids, req)) {
164                 return;
165         }
166         for (i=0; i < state->num_sids; i++) {
167                 xids[i].type = ID_TYPE_NOT_SPECIFIED;
168                 xids[i].id = UINT32_MAX;
169         }
170
171         status = wb_sids2xids_recv(subreq, xids, state->num_sids);
172         TALLOC_FREE(subreq);
173         if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED) ||
174             NT_STATUS_EQUAL(status, STATUS_SOME_UNMAPPED))
175         {
176                 status = NT_STATUS_OK;
177         }
178         if (tevent_req_nterror(req, status)) {
179                 return;
180         }
181
182         state->gids = talloc_array(state, gid_t, state->num_sids);
183         if (tevent_req_nomem(state->gids, req)) {
184                 return;
185         }
186         state->num_gids = 0;
187
188         for (i=0; i < state->num_sids; i++) {
189                 bool include_gid = false;
190                 const char *debug_missing = NULL;
191
192                 switch (xids[i].type) {
193                 case ID_TYPE_NOT_SPECIFIED:
194                         debug_missing = "not specified";
195                         break;
196                 case ID_TYPE_UID:
197                         if (i != 0) {
198                                 debug_missing = "uid";
199                         }
200                         break;
201                 case ID_TYPE_GID:
202                 case ID_TYPE_BOTH:
203                         include_gid = true;
204                         break;
205                 }
206
207                 if (!include_gid) {
208                         struct dom_sid_buf sidbuf;
209
210                         if (debug_missing == NULL) {
211                                 continue;
212                         }
213
214                         DEBUG(10, ("WARNING: skipping unix id (%u) for sid %s "
215                                    "from group list because the idmap type "
216                                    "is %s. "
217                                    "This might be a security problem when ACLs "
218                                    "contain DENY ACEs!\n",
219                                    (unsigned)xids[i].id,
220                                    dom_sid_str_buf(&state->sids[i], &sidbuf),
221                                    debug_missing));
222                         continue;
223                 }
224
225                 state->gids[state->num_gids] = (gid_t)xids[i].id;
226                 state->num_gids += 1;
227         }
228
229         /*
230          * This should not fail, as it does not do any reallocation,
231          * just updating the talloc size.
232          */
233         state->gids = talloc_realloc(state, state->gids, gid_t, state->num_gids);
234         if (tevent_req_nomem(state->gids, req)) {
235                 return;
236         }
237
238         tevent_req_done(req);
239 }
240
241 NTSTATUS winbindd_getgroups_recv(struct tevent_req *req,
242                                  struct winbindd_response *response)
243 {
244         struct winbindd_getgroups_state *state = tevent_req_data(
245                 req, struct winbindd_getgroups_state);
246         NTSTATUS status;
247
248         if (tevent_req_is_nterror(req, &status)) {
249                 DEBUG(5, ("Could not convert sid %s: %s\n",
250                           sid_string_dbg(&state->sid), nt_errstr(status)));
251                 return status;
252         }
253
254         response->data.num_entries = state->num_gids;
255
256         if (state->num_gids > 0) {
257                 response->extra_data.data = talloc_move(response,
258                                                         &state->gids);
259                 response->length += state->num_gids * sizeof(gid_t);
260         }
261         return NT_STATUS_OK;
262 }