eab5c26df4fc2e16f1f176c1f45241d170671743
[ira/wip.git] / source3 / winbindd / winbindd_group.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon for ntdom nss module
5
6    Copyright (C) Tim Potter 2000
7    Copyright (C) Jeremy Allison 2001.
8    Copyright (C) Gerald (Jerry) Carter 2003.
9    Copyright (C) Volker Lendecke 2005
10    
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 3 of the License, or
14    (at your option) any later version.
15    
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20    
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26 #include "winbindd.h"
27
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
30
31 /* Fill a grent structure from various other information */
32
33 bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
34                 const char *dom_name, const char *gr_name, gid_t unix_gid)
35 {
36         fstring full_group_name;
37         char *mapped_name = NULL;
38         struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
39         NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
40
41         nt_status = normalize_name_map(mem_ctx, domain, gr_name,
42                                        &mapped_name);
43
44         /* Basic whitespace replacement */
45         if (NT_STATUS_IS_OK(nt_status)) {
46                 fill_domain_username(full_group_name, dom_name,
47                                      mapped_name, true);
48         }
49         /* Mapped to an aliase */
50         else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
51                 fstrcpy(full_group_name, mapped_name);
52         }
53         /* no change */
54         else {
55                 fill_domain_username( full_group_name, dom_name,
56                                       gr_name, True );
57         }
58
59         gr->gr_gid = unix_gid;
60
61         /* Group name and password */
62
63         safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
64         safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
65
66         return True;
67 }
68
69 /* Get the list of domain groups and domain aliases for a domain.  We fill in
70    the sam_entries and num_sam_entries fields with domain group information.
71    Return True if some groups were returned, False otherwise. */
72
73 bool get_sam_group_entries(struct getent_state *ent)
74 {
75         NTSTATUS status;
76         uint32 num_entries;
77         struct acct_info *name_list = NULL;
78         TALLOC_CTX *mem_ctx;
79         bool result = False;
80         struct acct_info *sam_grp_entries = NULL;
81         struct winbindd_domain *domain;
82
83         if (ent->got_sam_entries)
84                 return False;
85
86         if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
87                                           ent->domain_name))) {
88                 DEBUG(1, ("get_sam_group_entries: "
89                           "could not create talloc context!\n"));
90                 return False;
91         }
92
93         /* Free any existing group info */
94
95         SAFE_FREE(ent->sam_entries);
96         ent->num_sam_entries = 0;
97         ent->got_sam_entries = True;
98
99         /* Enumerate domain groups */
100
101         num_entries = 0;
102
103         if (!(domain = find_domain_from_name(ent->domain_name))) {
104                 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
105                           ent->domain_name));
106                 goto done;
107         }
108
109         /* always get the domain global groups */
110
111         status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
112                                                   &sam_grp_entries);
113
114         if (!NT_STATUS_IS_OK(status)) {
115                 DEBUG(3, ("get_sam_group_entries: "
116                           "could not enumerate domain groups! Error: %s\n",
117                           nt_errstr(status)));
118                 result = False;
119                 goto done;
120         }
121
122         /* Copy entries into return buffer */
123
124         if (num_entries) {
125                 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
126                 if (!name_list) {
127                         DEBUG(0,("get_sam_group_entries: Failed to malloc "
128                                  "memory for %d domain groups!\n",
129                                  num_entries));
130                         result = False;
131                         goto done;
132                 }
133                 memcpy(name_list, sam_grp_entries,
134                         num_entries * sizeof(struct acct_info));
135         }
136
137         ent->num_sam_entries = num_entries;
138
139         /* get the domain local groups if we are a member of a native win2k
140          * domain and are not using LDAP to get the groups */
141
142         if ( ( lp_security() != SEC_ADS && domain->native_mode
143                 && domain->primary) || domain->internal )
144         {
145                 DEBUG(4,("get_sam_group_entries: %s domain; "
146                          "enumerating local groups as well\n",
147                          domain->native_mode ? "Native Mode 2k":
148                                                 "BUILTIN or local"));
149
150                 status = domain->methods->enum_local_groups(domain, mem_ctx,
151                                                             &num_entries,
152                                                             &sam_grp_entries);
153
154                 if ( !NT_STATUS_IS_OK(status) ) {
155                         DEBUG(3,("get_sam_group_entries: "
156                                 "Failed to enumerate "
157                                 "domain local groups with error %s!\n",
158                                 nt_errstr(status)));
159                         num_entries = 0;
160                 }
161                 else
162                         DEBUG(4,("get_sam_group_entries: "
163                                  "Returned %d local groups\n",
164                                  num_entries));
165
166                 /* Copy entries into return buffer */
167
168                 if ( num_entries ) {
169                         name_list = SMB_REALLOC_ARRAY(name_list,
170                                                       struct acct_info,
171                                                       ent->num_sam_entries+
172                                                         num_entries);
173                         if (!name_list) {
174                                 DEBUG(0,("get_sam_group_entries: "
175                                          "Failed to realloc more memory "
176                                          "for %d local groups!\n",
177                                          num_entries));
178                                 result = False;
179                                 goto done;
180                         }
181
182                         memcpy(&name_list[ent->num_sam_entries],
183                                 sam_grp_entries,
184                                 num_entries * sizeof(struct acct_info));
185                 }
186
187                 ent->num_sam_entries += num_entries;
188         }
189
190
191         /* Fill in remaining fields */
192
193         ent->sam_entries = name_list;
194         ent->sam_entry_index = 0;
195
196         result = (ent->num_sam_entries > 0);
197
198  done:
199         talloc_destroy(mem_ctx);
200
201         return result;
202 }
203
204 /* Get user supplementary groups.  This is much quicker than trying to
205    invert the groups database.  We merge the groups from the gids and
206    other_sids info3 fields as trusted domain, universal group
207    memberships, and nested groups (win2k native mode only) are not
208    returned by the getgroups RPC call but are present in the info3. */
209
210 struct getgroups_state {
211         struct winbindd_cli_state *state;
212         struct winbindd_domain *domain;
213         char *domname;
214         char *username;
215         DOM_SID user_sid;
216
217         const DOM_SID *token_sids;
218         size_t i, num_token_sids;
219
220         gid_t *token_gids;
221         size_t num_token_gids;
222 };
223
224 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
225                                                     struct winbindd_cli_state *state)
226 {
227         DOM_SID user_sid;
228         NTSTATUS status;
229
230         char *sidstring;
231         ssize_t len;
232         DOM_SID *groups;
233         uint32 num_groups;
234
235         /* Ensure null termination */
236         state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
237
238         if (!string_to_sid(&user_sid, state->request->data.sid)) {
239                 DEBUG(1, ("Could not get convert sid %s from string\n",
240                           state->request->data.sid));
241                 return WINBINDD_ERROR;
242         }
243
244         status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
245                                                     &user_sid, &num_groups,
246                                                     &groups);
247         if (!NT_STATUS_IS_OK(status))
248                 return WINBINDD_ERROR;
249
250         if (num_groups == 0) {
251                 state->response->data.num_entries = 0;
252                 state->response->extra_data.data = NULL;
253                 return WINBINDD_OK;
254         }
255
256         if (!print_sidlist(state->mem_ctx,
257                            groups, num_groups,
258                            &sidstring, &len)) {
259                 DEBUG(0, ("talloc failed\n"));
260                 return WINBINDD_ERROR;
261         }
262
263         state->response->extra_data.data = sidstring;
264         state->response->length += len+1;
265         state->response->data.num_entries = num_groups;
266
267         return WINBINDD_OK;
268 }
269
270 enum winbindd_result winbindd_dual_getsidaliases(struct winbindd_domain *domain,
271                                                  struct winbindd_cli_state *state)
272 {
273         DOM_SID *sids = NULL;
274         size_t num_sids = 0;
275         char *sidstr = NULL;
276         ssize_t len;
277         size_t i;
278         uint32 num_aliases;
279         uint32 *alias_rids;
280         NTSTATUS result;
281
282         DEBUG(3, ("[%5lu]: getsidaliases\n", (unsigned long)state->pid));
283
284         sidstr = state->request->extra_data.data;
285         if (sidstr == NULL) {
286                 sidstr = talloc_strdup(state->mem_ctx, "\n"); /* No SID */
287                 if (!sidstr) {
288                         DEBUG(0, ("Out of memory\n"));
289                         return WINBINDD_ERROR;
290                 }
291         }
292
293         DEBUG(10, ("Sidlist: %s\n", sidstr));
294
295         if (!parse_sidlist(state->mem_ctx, sidstr, &sids, &num_sids)) {
296                 DEBUG(0, ("Could not parse SID list: %s\n", sidstr));
297                 return WINBINDD_ERROR;
298         }
299
300         num_aliases = 0;
301         alias_rids = NULL;
302
303         result = domain->methods->lookup_useraliases(domain,
304                                                      state->mem_ctx,
305                                                      num_sids, sids,
306                                                      &num_aliases,
307                                                      &alias_rids);
308
309         if (!NT_STATUS_IS_OK(result)) {
310                 DEBUG(3, ("Could not lookup_useraliases: %s\n",
311                           nt_errstr(result)));
312                 return WINBINDD_ERROR;
313         }
314
315         num_sids = 0;
316         sids = NULL;
317         sidstr = NULL;
318
319         DEBUG(10, ("Got %d aliases\n", num_aliases));
320
321         for (i=0; i<num_aliases; i++) {
322                 DOM_SID sid;
323                 DEBUGADD(10, (" rid %d\n", alias_rids[i]));
324                 sid_copy(&sid, &domain->sid);
325                 sid_append_rid(&sid, alias_rids[i]);
326                 result = add_sid_to_array(state->mem_ctx, &sid, &sids,
327                                           &num_sids);
328                 if (!NT_STATUS_IS_OK(result)) {
329                         return WINBINDD_ERROR;
330                 }
331         }
332
333
334         if (!print_sidlist(state->mem_ctx, sids, num_sids, &sidstr, &len)) {
335                 DEBUG(0, ("Could not print_sidlist\n"));
336                 state->response->extra_data.data = NULL;
337                 return WINBINDD_ERROR;
338         }
339
340         state->response->extra_data.data = NULL;
341
342         if (sidstr) {
343                 state->response->extra_data.data = sidstr;
344                 DEBUG(10, ("aliases_list: %s\n",
345                            (char *)state->response->extra_data.data));
346                 state->response->length += len+1;
347                 state->response->data.num_entries = num_sids;
348         }
349
350         return WINBINDD_OK;
351 }
352
353 struct getgr_countmem {
354         int num;
355         size_t len;
356 };
357
358 static int getgr_calc_memberlen(DATA_BLOB key, void *data, void *priv)
359 {
360         struct wbint_Principal *m = talloc_get_type_abort(
361                 data, struct wbint_Principal);
362         struct getgr_countmem *buf = (struct getgr_countmem *)priv;
363
364         buf->num += 1;
365         buf->len += strlen(m->name) + 1;
366         return 0;
367 }
368
369 struct getgr_stringmem {
370         size_t ofs;
371         char *buf;
372 };
373
374 static int getgr_unparse_members(DATA_BLOB key, void *data, void *priv)
375 {
376         struct wbint_Principal *m = talloc_get_type_abort(
377                 data, struct wbint_Principal);
378         struct getgr_stringmem *buf = (struct getgr_stringmem *)priv;
379         int len;
380
381         len = strlen(m->name);
382
383         memcpy(buf->buf + buf->ofs, m->name, len);
384         buf->ofs += len;
385         buf->buf[buf->ofs] = ',';
386         buf->ofs += 1;
387         return 0;
388 }
389
390 NTSTATUS winbindd_print_groupmembers(struct talloc_dict *members,
391                                      TALLOC_CTX *mem_ctx,
392                                      int *num_members, char **result)
393 {
394         struct getgr_countmem c;
395         struct getgr_stringmem m;
396         int res;
397
398         c.num = 0;
399         c.len = 0;
400
401         res = talloc_dict_traverse(members, getgr_calc_memberlen, &c);
402         if (res != 0) {
403                 DEBUG(5, ("talloc_dict_traverse failed\n"));
404                 return NT_STATUS_INTERNAL_ERROR;
405         }
406
407         m.ofs = 0;
408         m.buf = talloc_array(mem_ctx, char, c.len);
409         if (m.buf == NULL) {
410                 DEBUG(5, ("talloc failed\n"));
411                 return NT_STATUS_NO_MEMORY;
412         }
413
414         res = talloc_dict_traverse(members, getgr_unparse_members, &m);
415         if (res != 0) {
416                 DEBUG(5, ("talloc_dict_traverse failed\n"));
417                 TALLOC_FREE(m.buf);
418                 return NT_STATUS_INTERNAL_ERROR;
419         }
420         m.buf[c.len-1] = '\0';
421
422         *num_members = c.num;
423         *result = m.buf;
424         return NT_STATUS_OK;
425 }