Use timegm, or our already existing replacement instead of timezone, as
[Samba/nascimento.git] / source3 / nsswitch / winbindd_group.c
blob3ee8c0877b5aec1ae7ffae03e79f9e8d3afb23da
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 extern BOOL opt_nocache;
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_WINBIND
33 /*********************************************************************
34 *********************************************************************/
36 static int gr_mem_buffer( char **buffer, char **members, int num_members )
38 int i;
39 int len = 0;
40 int idx = 0;
42 if ( num_members == 0 ) {
43 *buffer = NULL;
44 return 0;
47 for ( i=0; i<num_members; i++ )
48 len += strlen(members[i])+1;
50 *buffer = (char*)smb_xmalloc(len);
51 for ( i=0; i<num_members; i++ ) {
52 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]);
53 idx += strlen(members[i])+1;
55 /* terminate with NULL */
56 (*buffer)[len-1] = '\0';
58 return len;
61 /***************************************************************
62 Empty static struct for negative caching.
63 ****************************************************************/
65 /* Fill a grent structure from various other information */
67 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
68 const char *gr_name, gid_t unix_gid)
70 fstring full_group_name;
71 /* Fill in uid/gid */
72 fill_domain_username(full_group_name, dom_name, gr_name);
74 gr->gr_gid = unix_gid;
76 /* Group name and password */
78 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
79 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
81 return True;
84 /* Fill in the group membership field of a NT group given by group_sid */
86 static BOOL fill_grent_mem(struct winbindd_domain *domain,
87 DOM_SID *group_sid,
88 enum SID_NAME_USE group_name_type,
89 int *num_gr_mem, char **gr_mem, int *gr_mem_len)
91 DOM_SID **sid_mem = NULL;
92 uint32 num_names = 0;
93 uint32 *name_types = NULL;
94 unsigned int buf_len, buf_ndx, i;
95 char **names = NULL, *buf;
96 BOOL result = False;
97 TALLOC_CTX *mem_ctx;
98 NTSTATUS status;
99 fstring sid_string;
101 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
102 return False;
104 /* Initialise group membership information */
106 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
108 *num_gr_mem = 0;
110 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
111 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
113 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
114 sid_to_string(sid_string, group_sid), domain->name,
115 group_name_type));
116 goto done;
119 /* Lookup group members */
120 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
121 &sid_mem, &names, &name_types);
122 if (!NT_STATUS_IS_OK(status)) {
123 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
124 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
126 goto done;
129 DEBUG(10, ("looked up %d names\n", num_names));
131 if (DEBUGLEVEL >= 10) {
132 for (i = 0; i < num_names; i++)
133 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]),
134 name_types[i]));
137 /* Add members to list */
139 buf = NULL;
140 buf_len = buf_ndx = 0;
142 again:
144 for (i = 0; i < num_names; i++) {
145 char *the_name;
146 fstring name;
147 int len;
149 the_name = names[i];
151 DEBUG(10, ("processing name %s\n", the_name));
153 /* FIXME: need to cope with groups within groups. These
154 occur in Universal groups on a Windows 2000 native mode
155 server. */
157 /* make sure to allow machine accounts */
159 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
160 DEBUG(3, ("name %s isn't a domain user\n", the_name));
161 continue;
164 /* Append domain name */
166 fill_domain_username(name, domain->name, the_name);
168 len = strlen(name);
170 /* Add to list or calculate buffer length */
172 if (!buf) {
173 buf_len += len + 1; /* List is comma separated */
174 (*num_gr_mem)++;
175 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
176 } else {
177 DEBUG(10, ("appending %s at ndx %d\n", name, len));
178 safe_strcpy(&buf[buf_ndx], name, len);
179 buf_ndx += len;
180 buf[buf_ndx] = ',';
181 buf_ndx++;
185 /* Allocate buffer */
187 if (!buf && buf_len != 0) {
188 if (!(buf = malloc(buf_len))) {
189 DEBUG(1, ("out of memory\n"));
190 result = False;
191 goto done;
193 memset(buf, 0, buf_len);
194 goto again;
197 if (buf && buf_ndx > 0) {
198 buf[buf_ndx - 1] = '\0';
201 *gr_mem = buf;
202 *gr_mem_len = buf_len;
204 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem,
205 buf_len, *num_gr_mem ? buf : "NULL"));
206 result = True;
208 done:
210 talloc_destroy(mem_ctx);
212 DEBUG(10, ("fill_grent_mem returning %d\n", result));
214 return result;
217 /* Return a group structure from a group name */
219 enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state)
221 DOM_SID group_sid;
222 WINBINDD_GR *grp;
223 struct winbindd_domain *domain;
224 enum SID_NAME_USE name_type;
225 fstring name_domain, name_group;
226 char *tmp, *gr_mem;
227 int gr_mem_len;
228 gid_t gid;
230 /* Ensure null termination */
231 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
233 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
234 state->request.data.groupname));
236 /* Parse domain and groupname */
238 memset(name_group, 0, sizeof(fstring));
240 tmp = state->request.data.groupname;
242 parse_domain_user(tmp, name_domain, name_group);
244 /* if no domain or our local domain, then do a local tdb search */
246 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
247 char *buffer = NULL;
249 if ( !(grp=wb_getgrnam(name_group)) ) {
250 DEBUG(5,("winbindd_getgrnam: lookup for %s\\%s failed\n",
251 name_domain, name_group));
252 return WINBINDD_ERROR;
254 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
256 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
258 state->response.data.gr.gr_mem_ofs = 0;
259 state->response.length += gr_mem_len;
260 state->response.extra_data = buffer; /* give the memory away */
262 return WINBINDD_OK;
265 /* Get info for the domain */
267 if ((domain = find_domain_from_name(name_domain)) == NULL) {
268 DEBUG(3, ("could not get domain sid for domain %s\n",
269 name_domain));
270 return WINBINDD_ERROR;
272 /* should we deal with users for our domain? */
274 if ( lp_winbind_trusted_domains_only() && domain->primary) {
275 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
276 name_domain, name_group));
277 return WINBINDD_ERROR;
280 /* Get rid and name type from name */
282 if (!winbindd_lookup_sid_by_name(domain, name_group, &group_sid,
283 &name_type)) {
284 DEBUG(1, ("group %s in domain %s does not exist\n",
285 name_group, name_domain));
286 return WINBINDD_ERROR;
289 if ( !((name_type==SID_NAME_DOM_GRP) ||
290 ((name_type==SID_NAME_ALIAS) && domain->primary)) )
292 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
293 name_group, name_type));
294 return WINBINDD_ERROR;
297 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) {
298 DEBUG(1, ("error converting unix gid to sid\n"));
299 return WINBINDD_ERROR;
302 if (!fill_grent(&state->response.data.gr, name_domain,
303 name_group, gid) ||
304 !fill_grent_mem(domain, &group_sid, name_type,
305 &state->response.data.gr.num_gr_mem,
306 &gr_mem, &gr_mem_len)) {
307 return WINBINDD_ERROR;
310 /* Group membership lives at start of extra data */
312 state->response.data.gr.gr_mem_ofs = 0;
314 state->response.length += gr_mem_len;
315 state->response.extra_data = gr_mem;
317 return WINBINDD_OK;
320 /* Return a group structure from a gid number */
322 enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state)
324 struct winbindd_domain *domain;
325 WINBINDD_GR *grp;
326 DOM_SID group_sid;
327 enum SID_NAME_USE name_type;
328 fstring dom_name;
329 fstring group_name;
330 int gr_mem_len;
331 char *gr_mem;
333 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
334 (unsigned long)state->request.data.gid));
336 /* Bug out if the gid isn't in the winbind range */
338 if ((state->request.data.gid < server_state.gid_low) ||
339 (state->request.data.gid > server_state.gid_high))
340 return WINBINDD_ERROR;
342 /* alway try local tdb lookup first */
343 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) {
344 char *buffer = NULL;
346 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) );
348 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem );
350 state->response.data.gr.gr_mem_ofs = 0;
351 state->response.length += gr_mem_len;
352 state->response.extra_data = buffer; /* give away the memory */
354 return WINBINDD_OK;
357 /* Get rid from gid */
358 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) {
359 DEBUG(1, ("could not convert gid %lu to rid\n",
360 (unsigned long)state->request.data.gid));
361 return WINBINDD_ERROR;
364 /* Get name from sid */
366 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) {
367 DEBUG(1, ("could not lookup sid\n"));
368 return WINBINDD_ERROR;
371 /* Fill in group structure */
373 domain = find_domain_from_sid(&group_sid);
375 if (!domain) {
376 DEBUG(1,("Can't find domain from sid\n"));
377 return WINBINDD_ERROR;
380 if ( !((name_type==SID_NAME_DOM_GRP) ||
381 ((name_type==SID_NAME_ALIAS) && domain->primary) ))
383 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
384 group_name, name_type));
385 return WINBINDD_ERROR;
388 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
389 state->request.data.gid) ||
390 !fill_grent_mem(domain, &group_sid, name_type,
391 &state->response.data.gr.num_gr_mem,
392 &gr_mem, &gr_mem_len))
393 return WINBINDD_ERROR;
395 /* Group membership lives at start of extra data */
397 state->response.data.gr.gr_mem_ofs = 0;
399 state->response.length += gr_mem_len;
400 state->response.extra_data = gr_mem;
402 return WINBINDD_OK;
406 * set/get/endgrent functions
409 /* "Rewind" file pointer for group database enumeration */
411 enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state)
413 struct winbindd_domain *domain;
415 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
417 /* Check user has enabled this */
419 if (!lp_winbind_enum_groups())
420 return WINBINDD_ERROR;
422 /* Free old static data if it exists */
424 if (state->getgrent_state != NULL) {
425 free_getent_state(state->getgrent_state);
426 state->getgrent_state = NULL;
429 /* Create sam pipes for each domain we know about */
431 for (domain = domain_list(); domain != NULL; domain = domain->next) {
432 struct getent_state *domain_state;
435 /* don't add our domaina if we are a PDC or if we
436 are a member of a Samba domain */
438 if ( (IS_DC || lp_winbind_trusted_domains_only())
439 && domain->primary )
441 continue;
444 /* Create a state record for this domain */
446 if ((domain_state = (struct getent_state *)
447 malloc(sizeof(struct getent_state))) == NULL) {
448 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
449 return WINBINDD_ERROR;
452 ZERO_STRUCTP(domain_state);
454 fstrcpy(domain_state->domain_name, domain->name);
456 /* Add to list of open domains */
458 DLIST_ADD(state->getgrent_state, domain_state);
461 return WINBINDD_OK;
464 /* Close file pointer to ntdom group database */
466 enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state)
468 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
470 free_getent_state(state->getgrent_state);
471 state->getgrent_state = NULL;
473 return WINBINDD_OK;
476 /* Get the list of domain groups and domain aliases for a domain. We fill in
477 the sam_entries and num_sam_entries fields with domain group information.
478 The dispinfo_ndx field is incremented to the index of the next group to
479 fetch. Return True if some groups were returned, False otherwise. */
481 #define MAX_FETCH_SAM_ENTRIES 100
483 static BOOL get_sam_group_entries(struct getent_state *ent)
485 NTSTATUS status;
486 uint32 num_entries;
487 struct acct_info *name_list = NULL, *tmp_name_list = NULL;
488 TALLOC_CTX *mem_ctx;
489 BOOL result = False;
490 struct acct_info *sam_grp_entries = NULL;
491 struct winbindd_domain *domain;
493 if (ent->got_sam_entries)
494 return False;
496 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
497 ent->domain_name))) {
498 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
499 return False;
502 /* Free any existing group info */
504 SAFE_FREE(ent->sam_entries);
505 ent->num_sam_entries = 0;
506 ent->got_sam_entries = True;
508 /* Enumerate domain groups */
510 num_entries = 0;
512 if (!(domain = find_domain_from_name(ent->domain_name))) {
513 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
514 goto done;
517 /* always get the domain global groups */
519 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
521 if (!NT_STATUS_IS_OK(status)) {
522 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
523 result = False;
524 goto done;
527 /* Copy entries into return buffer */
529 if (num_entries) {
530 if ( !(name_list = malloc(sizeof(struct acct_info) * num_entries)) ) {
531 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
532 num_entries));
533 result = False;
534 goto done;
536 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
539 ent->num_sam_entries = num_entries;
541 /* get the domain local groups if we are a member of a native win2k domain
542 and are not using LDAP to get the groups */
544 if ( lp_security() != SEC_ADS && domain->native_mode
545 && domain->primary )
547 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
549 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
551 if ( !NT_STATUS_IS_OK(status) ) {
552 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
553 num_entries = 0;
555 else
556 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
558 /* Copy entries into return buffer */
560 if ( num_entries ) {
561 if ( !(tmp_name_list = Realloc( name_list, sizeof(struct acct_info) * (ent->num_sam_entries+num_entries))) )
563 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
564 num_entries));
565 result = False;
566 SAFE_FREE( name_list );
567 goto done;
570 name_list = tmp_name_list;
572 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
573 num_entries * sizeof(struct acct_info) );
576 ent->num_sam_entries += num_entries;
580 /* Fill in remaining fields */
582 ent->sam_entries = name_list;
583 ent->sam_entry_index = 0;
585 result = (ent->num_sam_entries > 0);
587 done:
588 talloc_destroy(mem_ctx);
590 return result;
593 /* Fetch next group entry from ntdom database */
595 #define MAX_GETGRENT_GROUPS 500
597 enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state)
599 struct getent_state *ent;
600 struct winbindd_gr *group_list = NULL;
601 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0;
602 char *new_extra_data, *gr_mem_list = NULL;
604 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
606 /* Check user has enabled this */
608 if (!lp_winbind_enum_groups())
609 return WINBINDD_ERROR;
611 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
613 if ((state->response.extra_data =
614 malloc(num_groups * sizeof(struct winbindd_gr))) == NULL)
615 return WINBINDD_ERROR;
617 state->response.data.num_entries = 0;
619 group_list = (struct winbindd_gr *)state->response.extra_data;
621 if (!(ent = state->getgrent_state))
622 return WINBINDD_ERROR;
624 /* Start sending back groups */
626 for (i = 0; i < num_groups; i++) {
627 struct acct_info *name_list = NULL;
628 fstring domain_group_name;
629 uint32 result;
630 gid_t group_gid;
631 int gr_mem_len;
632 char *gr_mem, *new_gr_mem_list;
633 DOM_SID group_sid;
634 struct winbindd_domain *domain;
636 /* Do we need to fetch another chunk of groups? */
638 tryagain:
640 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
641 ent->sam_entry_index, ent->num_sam_entries));
643 if (ent->num_sam_entries == ent->sam_entry_index) {
645 while(ent && !get_sam_group_entries(ent)) {
646 struct getent_state *next_ent;
648 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
650 /* Free state information for this domain */
652 SAFE_FREE(ent->sam_entries);
654 next_ent = ent->next;
655 DLIST_REMOVE(state->getgrent_state, ent);
657 SAFE_FREE(ent);
658 ent = next_ent;
661 /* No more domains */
663 if (!ent)
664 break;
667 name_list = ent->sam_entries;
669 if (!(domain =
670 find_domain_from_name(ent->domain_name))) {
671 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
672 result = False;
673 goto done;
676 /* Lookup group info */
678 sid_copy(&group_sid, &domain->sid);
679 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
681 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) {
683 DEBUG(1, ("could not look up gid for group %s\n",
684 name_list[ent->sam_entry_index].acct_name));
686 ent->sam_entry_index++;
687 goto tryagain;
690 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
691 (unsigned long)name_list[ent->sam_entry_index].rid));
693 /* Fill in group entry */
695 fill_domain_username(domain_group_name, ent->domain_name,
696 name_list[ent->sam_entry_index].acct_name);
698 result = fill_grent(&group_list[group_list_ndx],
699 ent->domain_name,
700 name_list[ent->sam_entry_index].acct_name,
701 group_gid);
703 /* Fill in group membership entry */
705 if (result) {
706 DOM_SID member_sid;
707 group_list[group_list_ndx].num_gr_mem = 0;
708 gr_mem = NULL;
709 gr_mem_len = 0;
711 /* Get group membership */
712 if (state->request.cmd == WINBINDD_GETGRLST) {
713 result = True;
714 } else {
715 sid_copy(&member_sid, &domain->sid);
716 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
717 result = fill_grent_mem(
718 domain,
719 &member_sid,
720 SID_NAME_DOM_GRP,
721 &group_list[group_list_ndx].num_gr_mem,
722 &gr_mem, &gr_mem_len);
726 if (result) {
727 /* Append to group membership list */
728 new_gr_mem_list = Realloc(
729 gr_mem_list,
730 gr_mem_list_len + gr_mem_len);
732 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
733 DEBUG(0, ("out of memory\n"));
734 SAFE_FREE(gr_mem_list);
735 gr_mem_list_len = 0;
736 break;
739 DEBUG(10, ("list_len = %d, mem_len = %d\n",
740 gr_mem_list_len, gr_mem_len));
742 gr_mem_list = new_gr_mem_list;
744 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
745 gr_mem_len);
747 SAFE_FREE(gr_mem);
749 group_list[group_list_ndx].gr_mem_ofs =
750 gr_mem_list_len;
752 gr_mem_list_len += gr_mem_len;
755 ent->sam_entry_index++;
757 /* Add group to return list */
759 if (result) {
761 DEBUG(10, ("adding group num_entries = %d\n",
762 state->response.data.num_entries));
764 group_list_ndx++;
765 state->response.data.num_entries++;
767 state->response.length +=
768 sizeof(struct winbindd_gr);
770 } else {
771 DEBUG(0, ("could not lookup domain group %s\n",
772 domain_group_name));
776 /* Copy the list of group memberships to the end of the extra data */
778 if (group_list_ndx == 0)
779 goto done;
781 new_extra_data = Realloc(
782 state->response.extra_data,
783 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
785 if (!new_extra_data) {
786 DEBUG(0, ("out of memory\n"));
787 group_list_ndx = 0;
788 SAFE_FREE(state->response.extra_data);
789 SAFE_FREE(gr_mem_list);
791 return WINBINDD_ERROR;
794 state->response.extra_data = new_extra_data;
796 memcpy(&((char *)state->response.extra_data)
797 [group_list_ndx * sizeof(struct winbindd_gr)],
798 gr_mem_list, gr_mem_list_len);
800 SAFE_FREE(gr_mem_list);
802 state->response.length += gr_mem_list_len;
804 DEBUG(10, ("returning %d groups, length = %d\n",
805 group_list_ndx, gr_mem_list_len));
807 /* Out of domains */
809 done:
811 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR;
814 /* List domain groups without mapping to unix ids */
816 enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state)
818 uint32 total_entries = 0;
819 struct winbindd_domain *domain;
820 const char *which_domain;
821 char *extra_data = NULL;
822 char *ted = NULL;
823 unsigned int extra_data_len = 0, i;
825 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
827 /* Ensure null termination */
828 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
829 which_domain = state->request.domain_name;
831 /* Enumerate over trusted domains */
833 for (domain = domain_list(); domain; domain = domain->next) {
834 struct getent_state groups;
836 /* if we have a domain name restricting the request and this
837 one in the list doesn't match, then just bypass the remainder
838 of the loop */
840 if ( *which_domain && !strequal(which_domain, domain->name) )
841 continue;
843 ZERO_STRUCT(groups);
845 /* Get list of sam groups */
847 fstrcpy(groups.domain_name, domain->name);
849 get_sam_group_entries(&groups);
851 if (groups.num_sam_entries == 0) {
852 /* this domain is empty or in an error state */
853 continue;
856 /* keep track the of the total number of groups seen so
857 far over all domains */
858 total_entries += groups.num_sam_entries;
860 /* Allocate some memory for extra data. Note that we limit
861 account names to sizeof(fstring) = 128 characters. */
862 ted = Realloc(extra_data, sizeof(fstring) * total_entries);
864 if (!ted) {
865 DEBUG(0,("failed to enlarge buffer!\n"));
866 SAFE_FREE(extra_data);
867 return WINBINDD_ERROR;
868 } else
869 extra_data = ted;
871 /* Pack group list into extra data fields */
872 for (i = 0; i < groups.num_sam_entries; i++) {
873 char *group_name = ((struct acct_info *)
874 groups.sam_entries)[i].acct_name;
875 fstring name;
877 fill_domain_username(name, domain->name, group_name);
878 /* Append to extra data */
879 memcpy(&extra_data[extra_data_len], name,
880 strlen(name));
881 extra_data_len += strlen(name);
882 extra_data[extra_data_len++] = ',';
885 SAFE_FREE(groups.sam_entries);
888 /* Assign extra_data fields in response structure */
889 if (extra_data) {
890 extra_data[extra_data_len - 1] = '\0';
891 state->response.extra_data = extra_data;
892 state->response.length += extra_data_len;
895 /* No domains may have responded but that's still OK so don't
896 return an error. */
898 return WINBINDD_OK;
901 /* Get user supplementary groups. This is much quicker than trying to
902 invert the groups database. We merge the groups from the gids and
903 other_sids info3 fields as trusted domain, universal group
904 memberships, and nested groups (win2k native mode only) are not
905 returned by the getgroups RPC call but are present in the info3. */
907 enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state)
909 fstring name_domain, name_user;
910 DOM_SID user_sid, group_sid;
911 enum SID_NAME_USE name_type;
912 uint32 num_groups = 0;
913 uint32 num_gids = 0;
914 NTSTATUS status;
915 DOM_SID **user_grpsids;
916 struct winbindd_domain *domain;
917 enum winbindd_result result = WINBINDD_ERROR;
918 gid_t *gid_list;
919 unsigned int i;
920 TALLOC_CTX *mem_ctx;
921 NET_USER_INFO_3 *info3 = NULL;
923 /* Ensure null termination */
924 state->request.data.username[sizeof(state->request.data.username)-1]='\0';
926 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
927 state->request.data.username));
929 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)",
930 state->request.data.username)))
931 return WINBINDD_ERROR;
933 /* Parse domain and username */
935 parse_domain_user(state->request.data.username,
936 name_domain, name_user);
938 /* Get info for the domain */
940 if ((domain = find_domain_from_name(name_domain)) == NULL) {
941 DEBUG(7, ("could not find domain entry for domain %s\n",
942 name_domain));
943 goto done;
946 if ( domain->primary && lp_winbind_trusted_domains_only()) {
947 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
948 name_domain, name_user));
949 return WINBINDD_ERROR;
952 /* Get rid and name type from name. The following costs 1 packet */
954 if (!winbindd_lookup_sid_by_name(domain, name_user, &user_sid,
955 &name_type)) {
956 DEBUG(1, ("user '%s' does not exist\n", name_user));
957 goto done;
960 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) {
961 DEBUG(1, ("name '%s' is not a user name: %d\n",
962 name_user, name_type));
963 goto done;
966 /* Treat the info3 cache as authoritative as the
967 lookup_usergroups() function may return cached data. */
969 if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) {
971 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
972 info3->num_groups2, info3->num_other_sids));
974 num_groups = info3->num_other_sids + info3->num_groups2;
975 gid_list = calloc(sizeof(gid_t), num_groups);
977 /* Go through each other sid and convert it to a gid */
979 for (i = 0; i < info3->num_other_sids; i++) {
980 fstring name;
981 fstring dom_name;
982 enum SID_NAME_USE sid_type;
984 /* Is this sid known to us? It can either be
985 a trusted domain sid or a foreign sid. */
987 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid,
988 dom_name, name, &sid_type))
990 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
991 sid_string_static(&info3->other_sids[i].sid)));
992 continue;
995 /* Check it is a domain group or an alias (domain local group)
996 in a win2k native mode domain. */
998 if ( !((sid_type==SID_NAME_DOM_GRP) ||
999 ((sid_type==SID_NAME_ALIAS) && domain->primary)) )
1001 DEBUG(10, ("winbindd_getgroups: sid type %d "
1002 "for %s is not a domain group\n",
1003 sid_type,
1004 sid_string_static(
1005 &info3->other_sids[i].sid)));
1006 continue;
1009 /* Map to a gid */
1011 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&info3->other_sids[i].sid, &gid_list[num_gids], 0)) )
1013 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1014 sid_string_static(&info3->other_sids[i].sid)));
1015 continue;
1018 /* We've jumped through a lot of hoops to get here */
1020 DEBUG(10, ("winbindd_getgroups: mapped other sid %s to "
1021 "gid %lu\n", sid_string_static(
1022 &info3->other_sids[i].sid),
1023 (unsigned long)gid_list[num_gids]));
1025 num_gids++;
1028 for (i = 0; i < info3->num_groups2; i++) {
1030 /* create the group SID */
1032 sid_copy( &group_sid, &domain->sid );
1033 sid_append_rid( &group_sid, info3->gids[i].g_rid );
1035 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid_list[num_gids], 0)) ) {
1036 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1037 sid_string_static(&group_sid)));
1040 num_gids++;
1043 SAFE_FREE(info3);
1045 } else {
1046 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1047 &user_sid, &num_groups,
1048 &user_grpsids);
1049 if (!NT_STATUS_IS_OK(status))
1050 goto done;
1052 gid_list = malloc(sizeof(gid_t) * num_groups);
1054 if (state->response.extra_data)
1055 goto done;
1057 for (i = 0; i < num_groups; i++) {
1058 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_grpsids[i], &gid_list[num_gids], 0))) {
1059 DEBUG(1, ("unable to convert group sid %s to gid\n",
1060 sid_string_static(user_grpsids[i])));
1061 continue;
1063 num_gids++;
1067 /* Send data back to client */
1069 state->response.data.num_entries = num_gids;
1070 state->response.extra_data = gid_list;
1071 state->response.length += num_gids * sizeof(gid_t);
1073 result = WINBINDD_OK;
1075 done:
1077 talloc_destroy(mem_ctx);
1079 return result;
1083 /* Get user supplementary sids. This is equivalent to the
1084 winbindd_getgroups() function but it involves a SID->SIDs mapping
1085 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1086 idmap. This call is designed to be used with applications that need
1087 to do ACL evaluation themselves. Note that the cached info3 data is
1088 not used
1090 this function assumes that the SID that comes in is a user SID. If
1091 you pass in another type of SID then you may get unpredictable
1092 results.
1094 enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state)
1096 DOM_SID user_sid;
1097 NTSTATUS status;
1098 DOM_SID **user_grpsids;
1099 struct winbindd_domain *domain;
1100 enum winbindd_result result = WINBINDD_ERROR;
1101 unsigned int i;
1102 TALLOC_CTX *mem_ctx;
1103 char *ret = NULL;
1104 uint32 num_groups;
1105 unsigned ofs, ret_size = 0;
1107 /* Ensure null termination */
1108 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1110 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1111 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid));
1112 return WINBINDD_ERROR;
1115 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)",
1116 state->request.data.username))) {
1117 return WINBINDD_ERROR;
1120 /* Get info for the domain */
1121 if ((domain = find_domain_from_sid(&user_sid)) == NULL) {
1122 DEBUG(0,("could not find domain entry for sid %s\n",
1123 sid_string_static(&user_sid)));
1124 goto done;
1127 status = domain->methods->lookup_usergroups(domain, mem_ctx,
1128 &user_sid, &num_groups,
1129 &user_grpsids);
1130 if (!NT_STATUS_IS_OK(status))
1131 goto done;
1133 if (num_groups == 0) {
1134 goto no_groups;
1137 /* work out the response size */
1138 for (i = 0; i < num_groups; i++) {
1139 const char *s = sid_string_static(user_grpsids[i]);
1140 ret_size += strlen(s) + 1;
1143 /* build the reply */
1144 ret = malloc(ret_size);
1145 if (!ret) goto done;
1146 ofs = 0;
1147 for (i = 0; i < num_groups; i++) {
1148 const char *s = sid_string_static(user_grpsids[i]);
1149 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1150 ofs += strlen(ret+ofs) + 1;
1153 no_groups:
1154 /* Send data back to client */
1155 state->response.data.num_entries = num_groups;
1156 state->response.extra_data = ret;
1157 state->response.length += ret_size;
1158 result = WINBINDD_OK;
1160 done:
1161 talloc_destroy(mem_ctx);
1163 return result;