s3:winbind: unmapped Unix groups in getgrgid must be resolved locally
[samba.git] / source3 / winbindd / wb_getgrsid.c
1 /*
2    Unix SMB/CIFS implementation.
3    async getgrsid
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 "librpc/gen_ndr/ndr_winbind_c.h"
23 #include "../libcli/security/security.h"
24
25 struct wb_getgrsid_state {
26         struct tevent_context *ev;
27         struct dom_sid sid;
28         int max_nesting;
29         const char *domname;
30         const char *name;
31         enum lsa_SidType type;
32         gid_t gid;
33         struct talloc_dict *members;
34 };
35
36 static void wb_getgrsid_lookupsid_done(struct tevent_req *subreq);
37 static void wb_getgrsid_sid2gid_done(struct tevent_req *subreq);
38 static void wb_getgrsid_got_members(struct tevent_req *subreq);
39
40 struct tevent_req *wb_getgrsid_send(TALLOC_CTX *mem_ctx,
41                                     struct tevent_context *ev,
42                                     const struct dom_sid *group_sid,
43                                     int max_nesting)
44 {
45         struct tevent_req *req, *subreq;
46         struct wb_getgrsid_state *state;
47
48         req = tevent_req_create(mem_ctx, &state, struct wb_getgrsid_state);
49         if (req == NULL) {
50                 return NULL;
51         }
52         sid_copy(&state->sid, group_sid);
53         state->ev = ev;
54         state->max_nesting = max_nesting;
55
56         if (dom_sid_in_domain(&global_sid_Unix_Groups, group_sid)) {
57                 /* unmapped Unix groups must be resolved locally */
58                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
59                 return tevent_req_post(req, ev);
60         }
61
62         if (lp_winbind_trusted_domains_only()) {
63                 struct winbindd_domain *our_domain = find_our_domain();
64
65                 if (dom_sid_compare_domain(group_sid, &our_domain->sid) == 0) {
66                         DEBUG(7, ("winbindd_getgrsid: My domain -- rejecting "
67                                   "getgrsid() for %s\n", sid_string_tos(group_sid)));
68                         tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
69                         return tevent_req_post(req, ev);
70                 }
71         }
72
73         subreq = wb_lookupsid_send(state, ev, &state->sid);
74         if (tevent_req_nomem(subreq, req)) {
75                 return tevent_req_post(req, ev);
76         }
77         tevent_req_set_callback(subreq, wb_getgrsid_lookupsid_done, req);
78         return req;
79 }
80
81 static void wb_getgrsid_lookupsid_done(struct tevent_req *subreq)
82 {
83         struct tevent_req *req = tevent_req_callback_data(
84                 subreq, struct tevent_req);
85         struct wb_getgrsid_state *state = tevent_req_data(
86                 req, struct wb_getgrsid_state);
87         NTSTATUS status;
88
89         status = wb_lookupsid_recv(subreq, state, &state->type,
90                                    &state->domname, &state->name);
91         TALLOC_FREE(subreq);
92         if (tevent_req_nterror(req, status)) {
93                 return;
94         }
95
96         switch (state->type) {
97         case SID_NAME_DOM_GRP:
98         case SID_NAME_ALIAS:
99         case SID_NAME_WKN_GRP:
100         /*
101          * also treat user-type SIDS (they might map to ID_TYPE_BOTH)
102          */
103         case SID_NAME_USER:
104         case SID_NAME_COMPUTER:
105                 break;
106         default:
107                 tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
108                 return;
109         }
110
111         subreq = wb_sids2xids_send(state, state->ev, &state->sid, 1);
112         if (tevent_req_nomem(subreq, req)) {
113                 return;
114         }
115         tevent_req_set_callback(subreq, wb_getgrsid_sid2gid_done, req);
116 }
117
118 static void wb_getgrsid_sid2gid_done(struct tevent_req *subreq)
119 {
120         struct tevent_req *req = tevent_req_callback_data(
121                 subreq, struct tevent_req);
122         struct wb_getgrsid_state *state = tevent_req_data(
123                 req, struct wb_getgrsid_state);
124         NTSTATUS status;
125         struct unixid xids[1];
126
127         status = wb_sids2xids_recv(subreq, xids, ARRAY_SIZE(xids));
128         TALLOC_FREE(subreq);
129         if (tevent_req_nterror(req, status)) {
130                 return;
131         }
132
133         /*
134          * We are filtering further down in sids2xids, but that filtering
135          * depends on the actual type of the sid handed in (as determined
136          * by lookupsids). Here we need to filter for the type of object
137          * actually requested, in this case uid.
138          */
139         if (!(xids[0].type == ID_TYPE_GID || xids[0].type == ID_TYPE_BOTH)) {
140                 tevent_req_nterror(req, NT_STATUS_NONE_MAPPED);
141                 return;
142         }
143
144         state->gid = (gid_t)xids[0].id;
145
146         if (state->type == SID_NAME_USER || state->type == SID_NAME_COMPUTER) {
147                 /*
148                  * special treatment for a user sid that is
149                  * mapped to ID_TYPE_BOTH:
150                  * create a group with the sid/xid as only member
151                  */
152                 const char *name;
153
154                 if (xids[0].type != ID_TYPE_BOTH) {
155                         tevent_req_nterror(req, NT_STATUS_NO_SUCH_GROUP);
156                         return;
157                 }
158
159                 state->members = talloc_dict_init(state);
160                 if (tevent_req_nomem(state->members, req)) {
161                         return;
162                 }
163
164                 name = fill_domain_username_talloc(talloc_tos(),
165                                                    state->domname,
166                                                    state->name,
167                                                    true /* can_assume */);
168                 if (tevent_req_nomem(name, req)) {
169                         return;
170                 }
171
172                 status = add_wbint_Principal_to_dict(talloc_tos(),
173                                                      &state->sid,
174                                                      &name,
175                                                      state->type,
176                                                      state->members);
177                 if (!NT_STATUS_IS_OK(status)) {
178                         tevent_req_nterror(req, status);
179                         return;
180                 }
181
182                 tevent_req_done(req);
183                 return;
184         }
185
186         /*
187          * the "regular" case of a group type sid.
188          */
189
190         subreq = wb_group_members_send(state, state->ev, &state->sid,
191                                        state->type, state->max_nesting);
192         if (tevent_req_nomem(subreq, req)) {
193                 return;
194         }
195         tevent_req_set_callback(subreq, wb_getgrsid_got_members, req);
196 }
197
198 static void wb_getgrsid_got_members(struct tevent_req *subreq)
199 {
200         struct tevent_req *req = tevent_req_callback_data(
201                 subreq, struct tevent_req);
202         struct wb_getgrsid_state *state = tevent_req_data(
203                 req, struct wb_getgrsid_state);
204         NTSTATUS status;
205
206         status = wb_group_members_recv(subreq, state, &state->members);
207         TALLOC_FREE(subreq);
208         if (tevent_req_nterror(req, status)) {
209                 return;
210         }
211         tevent_req_done(req);
212 }
213
214 NTSTATUS wb_getgrsid_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
215                           const char **domname, const char **name, gid_t *gid,
216                           struct talloc_dict **members)
217 {
218         struct wb_getgrsid_state *state = tevent_req_data(
219                 req, struct wb_getgrsid_state);
220         NTSTATUS status;
221
222         if (tevent_req_is_nterror(req, &status)) {
223                 return status;
224         }
225         *domname = talloc_move(mem_ctx, &state->domname);
226         *name = talloc_move(mem_ctx, &state->name);
227         *gid = state->gid;
228         *members = talloc_move(mem_ctx, &state->members);
229         return NT_STATUS_OK;
230 }