r14403: * modifies create_local_nt_token() to create a BUILTIN\Administrators
[Samba/nascimento.git] / source3 / nsswitch / winbindd_group.c
blob6e7a242379979422b9b3e8758676fed4007b3324
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, False);
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 DOM_SID *group_sid,
61 enum SID_NAME_USE group_name_type,
62 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
64 DOM_SID *sid_mem = NULL;
65 uint32 num_names = 0;
66 uint32 *name_types = NULL;
67 unsigned int buf_len, buf_ndx, i;
68 char **names = NULL, *buf;
69 BOOL result = False;
70 TALLOC_CTX *mem_ctx;
71 NTSTATUS status;
72 fstring sid_string;
74 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
75 return False;
77 /* Initialise group membership information */
79 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
81 *num_gr_mem = 0;
83 /* HACK ALERT!! This whole routine does not cope with group members
84 * from more than one domain, ie aliases. Thus we have to work it out
85 * ourselves in a special routine. */
87 if (domain->internal)
88 return fill_passdb_alias_grmem(domain, group_sid,
89 num_gr_mem,
90 gr_mem, gr_mem_len);
92 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
93 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
95 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
96 sid_to_string(sid_string, group_sid), domain->name,
97 group_name_type));
98 goto done;
101 /* Lookup group members */
102 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
103 &sid_mem, &names, &name_types);
104 if (!NT_STATUS_IS_OK(status)) {
105 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
106 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
108 goto done;
111 DEBUG(10, ("looked up %d names\n", num_names));
113 if (DEBUGLEVEL >= 10) {
114 for (i = 0; i < num_names; i++)
115 DEBUG(10, ("\t%20s %s %d\n", names[i],
116 sid_string_static(&sid_mem[i]),
117 name_types[i]));
120 /* Add members to list */
122 buf = NULL;
123 buf_len = buf_ndx = 0;
125 again:
127 for (i = 0; i < num_names; i++) {
128 char *the_name;
129 fstring name;
130 int len;
132 the_name = names[i];
134 DEBUG(10, ("processing name %s\n", the_name));
136 /* FIXME: need to cope with groups within groups. These
137 occur in Universal groups on a Windows 2000 native mode
138 server. */
140 /* make sure to allow machine accounts */
142 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
143 DEBUG(3, ("name %s isn't a domain user (%s)\n", the_name, sid_type_lookup(name_types[i])));
144 continue;
147 /* Append domain name */
149 fill_domain_username(name, domain->name, the_name, False);
151 len = strlen(name);
153 /* Add to list or calculate buffer length */
155 if (!buf) {
156 buf_len += len + 1; /* List is comma separated */
157 (*num_gr_mem)++;
158 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
159 } else {
160 DEBUG(10, ("appending %s at ndx %d\n", name, len));
161 safe_strcpy(&buf[buf_ndx], name, len);
162 buf_ndx += len;
163 buf[buf_ndx] = ',';
164 buf_ndx++;
168 /* Allocate buffer */
170 if (!buf && buf_len != 0) {
171 if (!(buf = SMB_MALLOC(buf_len))) {
172 DEBUG(1, ("out of memory\n"));
173 result = False;
174 goto done;
176 memset(buf, 0, buf_len);
177 goto again;
180 if (buf && buf_ndx > 0) {
181 buf[buf_ndx - 1] = '\0';
184 *gr_mem = buf;
185 *gr_mem_len = buf_len;
187 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
188 buf_len, *num_gr_mem ? buf : "NULL"));
189 result = True;
191 done:
193 talloc_destroy(mem_ctx);
195 DEBUG(10, ("fill_grent_mem returning %d\n", result));
197 return result;
200 /* Return a group structure from a group name */
202 void winbindd_getgrnam(struct winbindd_cli_state *state)
204 DOM_SID group_sid, tmp_sid;
205 uint32 grp_rid;
206 struct winbindd_domain *domain;
207 enum SID_NAME_USE name_type;
208 fstring name_domain, name_group;
209 char *tmp, *gr_mem;
210 size_t gr_mem_len;
211 gid_t gid;
212 union unid_t id;
213 NTSTATUS status;
215 /* Ensure null termination */
216 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
218 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
219 state->request.data.groupname));
221 /* Parse domain and groupname */
223 memset(name_group, 0, sizeof(fstring));
225 tmp = state->request.data.groupname;
227 parse_domain_user(tmp, name_domain, name_group);
229 /* if no domain or our local domain and no local tdb group, default to
230 * our local domain for aliases */
232 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
233 fstrcpy(name_domain, get_global_sam_name());
236 /* Get info for the domain */
238 if ((domain = find_domain_from_name(name_domain)) == NULL) {
239 DEBUG(3, ("could not get domain sid for domain %s\n",
240 name_domain));
241 request_error(state);
242 return;
244 /* should we deal with users for our domain? */
246 if ( lp_winbind_trusted_domains_only() && domain->primary) {
247 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
248 "getgrnam() for %s\\%s.\n", name_domain, name_group));
249 request_error(state);
250 return;
253 /* Get rid and name type from name */
255 if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
256 name_group, &group_sid, &name_type)) {
257 DEBUG(1, ("group %s in domain %s does not exist\n",
258 name_group, name_domain));
259 request_error(state);
260 return;
263 if ( !((name_type==SID_NAME_DOM_GRP) ||
264 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
265 ((name_type==SID_NAME_ALIAS) && domain->internal) ||
266 ((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
268 DEBUG(1, ("name '%s' is not a local, domain or builtin "
269 "group: %d\n", name_group, name_type));
270 request_error(state);
271 return;
274 /* Make sure that the group SID is within the domain of the
275 original domain */
277 sid_copy( &tmp_sid, &group_sid );
278 sid_split_rid( &tmp_sid, &grp_rid );
279 if ( !sid_equal( &tmp_sid, &domain->sid ) ) {
280 DEBUG(3,("winbindd_getgrnam: group %s resolves to a SID in the wrong domain [%s]\n",
281 state->request.data.groupname, sid_string_static(&group_sid)));
282 request_error(state);
283 return;
288 /* Try to get the GID */
290 status = idmap_sid_to_gid(&group_sid, &gid, 0);
292 if (NT_STATUS_IS_OK(status)) {
293 goto got_gid;
296 /* Maybe it's one of our aliases in passdb */
298 if (pdb_sid_to_id(&group_sid, &id, &name_type) &&
299 ((name_type == SID_NAME_ALIAS) ||
300 (name_type == SID_NAME_WKN_GRP))) {
301 gid = id.gid;
302 goto got_gid;
305 DEBUG(1, ("error converting unix gid to sid\n"));
306 request_error(state);
307 return;
309 got_gid:
311 if (!fill_grent(&state->response.data.gr, name_domain,
312 name_group, gid) ||
313 !fill_grent_mem(domain, &group_sid, name_type,
314 &state->response.data.gr.num_gr_mem,
315 &gr_mem, &gr_mem_len)) {
316 request_error(state);
317 return;
320 /* Group membership lives at start of extra data */
322 state->response.data.gr.gr_mem_ofs = 0;
324 state->response.length += gr_mem_len;
325 state->response.extra_data = gr_mem;
326 request_ok(state);
329 /* Return a group structure from a gid number */
331 void winbindd_getgrgid(struct winbindd_cli_state *state)
333 struct winbindd_domain *domain;
334 DOM_SID group_sid;
335 enum SID_NAME_USE name_type;
336 fstring dom_name;
337 fstring group_name;
338 size_t gr_mem_len;
339 char *gr_mem;
340 NTSTATUS status;
342 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
343 (unsigned long)state->request.data.gid));
345 /* Bug out if the gid isn't in the winbind range */
347 if ((state->request.data.gid < server_state.gid_low) ||
348 (state->request.data.gid > server_state.gid_high)) {
349 request_error(state);
350 return;
353 /* Get sid from gid */
355 status = idmap_gid_to_sid(&group_sid, state->request.data.gid, 0);
356 if (NT_STATUS_IS_OK(status)) {
357 /* This is a remote one */
358 goto got_sid;
361 /* Ok, this might be "ours", i.e. an alias */
363 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
364 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
365 (name_type == SID_NAME_ALIAS)) {
366 /* Hey, got an alias */
367 goto got_sid;
370 DEBUG(1, ("could not convert gid %lu to sid\n",
371 (unsigned long)state->request.data.gid));
372 request_error(state);
373 return;
375 got_sid:
376 /* Get name from sid */
378 if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, dom_name,
379 group_name, &name_type)) {
380 DEBUG(1, ("could not lookup sid\n"));
381 request_error(state);
382 return;
385 /* Fill in group structure */
387 domain = find_domain_from_sid_noinit(&group_sid);
389 if (!domain) {
390 DEBUG(1,("Can't find domain from sid\n"));
391 request_error(state);
392 return;
395 if ( !((name_type==SID_NAME_DOM_GRP) ||
396 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
397 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
399 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
400 group_name, name_type));
401 request_error(state);
402 return;
405 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
406 state->request.data.gid) ||
407 !fill_grent_mem(domain, &group_sid, name_type,
408 &state->response.data.gr.num_gr_mem,
409 &gr_mem, &gr_mem_len)) {
410 request_error(state);
411 return;
414 /* Group membership lives at start of extra data */
416 state->response.data.gr.gr_mem_ofs = 0;
418 state->response.length += gr_mem_len;
419 state->response.extra_data = gr_mem;
420 request_ok(state);
424 * set/get/endgrent functions
427 /* "Rewind" file pointer for group database enumeration */
429 static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
431 struct winbindd_domain *domain;
433 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
435 /* Check user has enabled this */
437 if (!lp_winbind_enum_groups()) {
438 return False;
441 /* Free old static data if it exists */
443 if (state->getgrent_state != NULL) {
444 free_getent_state(state->getgrent_state);
445 state->getgrent_state = NULL;
448 /* Create sam pipes for each domain we know about */
450 for (domain = domain_list(); domain != NULL; domain = domain->next) {
451 struct getent_state *domain_state;
453 /* Create a state record for this domain */
455 /* don't add our domaina if we are a PDC or if we
456 are a member of a Samba domain */
458 if ( lp_winbind_trusted_domains_only() && domain->primary )
460 continue;
464 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
465 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
466 return False;
469 ZERO_STRUCTP(domain_state);
471 fstrcpy(domain_state->domain_name, domain->name);
473 /* Add to list of open domains */
475 DLIST_ADD(state->getgrent_state, domain_state);
478 state->getgrent_initialized = True;
479 return True;
482 void winbindd_setgrent(struct winbindd_cli_state *state)
484 if (winbindd_setgrent_internal(state)) {
485 request_ok(state);
486 } else {
487 request_error(state);
491 /* Close file pointer to ntdom group database */
493 void winbindd_endgrent(struct winbindd_cli_state *state)
495 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
497 free_getent_state(state->getgrent_state);
498 state->getgrent_initialized = False;
499 state->getgrent_state = NULL;
500 request_ok(state);
503 /* Get the list of domain groups and domain aliases for a domain. We fill in
504 the sam_entries and num_sam_entries fields with domain group information.
505 The dispinfo_ndx field is incremented to the index of the next group to
506 fetch. Return True if some groups were returned, False otherwise. */
508 static BOOL get_sam_group_entries(struct getent_state *ent)
510 NTSTATUS status;
511 uint32 num_entries;
512 struct acct_info *name_list = NULL;
513 TALLOC_CTX *mem_ctx;
514 BOOL result = False;
515 struct acct_info *sam_grp_entries = NULL;
516 struct winbindd_domain *domain;
518 if (ent->got_sam_entries)
519 return False;
521 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
522 ent->domain_name))) {
523 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
524 return False;
527 /* Free any existing group info */
529 SAFE_FREE(ent->sam_entries);
530 ent->num_sam_entries = 0;
531 ent->got_sam_entries = True;
533 /* Enumerate domain groups */
535 num_entries = 0;
537 if (!(domain = find_domain_from_name(ent->domain_name))) {
538 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
539 goto done;
542 /* always get the domain global groups */
544 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
546 if (!NT_STATUS_IS_OK(status)) {
547 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
548 result = False;
549 goto done;
552 /* Copy entries into return buffer */
554 if (num_entries) {
555 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
556 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
557 num_entries));
558 result = False;
559 goto done;
561 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
564 ent->num_sam_entries = num_entries;
566 /* get the domain local groups if we are a member of a native win2k domain
567 and are not using LDAP to get the groups */
569 if ( ( lp_security() != SEC_ADS && domain->native_mode
570 && domain->primary) || domain->internal )
572 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
573 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
575 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
577 if ( !NT_STATUS_IS_OK(status) ) {
578 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
579 num_entries = 0;
581 else
582 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
584 /* Copy entries into return buffer */
586 if ( num_entries ) {
587 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
589 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
590 num_entries));
591 result = False;
592 goto done;
595 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
596 num_entries * sizeof(struct acct_info) );
599 ent->num_sam_entries += num_entries;
603 /* Fill in remaining fields */
605 ent->sam_entries = name_list;
606 ent->sam_entry_index = 0;
608 result = (ent->num_sam_entries > 0);
610 done:
611 talloc_destroy(mem_ctx);
613 return result;
616 /* Fetch next group entry from ntdom database */
618 #define MAX_GETGRENT_GROUPS 500
620 void winbindd_getgrent(struct winbindd_cli_state *state)
622 struct getent_state *ent;
623 struct winbindd_gr *group_list = NULL;
624 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
625 char *gr_mem_list = NULL;
627 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
629 /* Check user has enabled this */
631 if (!lp_winbind_enum_groups()) {
632 request_error(state);
633 return;
636 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
638 if ((state->response.extra_data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
639 request_error(state);
640 return;
643 memset(state->response.extra_data, '\0',
644 num_groups * sizeof(struct winbindd_gr) );
646 state->response.data.num_entries = 0;
648 group_list = (struct winbindd_gr *)state->response.extra_data;
650 if (!state->getgrent_initialized)
651 winbindd_setgrent_internal(state);
653 if (!(ent = state->getgrent_state)) {
654 request_error(state);
655 return;
658 /* Start sending back groups */
660 for (i = 0; i < num_groups; i++) {
661 struct acct_info *name_list = NULL;
662 fstring domain_group_name;
663 uint32 result;
664 gid_t group_gid;
665 size_t gr_mem_len;
666 char *gr_mem;
667 DOM_SID group_sid;
668 struct winbindd_domain *domain;
670 /* Do we need to fetch another chunk of groups? */
672 tryagain:
674 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
675 ent->sam_entry_index, ent->num_sam_entries));
677 if (ent->num_sam_entries == ent->sam_entry_index) {
679 while(ent && !get_sam_group_entries(ent)) {
680 struct getent_state *next_ent;
682 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
684 /* Free state information for this domain */
686 SAFE_FREE(ent->sam_entries);
688 next_ent = ent->next;
689 DLIST_REMOVE(state->getgrent_state, ent);
691 SAFE_FREE(ent);
692 ent = next_ent;
695 /* No more domains */
697 if (!ent)
698 break;
701 name_list = ent->sam_entries;
703 if (!(domain =
704 find_domain_from_name(ent->domain_name))) {
705 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
706 result = False;
707 goto done;
710 /* Lookup group info */
712 sid_copy(&group_sid, &domain->sid);
713 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
715 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid,
716 &group_gid, 0))) {
717 union unid_t id;
718 enum SID_NAME_USE type;
720 DEBUG(10, ("SID %s not in idmap\n",
721 sid_string_static(&group_sid)));
723 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
724 DEBUG(1, ("could not look up gid for group "
725 "%s\n",
726 name_list[ent->sam_entry_index].acct_name));
727 ent->sam_entry_index++;
728 goto tryagain;
731 if ((type != SID_NAME_DOM_GRP) &&
732 (type != SID_NAME_ALIAS) &&
733 (type != SID_NAME_WKN_GRP)) {
734 DEBUG(1, ("Group %s is a %s, not a group\n",
735 sid_type_lookup(type),
736 name_list[ent->sam_entry_index].acct_name));
737 ent->sam_entry_index++;
738 goto tryagain;
740 group_gid = id.gid;
743 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
744 (unsigned long)name_list[ent->sam_entry_index].rid));
746 /* Fill in group entry */
748 fill_domain_username(domain_group_name, ent->domain_name,
749 name_list[ent->sam_entry_index].acct_name, False);
751 result = fill_grent(&group_list[group_list_ndx],
752 ent->domain_name,
753 name_list[ent->sam_entry_index].acct_name,
754 group_gid);
756 /* Fill in group membership entry */
758 if (result) {
759 DOM_SID member_sid;
760 group_list[group_list_ndx].num_gr_mem = 0;
761 gr_mem = NULL;
762 gr_mem_len = 0;
764 /* Get group membership */
765 if (state->request.cmd == WINBINDD_GETGRLST) {
766 result = True;
767 } else {
768 sid_copy(&member_sid, &domain->sid);
769 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
770 result = fill_grent_mem(
771 domain,
772 &member_sid,
773 SID_NAME_DOM_GRP,
774 &group_list[group_list_ndx].num_gr_mem,
775 &gr_mem, &gr_mem_len);
779 if (result) {
780 /* Append to group membership list */
781 gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len);
783 if (!gr_mem_list) {
784 DEBUG(0, ("out of memory\n"));
785 gr_mem_list_len = 0;
786 break;
789 DEBUG(10, ("list_len = %d, mem_len = %d\n",
790 gr_mem_list_len, gr_mem_len));
792 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
793 gr_mem_len);
795 SAFE_FREE(gr_mem);
797 group_list[group_list_ndx].gr_mem_ofs =
798 gr_mem_list_len;
800 gr_mem_list_len += gr_mem_len;
803 ent->sam_entry_index++;
805 /* Add group to return list */
807 if (result) {
809 DEBUG(10, ("adding group num_entries = %d\n",
810 state->response.data.num_entries));
812 group_list_ndx++;
813 state->response.data.num_entries++;
815 state->response.length +=
816 sizeof(struct winbindd_gr);
818 } else {
819 DEBUG(0, ("could not lookup domain group %s\n",
820 domain_group_name));
824 /* Copy the list of group memberships to the end of the extra data */
826 if (group_list_ndx == 0)
827 goto done;
829 state->response.extra_data = SMB_REALLOC(
830 state->response.extra_data,
831 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
833 if (!state->response.extra_data) {
834 DEBUG(0, ("out of memory\n"));
835 group_list_ndx = 0;
836 SAFE_FREE(gr_mem_list);
837 request_error(state);
838 return;
841 memcpy(&((char *)state->response.extra_data)
842 [group_list_ndx * sizeof(struct winbindd_gr)],
843 gr_mem_list, gr_mem_list_len);
845 state->response.length += gr_mem_list_len;
847 DEBUG(10, ("returning %d groups, length = %d\n",
848 group_list_ndx, gr_mem_list_len));
850 /* Out of domains */
852 done:
854 SAFE_FREE(gr_mem_list);
856 if (group_list_ndx > 0)
857 request_ok(state);
858 else
859 request_error(state);
862 /* List domain groups without mapping to unix ids */
864 void winbindd_list_groups(struct winbindd_cli_state *state)
866 uint32 total_entries = 0;
867 struct winbindd_domain *domain;
868 const char *which_domain;
869 char *extra_data = NULL;
870 unsigned int extra_data_len = 0, i;
872 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
874 /* Ensure null termination */
875 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
876 which_domain = state->request.domain_name;
878 /* Enumerate over trusted domains */
880 for (domain = domain_list(); domain; domain = domain->next) {
881 struct getent_state groups;
883 /* if we have a domain name restricting the request and this
884 one in the list doesn't match, then just bypass the remainder
885 of the loop */
887 if ( *which_domain && !strequal(which_domain, domain->name) )
888 continue;
890 ZERO_STRUCT(groups);
892 /* Get list of sam groups */
894 fstrcpy(groups.domain_name, domain->name);
896 get_sam_group_entries(&groups);
898 if (groups.num_sam_entries == 0) {
899 /* this domain is empty or in an error state */
900 continue;
903 /* keep track the of the total number of groups seen so
904 far over all domains */
905 total_entries += groups.num_sam_entries;
907 /* Allocate some memory for extra data. Note that we limit
908 account names to sizeof(fstring) = 128 characters. */
909 extra_data = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries);
911 if (!extra_data) {
912 DEBUG(0,("failed to enlarge buffer!\n"));
913 request_error(state);
914 return;
917 /* Pack group list into extra data fields */
918 for (i = 0; i < groups.num_sam_entries; i++) {
919 char *group_name = ((struct acct_info *)
920 groups.sam_entries)[i].acct_name;
921 fstring name;
923 fill_domain_username(name, domain->name, group_name, False);
924 /* Append to extra data */
925 memcpy(&extra_data[extra_data_len], name,
926 strlen(name));
927 extra_data_len += strlen(name);
928 extra_data[extra_data_len++] = ',';
931 SAFE_FREE(groups.sam_entries);
934 /* Assign extra_data fields in response structure */
935 if (extra_data) {
936 extra_data[extra_data_len - 1] = '\0';
937 state->response.extra_data = extra_data;
938 state->response.length += extra_data_len;
941 /* No domains may have responded but that's still OK so don't
942 return an error. */
944 request_ok(state);
947 /* Get user supplementary groups. This is much quicker than trying to
948 invert the groups database. We merge the groups from the gids and
949 other_sids info3 fields as trusted domain, universal group
950 memberships, and nested groups (win2k native mode only) are not
951 returned by the getgroups RPC call but are present in the info3. */
953 struct getgroups_state {
954 struct winbindd_cli_state *state;
955 struct winbindd_domain *domain;
956 char *domname;
957 char *username;
958 DOM_SID user_sid;
960 const DOM_SID *token_sids;
961 size_t i, num_token_sids;
963 gid_t *token_gids;
964 size_t num_token_gids;
967 static void getgroups_usersid_recv(void *private_data, BOOL success,
968 const DOM_SID *sid, enum SID_NAME_USE type);
969 static void getgroups_tokensids_recv(void *private_data, BOOL success,
970 DOM_SID *token_sids, size_t num_token_sids);
971 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
973 void winbindd_getgroups(struct winbindd_cli_state *state)
975 struct getgroups_state *s;
977 /* Ensure null termination */
978 state->request.data.username
979 [sizeof(state->request.data.username)-1]='\0';
981 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
982 state->request.data.username));
984 /* Parse domain and username */
986 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
987 if (s == NULL) {
988 DEBUG(0, ("talloc failed\n"));
989 request_error(state);
990 return;
993 s->state = state;
995 if (!parse_domain_user_talloc(state->mem_ctx,
996 state->request.data.username,
997 &s->domname, &s->username)) {
998 DEBUG(5, ("Could not parse domain user: %s\n",
999 state->request.data.username));
1000 request_error(state);
1001 return;
1004 /* Get info for the domain */
1006 s->domain = find_domain_from_name_noinit(s->domname);
1008 if (s->domain == NULL) {
1009 DEBUG(7, ("could not find domain entry for domain %s\n",
1010 s->domname));
1011 request_error(state);
1012 return;
1015 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1016 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting "
1017 "getgroups() for %s\\%s.\n", s->domname,
1018 s->username));
1019 request_error(state);
1020 return;
1023 /* Get rid and name type from name. The following costs 1 packet */
1025 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1026 getgroups_usersid_recv, s);
1029 static void getgroups_usersid_recv(void *private_data, BOOL success,
1030 const DOM_SID *sid, enum SID_NAME_USE type)
1032 struct getgroups_state *s = private_data;
1034 if ((!success) ||
1035 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1036 request_error(s->state);
1037 return;
1040 sid_copy(&s->user_sid, sid);
1042 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1043 getgroups_tokensids_recv, s);
1046 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1047 DOM_SID *token_sids, size_t num_token_sids)
1049 struct getgroups_state *s = private_data;
1051 /* We need at least the user sid and the primary group in the token,
1052 * otherwise it's an error */
1054 if ((!success) || (num_token_sids < 2)) {
1055 request_error(s->state);
1056 return;
1059 s->token_sids = token_sids;
1060 s->num_token_sids = num_token_sids;
1061 s->i = 0;
1063 s->token_gids = NULL;
1064 s->num_token_gids = 0;
1066 getgroups_sid2gid_recv(s, False, 0);
1069 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
1071 struct getgroups_state *s = private_data;
1073 if (success)
1074 add_gid_to_array_unique(NULL, gid,
1075 &s->token_gids,
1076 &s->num_token_gids);
1078 if (s->i < s->num_token_sids) {
1079 const DOM_SID *sid = &s->token_sids[s->i];
1080 s->i += 1;
1082 if (sid_equal(sid, &s->user_sid)) {
1083 getgroups_sid2gid_recv(s, False, 0);
1084 return;
1087 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1088 getgroups_sid2gid_recv, s);
1089 return;
1092 s->state->response.data.num_entries = s->num_token_gids;
1093 s->state->response.extra_data = s->token_gids;
1094 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1095 request_ok(s->state);
1098 /* Get user supplementary sids. This is equivalent to the
1099 winbindd_getgroups() function but it involves a SID->SIDs mapping
1100 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1101 idmap. This call is designed to be used with applications that need
1102 to do ACL evaluation themselves. Note that the cached info3 data is
1103 not used
1105 this function assumes that the SID that comes in is a user SID. If
1106 you pass in another type of SID then you may get unpredictable
1107 results.
1110 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1111 size_t num_sids);
1113 void winbindd_getusersids(struct winbindd_cli_state *state)
1115 DOM_SID *user_sid;
1117 /* Ensure null termination */
1118 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1120 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1121 if (user_sid == NULL) {
1122 DEBUG(1, ("talloc failed\n"));
1123 request_error(state);
1124 return;
1127 if (!string_to_sid(user_sid, state->request.data.sid)) {
1128 DEBUG(1, ("Could not get convert sid %s from string\n",
1129 state->request.data.sid));
1130 request_error(state);
1131 return;
1134 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1135 state);
1138 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1139 size_t num_sids)
1141 struct winbindd_cli_state *state = private_data;
1142 char *ret = NULL;
1143 unsigned ofs, ret_size = 0;
1144 size_t i;
1146 if (!success) {
1147 request_error(state);
1148 return;
1151 /* work out the response size */
1152 for (i = 0; i < num_sids; i++) {
1153 const char *s = sid_string_static(&sids[i]);
1154 ret_size += strlen(s) + 1;
1157 /* build the reply */
1158 ret = SMB_MALLOC(ret_size);
1159 if (!ret) {
1160 DEBUG(0, ("malloc failed\n"));
1161 request_error(state);
1162 return;
1164 ofs = 0;
1165 for (i = 0; i < num_sids; i++) {
1166 const char *s = sid_string_static(&sids[i]);
1167 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1168 ofs += strlen(ret+ofs) + 1;
1171 /* Send data back to client */
1172 state->response.data.num_entries = num_sids;
1173 state->response.extra_data = ret;
1174 state->response.length += ret_size;
1175 request_ok(state);
1178 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1180 DOM_SID user_sid;
1181 struct winbindd_domain *domain;
1183 /* Ensure null termination */
1184 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1186 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1187 DEBUG(1, ("Could not get convert sid %s from string\n",
1188 state->request.data.sid));
1189 request_error(state);
1190 return;
1193 /* Get info for the domain */
1194 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1195 DEBUG(0,("could not find domain entry for sid %s\n",
1196 sid_string_static(&user_sid)));
1197 request_error(state);
1198 return;
1201 sendto_domain(state, domain);
1204 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1205 struct winbindd_cli_state *state)
1207 DOM_SID user_sid;
1208 NTSTATUS status;
1210 char *sidstring;
1211 ssize_t len;
1212 DOM_SID *groups;
1213 uint32 num_groups;
1215 /* Ensure null termination */
1216 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1218 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1219 DEBUG(1, ("Could not get convert sid %s from string\n",
1220 state->request.data.sid));
1221 return WINBINDD_ERROR;
1224 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1225 &user_sid, &num_groups,
1226 &groups);
1227 if (!NT_STATUS_IS_OK(status))
1228 return WINBINDD_ERROR;
1230 if (num_groups == 0) {
1231 state->response.data.num_entries = 0;
1232 state->response.extra_data = NULL;
1233 return WINBINDD_OK;
1236 if (!print_sidlist(NULL, groups, num_groups, &sidstring, &len)) {
1237 DEBUG(0, ("malloc failed\n"));
1238 return WINBINDD_ERROR;
1241 state->response.extra_data = sidstring;
1242 state->response.length += len+1;
1243 state->response.data.num_entries = num_groups;
1245 return WINBINDD_OK;