r19784: smbd compiles (still a few warning which are actual bugs)
[Samba.git] / source / nsswitch / winbindd_group.c
blobccce7302a5a2e6051f2dac075bae548d0ff626d0
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
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
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 2 of the License, or
14 (at your option) any later version.
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.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "includes.h"
27 #include "winbindd.h"
29 extern BOOL opt_nocache;
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_WINBIND
34 /***************************************************************
35 Empty static struct for negative caching.
36 ****************************************************************/
38 /* Fill a grent structure from various other information */
40 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
41 const char *gr_name, gid_t unix_gid)
43 fstring full_group_name;
45 fill_domain_username( full_group_name, dom_name, gr_name, True );
47 gr->gr_gid = unix_gid;
49 /* Group name and password */
51 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
52 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
54 return True;
57 /* Fill in the group membership field of a NT group given by group_sid */
59 static BOOL fill_grent_mem(struct winbindd_domain *domain,
60 struct winbindd_cli_state *state,
61 DOM_SID *group_sid,
62 enum SID_NAME_USE group_name_type,
63 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
65 DOM_SID *sid_mem = NULL;
66 uint32 num_names = 0;
67 uint32 *name_types = NULL;
68 unsigned int buf_len = 0, buf_ndx = 0, i;
69 char **names = NULL, *buf = NULL;
70 BOOL result = False;
71 TALLOC_CTX *mem_ctx;
72 NTSTATUS status;
73 uint32 group_rid;
74 fstring sid_string;
76 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
77 return False;
79 /* Initialise group membership information */
81 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
83 *num_gr_mem = 0;
85 /* HACK ALERT!! This whole routine does not cope with group members
86 * from more than one domain, ie aliases. Thus we have to work it out
87 * ourselves in a special routine. */
89 if (domain->internal)
90 return fill_passdb_alias_grmem(domain, group_sid,
91 num_gr_mem,
92 gr_mem, gr_mem_len);
94 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
95 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
97 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
98 sid_to_string(sid_string, group_sid), domain->name,
99 group_name_type));
100 goto done;
103 /* OPTIMIZATION / HACK. */
104 /* If "enum users" is set to false, and the group being looked
105 up is the Domain Users SID: S-1-5-domain-513, then for the
106 list of members check if the querying user is in that group,
107 and if so only return that user as the gr_mem array.
108 We can change this to a different parameter than "enum users"
109 if neccessaey, or parameterize the group list we do this for. */
111 sid_peek_rid( group_sid, &group_rid );
112 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
113 DOM_SID querying_user_sid;
114 DOM_SID *pquerying_user_sid = NULL;
115 uint32 num_groups = 0;
116 DOM_SID *user_sids = NULL;
117 BOOL u_in_group = False;
119 DEBUG(10,("fill_grent_mem: optimized lookup for sid %s domain %s\n",
120 sid_to_string(sid_string, group_sid), domain->name ));
122 if (state) {
123 uid_t ret_uid = (uid_t)-1;
124 if (sys_getpeereid(state->sock, &ret_uid)==0) {
125 /* We know who's asking - look up their SID if
126 it's one we've mapped before. */
127 status = idmap_uid_to_sid(&querying_user_sid,
128 ret_uid,
129 ID_QUERY_ONLY|ID_CACHE_ONLY);
130 if (NT_STATUS_IS_OK(status)) {
131 pquerying_user_sid = &querying_user_sid;
132 DEBUG(10,("fill_grent_mem: querying uid %u -> %s\n",
133 (unsigned int)ret_uid,
134 sid_to_string(sid_string, pquerying_user_sid) ));
139 /* Only look up if it was a winbindd user in this domain. */
140 if (pquerying_user_sid &&
141 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
143 DEBUG(10,("fill_grent_mem: querying user = %s\n",
144 sid_to_string(sid_string, pquerying_user_sid) ));
146 status = domain->methods->lookup_usergroups(domain,
147 mem_ctx,
148 pquerying_user_sid,
149 &num_groups,
150 &user_sids);
151 if (!NT_STATUS_IS_OK(status)) {
152 DEBUG(1, ("fill_grent_mem: lookup_usergroups failed "
153 "for sid %s in domain %s (error: %s)\n",
154 sid_to_string(sid_string, pquerying_user_sid),
155 domain->name,
156 nt_errstr(status)));
157 goto done;
160 for (i = 0; i < num_groups; i++) {
161 if (sid_equal(group_sid, &user_sids[i])) {
162 /* User is in Domain Users, add their name
163 as the only group member. */
164 u_in_group = True;
165 break;
170 if (u_in_group) {
171 size_t len = 0;
172 char *domainname = NULL;
173 char *username = NULL;
174 fstring name;
175 enum SID_NAME_USE type;
177 DEBUG(10,("fill_grent_mem: sid %s in 'Domain Users' in domain %s\n",
178 sid_to_string(sid_string, pquerying_user_sid), domain->name ));
180 status = domain->methods->sid_to_name(domain, mem_ctx,
181 pquerying_user_sid,
182 &domainname,
183 &username,
184 &type);
185 if (!NT_STATUS_IS_OK(status)) {
186 DEBUG(1, ("could not lookup username for user "
187 "sid %s in domain %s (error: %s)\n",
188 sid_to_string(sid_string, pquerying_user_sid),
189 domain->name,
190 nt_errstr(status)));
191 goto done;
193 fill_domain_username(name, domain->name, username, True);
194 len = strlen(name);
195 buf_len = len + 1;
196 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
197 DEBUG(1, ("out of memory\n"));
198 goto done;
200 memcpy(buf, name, buf_len);
202 DEBUG(10,("fill_grent_mem: user %s in 'Domain Users' in domain %s\n",
203 name, domain->name ));
206 *gr_mem = buf;
207 *gr_mem_len = buf_len;
208 *num_gr_mem = 1;
210 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
211 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
212 result = True;
213 goto done;
216 /* Lookup group members */
217 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
218 &sid_mem, &names, &name_types);
219 if (!NT_STATUS_IS_OK(status)) {
220 DEBUG(1, ("could not lookup membership for group sid %s in domain %s (error: %s)\n",
221 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
222 goto done;
225 DEBUG(10, ("looked up %d names\n", num_names));
227 if (DEBUGLEVEL >= 10) {
228 for (i = 0; i < num_names; i++)
229 DEBUG(10, ("\t%20s %s %d\n", names[i],
230 sid_string_static(&sid_mem[i]),
231 name_types[i]));
234 /* Add members to list */
236 again:
238 for (i = 0; i < num_names; i++) {
239 char *the_name;
240 fstring name;
241 int len;
243 the_name = names[i];
245 DEBUG(10, ("processing name %s\n", the_name));
247 /* FIXME: need to cope with groups within groups. These
248 occur in Universal groups on a Windows 2000 native mode
249 server. */
251 /* make sure to allow machine accounts */
253 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
254 DEBUG(3, ("name %s isn't a domain user (%s)\n", the_name, sid_type_lookup(name_types[i])));
255 continue;
258 /* Append domain name */
260 fill_domain_username(name, domain->name, the_name, True);
262 len = strlen(name);
264 /* Add to list or calculate buffer length */
266 if (!buf) {
267 buf_len += len + 1; /* List is comma separated */
268 (*num_gr_mem)++;
269 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
270 } else {
271 DEBUG(10, ("appending %s at ndx %d\n", name, buf_ndx));
272 safe_strcpy(&buf[buf_ndx], name, len);
273 buf_ndx += len;
274 buf[buf_ndx] = ',';
275 buf_ndx++;
279 /* Allocate buffer */
281 if (!buf && buf_len != 0) {
282 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
283 DEBUG(1, ("out of memory\n"));
284 result = False;
285 goto done;
287 memset(buf, 0, buf_len);
288 goto again;
291 if (buf && buf_ndx > 0) {
292 buf[buf_ndx - 1] = '\0';
295 *gr_mem = buf;
296 *gr_mem_len = buf_len;
298 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
299 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
300 result = True;
302 done:
304 talloc_destroy(mem_ctx);
306 DEBUG(10, ("fill_grent_mem returning %d\n", result));
308 return result;
311 /* Return a group structure from a group name */
313 void winbindd_getgrnam(struct winbindd_cli_state *state)
315 DOM_SID group_sid, tmp_sid;
316 uint32 grp_rid;
317 struct winbindd_domain *domain;
318 enum SID_NAME_USE name_type;
319 fstring name_domain, name_group;
320 char *tmp, *gr_mem;
321 size_t gr_mem_len;
322 size_t num_gr_mem;
323 gid_t gid;
324 union unid_t id;
325 NTSTATUS status;
327 /* Ensure null termination */
328 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
330 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
331 state->request.data.groupname));
333 /* Parse domain and groupname */
335 memset(name_group, 0, sizeof(fstring));
337 tmp = state->request.data.groupname;
339 parse_domain_user(tmp, name_domain, name_group);
341 /* if no domain or our local domain and no local tdb group, default to
342 * our local domain for aliases */
344 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
345 fstrcpy(name_domain, get_global_sam_name());
348 /* Get info for the domain */
350 if ((domain = find_domain_from_name(name_domain)) == NULL) {
351 DEBUG(3, ("could not get domain sid for domain %s\n",
352 name_domain));
353 request_error(state);
354 return;
356 /* should we deal with users for our domain? */
358 if ( lp_winbind_trusted_domains_only() && domain->primary) {
359 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
360 "getgrnam() for %s\\%s.\n", name_domain, name_group));
361 request_error(state);
362 return;
365 /* Get rid and name type from name */
367 if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
368 name_group, &group_sid, &name_type)) {
369 DEBUG(1, ("group %s in domain %s does not exist\n",
370 name_group, name_domain));
371 request_error(state);
372 return;
375 if ( !((name_type==SID_NAME_DOM_GRP) ||
376 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
377 ((name_type==SID_NAME_ALIAS) && domain->internal) ||
378 ((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
380 DEBUG(1, ("name '%s' is not a local, domain or builtin "
381 "group: %d\n", name_group, name_type));
382 request_error(state);
383 return;
386 /* Make sure that the group SID is within the domain of the
387 original domain */
389 sid_copy( &tmp_sid, &group_sid );
390 sid_split_rid( &tmp_sid, &grp_rid );
391 if ( !sid_equal( &tmp_sid, &domain->sid ) ) {
392 DEBUG(3,("winbindd_getgrnam: group %s resolves to a SID in the wrong domain [%s]\n",
393 state->request.data.groupname, sid_string_static(&group_sid)));
394 request_error(state);
395 return;
400 /* Try to get the GID */
402 status = idmap_sid_to_gid(&group_sid, &gid, 0);
404 if (NT_STATUS_IS_OK(status)) {
405 goto got_gid;
408 /* Maybe it's one of our aliases in passdb */
410 if (pdb_sid_to_id(&group_sid, &id, &name_type) &&
411 ((name_type == SID_NAME_ALIAS) ||
412 (name_type == SID_NAME_WKN_GRP))) {
413 gid = id.gid;
414 goto got_gid;
417 DEBUG(1, ("error converting unix gid to sid\n"));
418 request_error(state);
419 return;
421 got_gid:
423 if (!fill_grent(&state->response.data.gr, name_domain,
424 name_group, gid) ||
425 !fill_grent_mem(domain, state, &group_sid, name_type,
426 &num_gr_mem,
427 &gr_mem, &gr_mem_len)) {
428 request_error(state);
429 return;
432 state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
434 /* Group membership lives at start of extra data */
436 state->response.data.gr.gr_mem_ofs = 0;
438 state->response.length += gr_mem_len;
439 state->response.extra_data.data = gr_mem;
440 request_ok(state);
443 static void getgrgid_got_sid(struct winbindd_cli_state *state, DOM_SID group_sid)
445 struct winbindd_domain *domain;
446 enum SID_NAME_USE name_type;
447 fstring dom_name;
448 fstring group_name;
449 size_t gr_mem_len;
450 size_t num_gr_mem;
451 char *gr_mem;
453 /* Get name from sid */
455 if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
456 group_name, &name_type)) {
457 DEBUG(1, ("could not lookup sid\n"));
458 request_error(state);
459 return;
462 /* Fill in group structure */
464 domain = find_domain_from_sid_noinit(&group_sid);
466 if (!domain) {
467 DEBUG(1,("Can't find domain from sid\n"));
468 request_error(state);
469 return;
472 if ( !((name_type==SID_NAME_DOM_GRP) ||
473 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
474 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
476 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
477 group_name, name_type));
478 request_error(state);
479 return;
482 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
483 state->request.data.gid) ||
484 !fill_grent_mem(domain, state, &group_sid, name_type,
485 &num_gr_mem,
486 &gr_mem, &gr_mem_len)) {
487 request_error(state);
488 return;
491 state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
493 /* Group membership lives at start of extra data */
495 state->response.data.gr.gr_mem_ofs = 0;
497 state->response.length += gr_mem_len;
498 state->response.extra_data.data = gr_mem;
500 request_ok(state);
503 static void getgrgid_recv(void *private_data, BOOL success, const char *sid)
505 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
506 enum SID_NAME_USE name_type;
507 DOM_SID group_sid;
509 if (success) {
510 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
511 (unsigned long)(state->request.data.gid), sid));
513 string_to_sid(&group_sid, sid);
514 getgrgid_got_sid(state, group_sid);
515 return;
518 /* Ok, this might be "ours", i.e. an alias */
519 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
520 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
521 (name_type == SID_NAME_ALIAS)) {
522 /* Hey, got an alias */
523 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
524 (unsigned long)(state->request.data.gid), sid));
525 getgrgid_got_sid(state, group_sid);
526 return;
529 DEBUG(1, ("could not convert gid %lu to sid\n",
530 (unsigned long)state->request.data.gid));
531 request_error(state);
534 /* Return a group structure from a gid number */
535 void winbindd_getgrgid(struct winbindd_cli_state *state)
537 DOM_SID group_sid;
538 NTSTATUS status;
540 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
541 (unsigned long)state->request.data.gid));
543 /* Bug out if the gid isn't in the winbind range */
545 if ((state->request.data.gid < server_state.gid_low) ||
546 (state->request.data.gid > server_state.gid_high)) {
547 request_error(state);
548 return;
551 /* Get sid from gid */
553 status = idmap_gid_to_sid(&group_sid, state->request.data.gid, 0);
554 if (NT_STATUS_IS_OK(status)) {
555 /* This is a remote one */
556 getgrgid_got_sid(state, group_sid);
557 return;
560 DEBUG(10,("winbindd_getgrgid: gid %lu not found in cache, try with the async interface\n",
561 (unsigned long)state->request.data.gid));
563 winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
567 * set/get/endgrent functions
570 /* "Rewind" file pointer for group database enumeration */
572 static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
574 struct winbindd_domain *domain;
576 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
578 /* Check user has enabled this */
580 if (!lp_winbind_enum_groups()) {
581 return False;
584 /* Free old static data if it exists */
586 if (state->getgrent_state != NULL) {
587 free_getent_state(state->getgrent_state);
588 state->getgrent_state = NULL;
591 /* Create sam pipes for each domain we know about */
593 for (domain = domain_list(); domain != NULL; domain = domain->next) {
594 struct getent_state *domain_state;
596 /* Create a state record for this domain */
598 /* don't add our domaina if we are a PDC or if we
599 are a member of a Samba domain */
601 if ( lp_winbind_trusted_domains_only() && domain->primary )
603 continue;
607 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
608 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
609 return False;
612 ZERO_STRUCTP(domain_state);
614 fstrcpy(domain_state->domain_name, domain->name);
616 /* Add to list of open domains */
618 DLIST_ADD(state->getgrent_state, domain_state);
621 state->getgrent_initialized = True;
622 return True;
625 void winbindd_setgrent(struct winbindd_cli_state *state)
627 if (winbindd_setgrent_internal(state)) {
628 request_ok(state);
629 } else {
630 request_error(state);
634 /* Close file pointer to ntdom group database */
636 void winbindd_endgrent(struct winbindd_cli_state *state)
638 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
640 free_getent_state(state->getgrent_state);
641 state->getgrent_initialized = False;
642 state->getgrent_state = NULL;
643 request_ok(state);
646 /* Get the list of domain groups and domain aliases for a domain. We fill in
647 the sam_entries and num_sam_entries fields with domain group information.
648 The dispinfo_ndx field is incremented to the index of the next group to
649 fetch. Return True if some groups were returned, False otherwise. */
651 static BOOL get_sam_group_entries(struct getent_state *ent)
653 NTSTATUS status;
654 uint32 num_entries;
655 struct acct_info *name_list = NULL;
656 TALLOC_CTX *mem_ctx;
657 BOOL result = False;
658 struct acct_info *sam_grp_entries = NULL;
659 struct winbindd_domain *domain;
661 if (ent->got_sam_entries)
662 return False;
664 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
665 ent->domain_name))) {
666 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
667 return False;
670 /* Free any existing group info */
672 SAFE_FREE(ent->sam_entries);
673 ent->num_sam_entries = 0;
674 ent->got_sam_entries = True;
676 /* Enumerate domain groups */
678 num_entries = 0;
680 if (!(domain = find_domain_from_name(ent->domain_name))) {
681 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
682 goto done;
685 /* always get the domain global groups */
687 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
689 if (!NT_STATUS_IS_OK(status)) {
690 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
691 result = False;
692 goto done;
695 /* Copy entries into return buffer */
697 if (num_entries) {
698 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
699 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
700 num_entries));
701 result = False;
702 goto done;
704 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
707 ent->num_sam_entries = num_entries;
709 /* get the domain local groups if we are a member of a native win2k domain
710 and are not using LDAP to get the groups */
712 if ( ( lp_security() != SEC_ADS && domain->native_mode
713 && domain->primary) || domain->internal )
715 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
716 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
718 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
720 if ( !NT_STATUS_IS_OK(status) ) {
721 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
722 num_entries = 0;
724 else
725 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
727 /* Copy entries into return buffer */
729 if ( num_entries ) {
730 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
732 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
733 num_entries));
734 result = False;
735 goto done;
738 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
739 num_entries * sizeof(struct acct_info) );
742 ent->num_sam_entries += num_entries;
746 /* Fill in remaining fields */
748 ent->sam_entries = name_list;
749 ent->sam_entry_index = 0;
751 result = (ent->num_sam_entries > 0);
753 done:
754 talloc_destroy(mem_ctx);
756 return result;
759 /* Fetch next group entry from ntdom database */
761 #define MAX_GETGRENT_GROUPS 500
763 void winbindd_getgrent(struct winbindd_cli_state *state)
765 struct getent_state *ent;
766 struct winbindd_gr *group_list = NULL;
767 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
768 char *gr_mem_list = NULL;
770 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
772 /* Check user has enabled this */
774 if (!lp_winbind_enum_groups()) {
775 request_error(state);
776 return;
779 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
781 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
782 request_error(state);
783 return;
786 memset(state->response.extra_data.data, '\0',
787 num_groups * sizeof(struct winbindd_gr) );
789 state->response.data.num_entries = 0;
791 group_list = (struct winbindd_gr *)state->response.extra_data.data;
793 if (!state->getgrent_initialized)
794 winbindd_setgrent_internal(state);
796 if (!(ent = state->getgrent_state)) {
797 request_error(state);
798 return;
801 /* Start sending back groups */
803 for (i = 0; i < num_groups; i++) {
804 struct acct_info *name_list = NULL;
805 fstring domain_group_name;
806 uint32 result;
807 gid_t group_gid;
808 size_t gr_mem_len;
809 char *gr_mem;
810 DOM_SID group_sid;
811 struct winbindd_domain *domain;
813 /* Do we need to fetch another chunk of groups? */
815 tryagain:
817 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
818 ent->sam_entry_index, ent->num_sam_entries));
820 if (ent->num_sam_entries == ent->sam_entry_index) {
822 while(ent && !get_sam_group_entries(ent)) {
823 struct getent_state *next_ent;
825 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
827 /* Free state information for this domain */
829 SAFE_FREE(ent->sam_entries);
831 next_ent = ent->next;
832 DLIST_REMOVE(state->getgrent_state, ent);
834 SAFE_FREE(ent);
835 ent = next_ent;
838 /* No more domains */
840 if (!ent)
841 break;
844 name_list = (struct acct_info *)ent->sam_entries;
846 if (!(domain =
847 find_domain_from_name(ent->domain_name))) {
848 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
849 result = False;
850 goto done;
853 /* Lookup group info */
855 sid_copy(&group_sid, &domain->sid);
856 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
858 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid,
859 &group_gid, 0))) {
860 union unid_t id;
861 enum SID_NAME_USE type;
863 DEBUG(10, ("SID %s not in idmap\n",
864 sid_string_static(&group_sid)));
866 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
867 DEBUG(1, ("could not look up gid for group "
868 "%s\n",
869 name_list[ent->sam_entry_index].acct_name));
870 ent->sam_entry_index++;
871 goto tryagain;
874 if ((type != SID_NAME_DOM_GRP) &&
875 (type != SID_NAME_ALIAS) &&
876 (type != SID_NAME_WKN_GRP)) {
877 DEBUG(1, ("Group %s is a %s, not a group\n",
878 sid_type_lookup(type),
879 name_list[ent->sam_entry_index].acct_name));
880 ent->sam_entry_index++;
881 goto tryagain;
883 group_gid = id.gid;
886 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
887 (unsigned long)name_list[ent->sam_entry_index].rid));
889 /* Fill in group entry */
891 fill_domain_username(domain_group_name, ent->domain_name,
892 name_list[ent->sam_entry_index].acct_name, True);
894 result = fill_grent(&group_list[group_list_ndx],
895 ent->domain_name,
896 name_list[ent->sam_entry_index].acct_name,
897 group_gid);
899 /* Fill in group membership entry */
901 if (result) {
902 size_t num_gr_mem = 0;
903 DOM_SID member_sid;
904 group_list[group_list_ndx].num_gr_mem = 0;
905 gr_mem = NULL;
906 gr_mem_len = 0;
908 /* Get group membership */
909 if (state->request.cmd == WINBINDD_GETGRLST) {
910 result = True;
911 } else {
912 sid_copy(&member_sid, &domain->sid);
913 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
914 result = fill_grent_mem(
915 domain,
916 NULL,
917 &member_sid,
918 SID_NAME_DOM_GRP,
919 &num_gr_mem,
920 &gr_mem, &gr_mem_len);
922 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
926 if (result) {
927 /* Append to group membership list */
928 gr_mem_list = (char *)SMB_REALLOC(
929 gr_mem_list, gr_mem_list_len + gr_mem_len);
931 if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
932 DEBUG(0, ("out of memory\n"));
933 gr_mem_list_len = 0;
934 break;
937 DEBUG(10, ("list_len = %d, mem_len = %u\n",
938 gr_mem_list_len, (unsigned int)gr_mem_len));
940 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
941 gr_mem_len);
943 SAFE_FREE(gr_mem);
945 group_list[group_list_ndx].gr_mem_ofs =
946 gr_mem_list_len;
948 gr_mem_list_len += gr_mem_len;
951 ent->sam_entry_index++;
953 /* Add group to return list */
955 if (result) {
957 DEBUG(10, ("adding group num_entries = %d\n",
958 state->response.data.num_entries));
960 group_list_ndx++;
961 state->response.data.num_entries++;
963 state->response.length +=
964 sizeof(struct winbindd_gr);
966 } else {
967 DEBUG(0, ("could not lookup domain group %s\n",
968 domain_group_name));
972 /* Copy the list of group memberships to the end of the extra data */
974 if (group_list_ndx == 0)
975 goto done;
977 state->response.extra_data.data = SMB_REALLOC(
978 state->response.extra_data.data,
979 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
981 if (!state->response.extra_data.data) {
982 DEBUG(0, ("out of memory\n"));
983 group_list_ndx = 0;
984 SAFE_FREE(gr_mem_list);
985 request_error(state);
986 return;
989 memcpy(&((char *)state->response.extra_data.data)
990 [group_list_ndx * sizeof(struct winbindd_gr)],
991 gr_mem_list, gr_mem_list_len);
993 state->response.length += gr_mem_list_len;
995 DEBUG(10, ("returning %d groups, length = %d\n",
996 group_list_ndx, gr_mem_list_len));
998 /* Out of domains */
1000 done:
1002 SAFE_FREE(gr_mem_list);
1004 if (group_list_ndx > 0)
1005 request_ok(state);
1006 else
1007 request_error(state);
1010 /* List domain groups without mapping to unix ids */
1012 void winbindd_list_groups(struct winbindd_cli_state *state)
1014 uint32 total_entries = 0;
1015 struct winbindd_domain *domain;
1016 const char *which_domain;
1017 char *extra_data = NULL;
1018 unsigned int extra_data_len = 0, i;
1020 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
1022 /* Ensure null termination */
1023 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
1024 which_domain = state->request.domain_name;
1026 /* Enumerate over trusted domains */
1028 for (domain = domain_list(); domain; domain = domain->next) {
1029 struct getent_state groups;
1031 /* if we have a domain name restricting the request and this
1032 one in the list doesn't match, then just bypass the remainder
1033 of the loop */
1035 if ( *which_domain && !strequal(which_domain, domain->name) )
1036 continue;
1038 ZERO_STRUCT(groups);
1040 /* Get list of sam groups */
1042 fstrcpy(groups.domain_name, domain->name);
1044 get_sam_group_entries(&groups);
1046 if (groups.num_sam_entries == 0) {
1047 /* this domain is empty or in an error state */
1048 continue;
1051 /* keep track the of the total number of groups seen so
1052 far over all domains */
1053 total_entries += groups.num_sam_entries;
1055 /* Allocate some memory for extra data. Note that we limit
1056 account names to sizeof(fstring) = 128 characters. */
1057 extra_data = (char *)SMB_REALLOC(
1058 extra_data, sizeof(fstring) * total_entries);
1060 if (!extra_data) {
1061 DEBUG(0,("failed to enlarge buffer!\n"));
1062 request_error(state);
1063 return;
1066 /* Pack group list into extra data fields */
1067 for (i = 0; i < groups.num_sam_entries; i++) {
1068 char *group_name = ((struct acct_info *)
1069 groups.sam_entries)[i].acct_name;
1070 fstring name;
1072 fill_domain_username(name, domain->name, group_name, True);
1073 /* Append to extra data */
1074 memcpy(&extra_data[extra_data_len], name,
1075 strlen(name));
1076 extra_data_len += strlen(name);
1077 extra_data[extra_data_len++] = ',';
1080 SAFE_FREE(groups.sam_entries);
1083 /* Assign extra_data fields in response structure */
1084 if (extra_data) {
1085 extra_data[extra_data_len - 1] = '\0';
1086 state->response.extra_data.data = extra_data;
1087 state->response.length += extra_data_len;
1090 /* No domains may have responded but that's still OK so don't
1091 return an error. */
1093 request_ok(state);
1096 /* Get user supplementary groups. This is much quicker than trying to
1097 invert the groups database. We merge the groups from the gids and
1098 other_sids info3 fields as trusted domain, universal group
1099 memberships, and nested groups (win2k native mode only) are not
1100 returned by the getgroups RPC call but are present in the info3. */
1102 struct getgroups_state {
1103 struct winbindd_cli_state *state;
1104 struct winbindd_domain *domain;
1105 char *domname;
1106 char *username;
1107 DOM_SID user_sid;
1109 const DOM_SID *token_sids;
1110 size_t i, num_token_sids;
1112 gid_t *token_gids;
1113 size_t num_token_gids;
1116 static void getgroups_usersid_recv(void *private_data, BOOL success,
1117 const DOM_SID *sid, enum SID_NAME_USE type);
1118 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1119 DOM_SID *token_sids, size_t num_token_sids);
1120 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
1122 void winbindd_getgroups(struct winbindd_cli_state *state)
1124 struct getgroups_state *s;
1126 /* Ensure null termination */
1127 state->request.data.username
1128 [sizeof(state->request.data.username)-1]='\0';
1130 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1131 state->request.data.username));
1133 /* Parse domain and username */
1135 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1136 if (s == NULL) {
1137 DEBUG(0, ("talloc failed\n"));
1138 request_error(state);
1139 return;
1142 s->state = state;
1144 if (!parse_domain_user_talloc(state->mem_ctx,
1145 state->request.data.username,
1146 &s->domname, &s->username)) {
1147 DEBUG(5, ("Could not parse domain user: %s\n",
1148 state->request.data.username));
1150 /* error out if we do not have nested group support */
1152 if ( !lp_winbind_nested_groups() ) {
1153 request_error(state);
1154 return;
1157 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1158 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1161 /* Get info for the domain */
1163 s->domain = find_domain_from_name_noinit(s->domname);
1165 if (s->domain == NULL) {
1166 DEBUG(7, ("could not find domain entry for domain %s\n",
1167 s->domname));
1168 request_error(state);
1169 return;
1172 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1173 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting "
1174 "getgroups() for %s\\%s.\n", s->domname,
1175 s->username));
1176 request_error(state);
1177 return;
1180 /* Get rid and name type from name. The following costs 1 packet */
1182 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1183 getgroups_usersid_recv, s);
1186 static void getgroups_usersid_recv(void *private_data, BOOL success,
1187 const DOM_SID *sid, enum SID_NAME_USE type)
1189 struct getgroups_state *s =
1190 (struct getgroups_state *)private_data;
1192 if ((!success) ||
1193 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1194 request_error(s->state);
1195 return;
1198 sid_copy(&s->user_sid, sid);
1200 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1201 getgroups_tokensids_recv, s);
1204 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1205 DOM_SID *token_sids, size_t num_token_sids)
1207 struct getgroups_state *s =
1208 (struct getgroups_state *)private_data;
1210 /* We need at least the user sid and the primary group in the token,
1211 * otherwise it's an error */
1213 if ((!success) || (num_token_sids < 2)) {
1214 request_error(s->state);
1215 return;
1218 s->token_sids = token_sids;
1219 s->num_token_sids = num_token_sids;
1220 s->i = 0;
1222 s->token_gids = NULL;
1223 s->num_token_gids = 0;
1225 getgroups_sid2gid_recv(s, False, 0);
1228 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
1230 struct getgroups_state *s =
1231 (struct getgroups_state *)private_data;
1233 if (success)
1234 add_gid_to_array_unique(NULL, gid,
1235 &s->token_gids,
1236 &s->num_token_gids);
1238 if (s->i < s->num_token_sids) {
1239 const DOM_SID *sid = &s->token_sids[s->i];
1240 s->i += 1;
1242 if (sid_equal(sid, &s->user_sid)) {
1243 getgroups_sid2gid_recv(s, False, 0);
1244 return;
1247 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1248 getgroups_sid2gid_recv, s);
1249 return;
1252 s->state->response.data.num_entries = s->num_token_gids;
1253 s->state->response.extra_data.data = s->token_gids;
1254 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1255 request_ok(s->state);
1258 /* Get user supplementary sids. This is equivalent to the
1259 winbindd_getgroups() function but it involves a SID->SIDs mapping
1260 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1261 idmap. This call is designed to be used with applications that need
1262 to do ACL evaluation themselves. Note that the cached info3 data is
1263 not used
1265 this function assumes that the SID that comes in is a user SID. If
1266 you pass in another type of SID then you may get unpredictable
1267 results.
1270 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1271 size_t num_sids);
1273 void winbindd_getusersids(struct winbindd_cli_state *state)
1275 DOM_SID *user_sid;
1277 /* Ensure null termination */
1278 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1280 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1281 if (user_sid == NULL) {
1282 DEBUG(1, ("talloc failed\n"));
1283 request_error(state);
1284 return;
1287 if (!string_to_sid(user_sid, state->request.data.sid)) {
1288 DEBUG(1, ("Could not get convert sid %s from string\n",
1289 state->request.data.sid));
1290 request_error(state);
1291 return;
1294 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1295 state);
1298 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1299 size_t num_sids)
1301 struct winbindd_cli_state *state =
1302 (struct winbindd_cli_state *)private_data;
1303 char *ret = NULL;
1304 unsigned ofs, ret_size = 0;
1305 size_t i;
1307 if (!success) {
1308 request_error(state);
1309 return;
1312 /* work out the response size */
1313 for (i = 0; i < num_sids; i++) {
1314 const char *s = sid_string_static(&sids[i]);
1315 ret_size += strlen(s) + 1;
1318 /* build the reply */
1319 ret = (char *)SMB_MALLOC(ret_size);
1320 if (!ret) {
1321 DEBUG(0, ("malloc failed\n"));
1322 request_error(state);
1323 return;
1325 ofs = 0;
1326 for (i = 0; i < num_sids; i++) {
1327 const char *s = sid_string_static(&sids[i]);
1328 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1329 ofs += strlen(ret+ofs) + 1;
1332 /* Send data back to client */
1333 state->response.data.num_entries = num_sids;
1334 state->response.extra_data.data = ret;
1335 state->response.length += ret_size;
1336 request_ok(state);
1339 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1341 DOM_SID user_sid;
1342 struct winbindd_domain *domain;
1344 /* Ensure null termination */
1345 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1347 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1348 DEBUG(1, ("Could not get convert sid %s from string\n",
1349 state->request.data.sid));
1350 request_error(state);
1351 return;
1354 /* Get info for the domain */
1355 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1356 DEBUG(0,("could not find domain entry for sid %s\n",
1357 sid_string_static(&user_sid)));
1358 request_error(state);
1359 return;
1362 sendto_domain(state, domain);
1365 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1366 struct winbindd_cli_state *state)
1368 DOM_SID user_sid;
1369 NTSTATUS status;
1371 char *sidstring;
1372 ssize_t len;
1373 DOM_SID *groups;
1374 uint32 num_groups;
1376 /* Ensure null termination */
1377 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1379 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1380 DEBUG(1, ("Could not get convert sid %s from string\n",
1381 state->request.data.sid));
1382 return WINBINDD_ERROR;
1385 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1386 &user_sid, &num_groups,
1387 &groups);
1388 if (!NT_STATUS_IS_OK(status))
1389 return WINBINDD_ERROR;
1391 if (num_groups == 0) {
1392 state->response.data.num_entries = 0;
1393 state->response.extra_data.data = NULL;
1394 return WINBINDD_OK;
1397 if (!print_sidlist(NULL, groups, num_groups, &sidstring, &len)) {
1398 DEBUG(0, ("malloc failed\n"));
1399 return WINBINDD_ERROR;
1402 state->response.extra_data.data = sidstring;
1403 state->response.length += len+1;
1404 state->response.data.num_entries = num_groups;
1406 return WINBINDD_OK;