And another memory corruption in winbind. Arg 3 of safe_strcpy does not
[Samba/gebeck_regimport.git] / source3 / nsswitch / winbindd_group.c
blob4805e628dd800fc0c592eab48640df5b673cffd9
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.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include "includes.h"
26 #include "winbindd.h"
28 #undef DBGC_CLASS
29 #define DBGC_CLASS DBGC_WINBIND
31 /*********************************************************************
32 *********************************************************************/
34 static int gr_mem_buffer( char **buffer, char **members, int num_members )
36 int i;
37 int len = 0;
38 int idx = 0;
40 if ( num_members == 0 ) {
41 *buffer = NULL;
42 return 0;
45 for ( i=0; i<num_members; i++ )
46 len += strlen(members[i])+1;
48 *buffer = (char*)smb_xmalloc(len);
49 for ( i=0; i<num_members; i++ ) {
50 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
51 idx += strlen(members[i])+1;
53 /* terminate with NULL */
54 (*buffer)[len-1] = '\0';
56 return len;
59 /***************************************************************
60 Empty static struct for negative caching.
61 ****************************************************************/
63 /* Fill a grent structure from various other information */
65 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
66 const char *gr_name, gid_t unix_gid)
68 fstring full_group_name;
69 /* Fill in uid/gid */
70 fill_domain_username(full_group_name, dom_name, gr_name);
72 gr->gr_gid = unix_gid;
74 /* Group name and password */
76 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
77 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
79 return True;
82 /* Fill in the group membership field of a NT group given by group_sid */
84 static BOOL fill_grent_mem(struct winbindd_domain *domain,
85 DOM_SID *group_sid,
86 enum SID_NAME_USE group_name_type,
87 int *num_gr_mem, char **gr_mem, int *gr_mem_len)
89 DOM_SID **sid_mem = NULL;
90 uint32 num_names = 0;
91 uint32 *name_types = NULL;
92 unsigned int buf_len, buf_ndx, i;
93 char **names = NULL, *buf;
94 BOOL result = False;
95 TALLOC_CTX *mem_ctx;
96 NTSTATUS status;
97 fstring sid_string;
99 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
100 return False;
102 /* Initialise group membership information */
104 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
106 *num_gr_mem = 0;
108 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
109 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
111 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
112 sid_to_string(sid_string, group_sid), domain->name,
113 group_name_type));
114 goto done;
117 /* Lookup group members */
118 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
119 &sid_mem, &names, &name_types);
120 if (!NT_STATUS_IS_OK(status)) {
121 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
122 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
124 goto done;
127 DEBUG(10, ("looked up %d names\n", num_names));
129 if (DEBUGLEVEL >= 10) {
130 for (i = 0; i < num_names; i++)
131 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
132 name_types[i]));
135 /* Add members to list */
137 buf = NULL;
138 buf_len = buf_ndx = 0;
140 again:
142 for (i = 0; i < num_names; i++) {
143 char *the_name;
144 fstring name;
145 int len;
147 the_name = names[i];
149 DEBUG(10, ("processing name %s\n", the_name));
151 /* FIXME: need to cope with groups within groups. These
152 occur in Universal groups on a Windows 2000 native mode
153 server. */
155 /* make sure to allow machine accounts */
157 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
158 DEBUG(3, ("name %s isn't a domain user\n", the_name));
159 continue;
162 /* Append domain name */
164 fill_domain_username(name, domain->name, the_name);
166 len = strlen(name);
168 /* Add to list or calculate buffer length */
170 if (!buf) {
171 buf_len += len + 1; /* List is comma separated */
172 (*num_gr_mem)++;
173 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
174 } else {
175 DEBUG(10, ("appending %s at ndx %d\n", name, len));
176 safe_strcpy(&buf[buf_ndx], name, len);
177 buf_ndx += len;
178 buf[buf_ndx] = ',';
179 buf_ndx++;
183 /* Allocate buffer */
185 if (!buf && buf_len != 0) {
186 if (!(buf = malloc(buf_len))) {
187 DEBUG(1, ("out of memory\n"));
188 result = False;
189 goto done;
191 memset(buf, 0, buf_len);
192 goto again;
195 if (buf && buf_ndx > 0) {
196 buf[buf_ndx - 1] = '\0';
199 *gr_mem = buf;
200 *gr_mem_len = buf_len;
202 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
203 buf_len, *num_gr_mem ? buf : "NULL"));
204 result = True;
206 done:
208 talloc_destroy(mem_ctx);
210 DEBUG(10, ("fill_grent_mem returning %d\n", result));
212 return result;
215 /* Return a group structure from a group name */
217 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
219 DOM_SID group_sid;
220 WINBINDD_GR *grp;
221 struct winbindd_domain *domain;
222 enum SID_NAME_USE name_type;
223 fstring name_domain, name_group;
224 char *tmp, *gr_mem;
225 int gr_mem_len;
226 gid_t gid;
228 /* Ensure null termination */
229 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
231 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
232 state->request.data.groupname));
234 /* Parse domain and groupname */
236 memset(name_group, 0, sizeof(fstring));
238 tmp = state->request.data.groupname;
240 parse_domain_user(tmp, name_domain, name_group);
242 /* if no domain or our local domain, then do a local tdb search */
244 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
245 char *buffer = NULL;
247 if ( !(grp=wb_getgrnam(name_group)) ) {
248 DEBUG(5,("winbindd_getgrnam: lookup for %s\\%s failed\n",
249 name_domain, name_group));
250 return WINBINDD_ERROR;
252 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
254 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
256 state->response.data.gr.gr_mem_ofs = 0;
257 state->response.length += gr_mem_len;
258 state->response.extra_data = buffer; /* give the memory away */
260 return WINBINDD_OK;
263 /* Get info for the domain */
265 if ((domain = find_domain_from_name(name_domain)) == NULL) {
266 DEBUG(3, ("could not get domain sid for domain %s\n",
267 name_domain));
268 return WINBINDD_ERROR;
270 /* should we deal with users for our domain? */
272 if ( lp_winbind_trusted_domains_only() && domain->primary) {
273 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
274 name_domain, name_group));
275 return WINBINDD_ERROR;
278 /* Get rid and name type from name */
280 if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
281 &name_type)) {
282 DEBUG(1, ("group %s in domain %s does not exist\n",
283 name_group, name_domain));
284 return WINBINDD_ERROR;
287 if ( !((name_type==SID_NAME_DOM_GRP) ||
288 ((name_type==SID_NAME_ALIAS) && domain->primary)) )
290 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
291 name_group, name_type));
292 return WINBINDD_ERROR;
295 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
296 DEBUG(1, ("error converting unix gid to sid\n"));
297 return WINBINDD_ERROR;
300 if (!fill_grent(&state->response.data.gr, name_domain,
301 name_group, gid) ||
302 !fill_grent_mem(domain, &group_sid, name_type,
303 &state->response.data.gr.num_gr_mem,
304 &gr_mem, &gr_mem_len)) {
305 return WINBINDD_ERROR;
308 /* Group membership lives at start of extra data */
310 state->response.data.gr.gr_mem_ofs = 0;
312 state->response.length += gr_mem_len;
313 state->response.extra_data = gr_mem;
315 return WINBINDD_OK;
318 /* Return a group structure from a gid number */
320 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
322 struct winbindd_domain *domain;
323 WINBINDD_GR *grp;
324 DOM_SID group_sid;
325 enum SID_NAME_USE name_type;
326 fstring dom_name;
327 fstring group_name;
328 int gr_mem_len;
329 char *gr_mem;
331 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
332 (unsigned long)state->request.data.gid));
334 /* Bug out if the gid isn't in the winbind range */
336 if ((state->request.data.gid < server_state.gid_low) ||
337 (state->request.data.gid > server_state.gid_high))
338 return WINBINDD_ERROR;
340 /* alway try local tdb lookup first */
341 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
342 char *buffer = NULL;
344 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
346 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
348 state->response.data.gr.gr_mem_ofs = 0;
349 state->response.length += gr_mem_len;
350 state->response.extra_data = buffer; /* give away the memory */
352 return WINBINDD_OK;
355 /* Get rid from gid */
356 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
357 DEBUG(1, ("could not convert gid %lu to rid\n",
358 (unsigned long)state->request.data.gid));
359 return WINBINDD_ERROR;
362 /* Get name from sid */
364 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
365 DEBUG(1, ("could not lookup sid\n"));
366 return WINBINDD_ERROR;
369 /* Fill in group structure */
371 domain = find_domain_from_sid(&group_sid);
373 if (!domain) {
374 DEBUG(1,("Can't find domain from sid\n"));
375 return WINBINDD_ERROR;
378 if ( !((name_type==SID_NAME_DOM_GRP) ||
379 ((name_type==SID_NAME_ALIAS) && domain->primary) ))
381 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
382 group_name, name_type));
383 return WINBINDD_ERROR;
386 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
387 state->request.data.gid) ||
388 !fill_grent_mem(domain, &group_sid, name_type,
389 &state->response.data.gr.num_gr_mem,
390 &gr_mem, &gr_mem_len))
391 return WINBINDD_ERROR;
393 /* Group membership lives at start of extra data */
395 state->response.data.gr.gr_mem_ofs = 0;
397 state->response.length += gr_mem_len;
398 state->response.extra_data = gr_mem;
400 return WINBINDD_OK;
404 * set/get/endgrent functions
407 /* "Rewind" file pointer for group database enumeration */
409 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
411 struct winbindd_domain *domain;
413 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
415 /* Check user has enabled this */
417 if (!lp_winbind_enum_groups())
418 return WINBINDD_ERROR;
420 /* Free old static data if it exists */
422 if (state->getgrent_state != NULL) {
423 free_getent_state(state->getgrent_state);
424 state->getgrent_state = NULL;
427 /* Create sam pipes for each domain we know about */
429 for (domain = domain_list(); domain != NULL; domain = domain->next) {
430 struct getent_state *domain_state;
433 /* don't add our domaina if we are a PDC or if we
434 are a member of a Samba domain */
436 if ( (IS_DC || lp_winbind_trusted_domains_only())
437 && domain->primary )
439 continue;
442 /* Create a state record for this domain */
444 if ((domain_state = (struct getent_state *)
445 malloc(sizeof(struct getent_state))) == NULL) {
446 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
447 return WINBINDD_ERROR;
450 ZERO_STRUCTP(domain_state);
452 fstrcpy(domain_state->domain_name, domain->name);
454 /* Add to list of open domains */
456 DLIST_ADD(state->getgrent_state, domain_state);
459 return WINBINDD_OK;
462 /* Close file pointer to ntdom group database */
464 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
466 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
468 free_getent_state(state->getgrent_state);
469 state->getgrent_state = NULL;
471 return WINBINDD_OK;
474 /* Get the list of domain groups and domain aliases for a domain. We fill in
475 the sam_entries and num_sam_entries fields with domain group information.
476 The dispinfo_ndx field is incremented to the index of the next group to
477 fetch. Return True if some groups were returned, False otherwise. */
479 #define MAX_FETCH_SAM_ENTRIES 100
481 static BOOL get_sam_group_entries(struct getent_state *ent)
483 NTSTATUS status;
484 uint32 num_entries;
485 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
486 TALLOC_CTX *mem_ctx;
487 BOOL result = False;
488 struct acct_info *sam_grp_entries = NULL;
489 struct winbindd_domain *domain;
491 if (ent->got_sam_entries)
492 return False;
494 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
495 ent->domain_name))) {
496 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
497 return False;
500 /* Free any existing group info */
502 SAFE_FREE(ent->sam_entries);
503 ent->num_sam_entries = 0;
504 ent->got_sam_entries = True;
506 /* Enumerate domain groups */
508 num_entries = 0;
510 if (!(domain = find_domain_from_name(ent->domain_name))) {
511 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
512 goto done;
515 /* always get the domain global groups */
517 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
519 if (!NT_STATUS_IS_OK(status)) {
520 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
521 result = False;
522 goto done;
525 /* Copy entries into return buffer */
527 if (num_entries) {
528 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
529 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
530 num_entries));
531 result = False;
532 goto done;
534 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
537 ent->num_sam_entries = num_entries;
539 /* get the domain local groups if we are a member of a native win2k domain
540 and are not using LDAP to get the groups */
542 if ( lp_security() != SEC_ADS && domain->native_mode
543 && domain->primary )
545 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
547 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
549 if ( !NT_STATUS_IS_OK(status) ) {
550 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
551 num_entries = 0;
553 else
554 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
556 /* Copy entries into return buffer */
558 if ( num_entries ) {
559 if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
561 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
562 num_entries));
563 result = False;
564 SAFE_FREE( name_list );
565 goto done;
568 name_list = tmp_name_list;
570 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
571 num_entries * sizeof(struct acct_info) );
574 ent->num_sam_entries += num_entries;
578 /* Fill in remaining fields */
580 ent->sam_entries = name_list;
581 ent->sam_entry_index = 0;
583 result = (ent->num_sam_entries > 0);
585 done:
586 talloc_destroy(mem_ctx);
588 return result;
591 /* Fetch next group entry from ntdom database */
593 #define MAX_GETGRENT_GROUPS 500
595 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
597 struct getent_state *ent;
598 struct winbindd_gr *group_list = NULL;
599 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
600 char *new_extra_data, *gr_mem_list = NULL;
602 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
604 /* Check user has enabled this */
606 if (!lp_winbind_enum_groups())
607 return WINBINDD_ERROR;
609 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
611 if ((state->response.extra_data =
612 malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
613 return WINBINDD_ERROR;
615 state->response.data.num_entries = 0;
617 group_list = (struct winbindd_gr *)state->response.extra_data;
619 if (!(ent = state->getgrent_state))
620 return WINBINDD_ERROR;
622 /* Start sending back groups */
624 for (i = 0; i < num_groups; i++) {
625 struct acct_info *name_list = NULL;
626 fstring domain_group_name;
627 uint32 result;
628 gid_t group_gid;
629 int gr_mem_len;
630 char *gr_mem, *new_gr_mem_list;
631 DOM_SID group_sid;
632 struct winbindd_domain *domain;
634 /* Do we need to fetch another chunk of groups? */
636 tryagain:
638 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
639 ent->sam_entry_index, ent->num_sam_entries));
641 if (ent->num_sam_entries == ent->sam_entry_index) {
643 while(ent && !get_sam_group_entries(ent)) {
644 struct getent_state *next_ent;
646 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
648 /* Free state information for this domain */
650 SAFE_FREE(ent->sam_entries);
652 next_ent = ent->next;
653 DLIST_REMOVE(state->getgrent_state, ent);
655 SAFE_FREE(ent);
656 ent = next_ent;
659 /* No more domains */
661 if (!ent)
662 break;
665 name_list = ent->sam_entries;
667 if (!(domain =
668 find_domain_from_name(ent->domain_name))) {
669 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
670 result = False;
671 goto done;
674 /* Lookup group info */
676 sid_copy(&group_sid, &domain->sid);
677 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
679 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
681 DEBUG(1, ("could not look up gid for group %s\n",
682 name_list[ent->sam_entry_index].acct_name));
684 ent->sam_entry_index++;
685 goto tryagain;
688 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
689 (unsigned long)name_list[ent->sam_entry_index].rid));
691 /* Fill in group entry */
693 fill_domain_username(domain_group_name, ent->domain_name,
694 name_list[ent->sam_entry_index].acct_name);
696 result = fill_grent(&group_list[group_list_ndx],
697 ent->domain_name,
698 name_list[ent->sam_entry_index].acct_name,
699 group_gid);
701 /* Fill in group membership entry */
703 if (result) {
704 DOM_SID member_sid;
705 group_list[group_list_ndx].num_gr_mem = 0;
706 gr_mem = NULL;
707 gr_mem_len = 0;
709 /* Get group membership */
710 if (state->request.cmd == WINBINDD_GETGRLST) {
711 result = True;
712 } else {
713 sid_copy(&member_sid, &domain->sid);
714 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
715 result = fill_grent_mem(
716 domain,
717 &member_sid,
718 SID_NAME_DOM_GRP,
719 &group_list[group_list_ndx].num_gr_mem,
720 &gr_mem, &gr_mem_len);
724 if (result) {
725 /* Append to group membership list */
726 new_gr_mem_list = Realloc(
727 gr_mem_list,
728 gr_mem_list_len + gr_mem_len);
730 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
731 DEBUG(0, ("out of memory\n"));
732 SAFE_FREE(gr_mem_list);
733 gr_mem_list_len = 0;
734 break;
737 DEBUG(10, ("list_len = %d, mem_len = %d\n",
738 gr_mem_list_len, gr_mem_len));
740 gr_mem_list = new_gr_mem_list;
742 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
743 gr_mem_len);
745 SAFE_FREE(gr_mem);
747 group_list[group_list_ndx].gr_mem_ofs =
748 gr_mem_list_len;
750 gr_mem_list_len += gr_mem_len;
753 ent->sam_entry_index++;
755 /* Add group to return list */
757 if (result) {
759 DEBUG(10, ("adding group num_entries = %d\n",
760 state->response.data.num_entries));
762 group_list_ndx++;
763 state->response.data.num_entries++;
765 state->response.length +=
766 sizeof(struct winbindd_gr);
768 } else {
769 DEBUG(0, ("could not lookup domain group %s\n",
770 domain_group_name));
774 /* Copy the list of group memberships to the end of the extra data */
776 if (group_list_ndx == 0)
777 goto done;
779 new_extra_data = Realloc(
780 state->response.extra_data,
781 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
783 if (!new_extra_data) {
784 DEBUG(0, ("out of memory\n"));
785 group_list_ndx = 0;
786 SAFE_FREE(state->response.extra_data);
787 SAFE_FREE(gr_mem_list);
789 return WINBINDD_ERROR;
792 state->response.extra_data = new_extra_data;
794 memcpy(&((char *)state->response.extra_data)
795 [group_list_ndx * sizeof(struct winbindd_gr)],
796 gr_mem_list, gr_mem_list_len);
798 SAFE_FREE(gr_mem_list);
800 state->response.length += gr_mem_list_len;
802 DEBUG(10, ("returning %d groups, length = %d\n",
803 group_list_ndx, gr_mem_list_len));
805 /* Out of domains */
807 done:
809 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
812 /* List domain groups without mapping to unix ids */
814 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
816 uint32 total_entries = 0;
817 struct winbindd_domain *domain;
818 const char *which_domain;
819 char *extra_data = NULL;
820 char *ted = NULL;
821 unsigned int extra_data_len = 0, i;
823 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
825 /* Ensure null termination */
826 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
827 which_domain = state->request.domain_name;
829 /* Enumerate over trusted domains */
831 for (domain = domain_list(); domain; domain = domain->next) {
832 struct getent_state groups;
834 /* if we have a domain name restricting the request and this
835 one in the list doesn't match, then just bypass the remainder
836 of the loop */
838 if ( *which_domain && !strequal(which_domain, domain->name) )
839 continue;
841 ZERO_STRUCT(groups);
843 /* Get list of sam groups */
845 fstrcpy(groups.domain_name, domain->name);
847 get_sam_group_entries(&groups);
849 if (groups.num_sam_entries == 0) {
850 /* this domain is empty or in an error state */
851 continue;
854 /* keep track the of the total number of groups seen so
855 far over all domains */
856 total_entries += groups.num_sam_entries;
858 /* Allocate some memory for extra data. Note that we limit
859 account names to sizeof(fstring) = 128 characters. */
860 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
862 if (!ted) {
863 DEBUG(0,("failed to enlarge buffer!\n"));
864 SAFE_FREE(extra_data);
865 return WINBINDD_ERROR;
866 } else
867 extra_data = ted;
869 /* Pack group list into extra data fields */
870 for (i = 0; i < groups.num_sam_entries; i++) {
871 char *group_name = ((struct acct_info *)
872 groups.sam_entries)[i].acct_name;
873 fstring name;
875 fill_domain_username(name, domain->name, group_name);
876 /* Append to extra data */
877 memcpy(&extra_data[extra_data_len], name,
878 strlen(name));
879 extra_data_len += strlen(name);
880 extra_data[extra_data_len++] = ',';
883 SAFE_FREE(groups.sam_entries);
886 /* Assign extra_data fields in response structure */
887 if (extra_data) {
888 extra_data[extra_data_len - 1] = '\0';
889 state->response.extra_data = extra_data;
890 state->response.length += extra_data_len;
893 /* No domains may have responded but that's still OK so don't
894 return an error. */
896 return WINBINDD_OK;
899 /* Get user supplementary groups. This is much quicker than trying to
900 invert the groups database. We merge the groups from the gids and
901 other_sids info3 fields as trusted domain, universal group
902 memberships, and nested groups (win2k native mode only) are not
903 returned by the getgroups RPC call but are present in the info3. */
905 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
907 fstring name_domain, name_user;
908 DOM_SID user_sid, group_sid;
909 enum SID_NAME_USE name_type;
910 uint32 num_groups = 0;
911 uint32 num_gids = 0;
912 NTSTATUS status;
913 DOM_SID **user_grpsids;
914 struct winbindd_domain *domain;
915 enum winbindd_result result = WINBINDD_ERROR;
916 gid_t *gid_list;
917 unsigned int i;
918 TALLOC_CTX *mem_ctx;
919 NET_USER_INFO_3 *info3 = NULL;
921 /* Ensure null termination */
922 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
924 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
925 state->request.data.username));
927 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
928 state->request.data.username)))
929 return WINBINDD_ERROR;
931 /* Parse domain and username */
933 parse_domain_user(state->request.data.username,
934 name_domain, name_user);
936 /* Get info for the domain */
938 if ((domain = find_domain_from_name(name_domain)) == NULL) {
939 DEBUG(7, ("could not find domain entry for domain %s\n",
940 name_domain));
941 goto done;
944 if ( domain->primary && lp_winbind_trusted_domains_only()) {
945 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
946 name_domain, name_user));
947 return WINBINDD_ERROR;
950 /* Get rid and name type from name. The following costs 1 packet */
952 if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
953 &name_type)) {
954 DEBUG(1, ("user '%s' does not exist\n", name_user));
955 goto done;
958 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
959 DEBUG(1, ("name '%s' is not a user name: %d\n",
960 name_user, name_type));
961 goto done;
964 /* Treat the info3 cache as authoritative as the
965 lookup_usergroups() function may return cached data. */
967 if ((info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
969 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
970 info3->num_groups2, info3->num_other_sids));
972 num_groups = info3->num_other_sids + info3->num_groups2;
973 gid_list = calloc(sizeof(gid_t), num_groups);
975 /* Go through each other sid and convert it to a gid */
977 for (i = 0; i < info3->num_other_sids; i++) {
978 fstring name;
979 fstring dom_name;
980 enum SID_NAME_USE sid_type;
982 /* Is this sid known to us? It can either be
983 a trusted domain sid or a foreign sid. */
985 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
986 dom_name, name, &sid_type))
988 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
989 sid_string_static(&info3->other_sids[i].sid)));
990 continue;
993 /* Check it is a domain group or an alias (domain local group)
994 in a win2k native mode domain. */
996 if ( !((sid_type==SID_NAME_DOM_GRP) ||
997 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
999 DEBUG(10, ("winbindd_getgroups: sid type %d "
1000 "for %s is not a domain group\n",
1001 sid_type,
1002 sid_string_static(
1003 &info3->other_sids[i].sid)));
1004 continue;
1007 /* Map to a gid */
1009 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&info3->other_sids[i].sid, &gid_list[num_gids], 0)) )
1011 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1012 sid_string_static(&info3->other_sids[i].sid)));
1013 continue;
1016 /* We've jumped through a lot of hoops to get here */
1018 DEBUG(10, ("winbindd_getgroups: mapped other sid %s to "
1019 "gid %lu\n", sid_string_static(
1020 &info3->other_sids[i].sid),
1021 (unsigned long)gid_list[num_gids]));
1023 num_gids++;
1026 for (i = 0; i < info3->num_groups2; i++) {
1028 /* create the group SID */
1030 sid_copy( &group_sid, &domain->sid );
1031 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1033 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid_list[num_gids], 0)) ) {
1034 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1035 sid_string_static(&group_sid)));
1038 num_gids++;
1041 SAFE_FREE(info3);
1043 } else {
1044 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1045 &user_sid, &num_groups,
1046 &user_grpsids);
1047 if (!NT_STATUS_IS_OK(status))
1048 goto done;
1050 gid_list = malloc(sizeof(gid_t) * num_groups);
1052 if (state->response.extra_data)
1053 goto done;
1055 for (i = 0; i < num_groups; i++) {
1056 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_grpsids[i], &gid_list[num_gids], 0))) {
1057 DEBUG(1, ("unable to convert group sid %s to gid\n",
1058 sid_string_static(user_grpsids[i])));
1059 continue;
1061 num_gids++;
1065 /* Send data back to client */
1067 state->response.data.num_entries = num_gids;
1068 state->response.extra_data = gid_list;
1069 state->response.length += num_gids * sizeof(gid_t);
1071 result = WINBINDD_OK;
1073 done:
1075 talloc_destroy(mem_ctx);
1077 return result;
1081 /* Get user supplementary sids. This is equivalent to the
1082 winbindd_getgroups() function but it involves a SID->SIDs mapping
1083 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1084 idmap. This call is designed to be used with applications that need
1085 to do ACL evaluation themselves. Note that the cached info3 data is
1086 not used
1088 this function assumes that the SID that comes in is a user SID. If
1089 you pass in another type of SID then you may get unpredictable
1090 results.
1092 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1094 DOM_SID user_sid;
1095 NTSTATUS status;
1096 DOM_SID **user_grpsids;
1097 struct winbindd_domain *domain;
1098 enum winbindd_result result = WINBINDD_ERROR;
1099 unsigned int i;
1100 TALLOC_CTX *mem_ctx;
1101 char *ret = NULL;
1102 uint32 num_groups;
1103 unsigned ofs, ret_size = 0;
1105 /* Ensure null termination */
1106 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1108 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1109 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1110 return WINBINDD_ERROR;
1113 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1114 state->request.data.username))) {
1115 return WINBINDD_ERROR;
1118 /* Get info for the domain */
1119 if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1120 DEBUG(0,("could not find domain entry for sid %s\n",
1121 sid_string_static(&user_sid)));
1122 goto done;
1125 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1126 &user_sid, &num_groups,
1127 &user_grpsids);
1128 if (!NT_STATUS_IS_OK(status))
1129 goto done;
1131 if (num_groups == 0) {
1132 goto no_groups;
1135 /* work out the response size */
1136 for (i = 0; i < num_groups; i++) {
1137 const char *s = sid_string_static(user_grpsids[i]);
1138 ret_size += strlen(s) + 1;
1141 /* build the reply */
1142 ret = malloc(ret_size);
1143 if (!ret) goto done;
1144 ofs = 0;
1145 for (i = 0; i < num_groups; i++) {
1146 const char *s = sid_string_static(user_grpsids[i]);
1147 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1148 ofs += strlen(ret+ofs) + 1;
1151 no_groups:
1152 /* Send data back to client */
1153 state->response.data.num_entries = num_groups;
1154 state->response.extra_data = ret;
1155 state->response.length += ret_size;
1156 result = WINBINDD_OK;
1158 done:
1159 talloc_destroy(mem_ctx);
1161 return result;