VERSION: Raise version number up to 3.0.36.
[Samba.git] / source / nsswitch / winbindd_group.c
blobcc8bfb0c404a57a4d1033cb8af2c4b116cda3bfe
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 static void add_member(const char *domain, const char *user,
35 char **pp_members, size_t *p_num_members)
37 fstring name;
39 fill_domain_username(name, domain, user, True);
40 safe_strcat(name, ",", sizeof(name)-1);
41 string_append(pp_members, name);
42 *p_num_members += 1;
45 /**********************************************************************
46 Add member users resulting from sid. Expand if it is a domain group.
47 **********************************************************************/
49 static void add_expanded_sid(const DOM_SID *sid, char **pp_members, size_t *p_num_members)
51 DOM_SID dom_sid;
52 uint32 rid;
53 struct winbindd_domain *domain;
54 size_t i;
56 char *domain_name = NULL;
57 char *name = NULL;
58 enum lsa_SidType type;
60 uint32 num_names;
61 DOM_SID *sid_mem;
62 char **names;
63 uint32 *types;
65 NTSTATUS result;
67 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
69 if (mem_ctx == NULL) {
70 DEBUG(1, ("talloc_init failed\n"));
71 return;
74 sid_copy(&dom_sid, sid);
75 sid_split_rid(&dom_sid, &rid);
77 domain = find_lookup_domain_from_sid(sid);
79 if (domain == NULL) {
80 DEBUG(3, ("Could not find domain for sid %s\n",
81 sid_string_static(sid)));
82 goto done;
85 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
86 &domain_name, &name, &type);
88 if (!NT_STATUS_IS_OK(result)) {
89 DEBUG(3, ("sid_to_name failed for sid %s\n",
90 sid_string_static(sid)));
91 goto done;
94 DEBUG(10, ("Found name %s, type %d\n", name, type));
96 if (type == SID_NAME_USER) {
97 add_member(domain_name, name, pp_members, p_num_members);
98 goto done;
101 if (type != SID_NAME_DOM_GRP) {
102 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
103 name));
104 goto done;
107 /* Expand the domain group, this must be done via the target domain */
109 domain = find_domain_from_sid(sid);
111 if (domain == NULL) {
112 DEBUG(3, ("Could not find domain from SID %s\n",
113 sid_string_static(sid)));
114 goto done;
117 result = domain->methods->lookup_groupmem(domain, mem_ctx,
118 sid, &num_names,
119 &sid_mem, &names,
120 &types);
122 if (!NT_STATUS_IS_OK(result)) {
123 DEBUG(10, ("Could not lookup group members for %s: %s\n",
124 name, nt_errstr(result)));
125 goto done;
128 for (i=0; i<num_names; i++) {
129 DEBUG(10, ("Adding group member SID %s\n",
130 sid_string_static(&sid_mem[i])));
132 if (types[i] != SID_NAME_USER) {
133 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
134 "Ignoring.\n", names[i], name));
135 continue;
138 add_member(domain->name, names[i], pp_members, p_num_members);
141 done:
142 talloc_destroy(mem_ctx);
143 return;
146 static BOOL fill_passdb_alias_grmem(struct winbindd_domain *domain,
147 DOM_SID *group_sid,
148 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
150 DOM_SID *members;
151 size_t i, num_members;
153 *num_gr_mem = 0;
154 *gr_mem = NULL;
155 *gr_mem_len = 0;
157 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
158 &num_members)))
159 return True;
161 for (i=0; i<num_members; i++) {
162 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
165 TALLOC_FREE(members);
167 if (*gr_mem != NULL) {
168 size_t len;
170 /* We have at least one member, strip off the last "," */
171 len = strlen(*gr_mem);
172 (*gr_mem)[len-1] = '\0';
173 *gr_mem_len = len;
176 return True;
179 /* Fill a grent structure from various other information */
181 static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name,
182 const char *gr_name, gid_t unix_gid)
184 fstring full_group_name;
186 fill_domain_username( full_group_name, dom_name, gr_name, True );
188 gr->gr_gid = unix_gid;
190 /* Group name and password */
192 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
193 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
195 return True;
198 /* Fill in the group membership field of a NT group given by group_sid */
200 static BOOL fill_grent_mem(struct winbindd_domain *domain,
201 struct winbindd_cli_state *state,
202 DOM_SID *group_sid,
203 enum lsa_SidType group_name_type,
204 size_t *num_gr_mem, char **gr_mem, size_t *gr_mem_len)
206 DOM_SID *sid_mem = NULL;
207 uint32 num_names = 0;
208 uint32 *name_types = NULL;
209 unsigned int buf_len = 0, buf_ndx = 0, i;
210 char **names = NULL, *buf = NULL;
211 BOOL result = False;
212 TALLOC_CTX *mem_ctx;
213 NTSTATUS status;
214 uint32 group_rid;
215 fstring sid_string;
217 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
218 return False;
220 /* Initialise group membership information */
222 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid)));
224 /* Initialize with no members */
225 *num_gr_mem = 0;
227 /* HACK ALERT!! This whole routine does not cope with group members
228 * from more than one domain, ie aliases. Thus we have to work it out
229 * ourselves in a special routine. */
231 if (domain->internal) {
232 result = fill_passdb_alias_grmem(domain, group_sid,
233 num_gr_mem,
234 gr_mem, gr_mem_len);
235 goto done;
238 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
239 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
241 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
242 sid_to_string(sid_string, group_sid), domain->name,
243 group_name_type));
244 goto done;
247 /* OPTIMIZATION / HACK. */
248 /* If "enum users" is set to false, and the group being looked
249 up is the Domain Users SID: S-1-5-domain-513, then for the
250 list of members check if the querying user is in that group,
251 and if so only return that user as the gr_mem array.
252 We can change this to a different parameter than "enum users"
253 if neccessaey, or parameterize the group list we do this for. */
255 sid_peek_rid( group_sid, &group_rid );
256 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
257 DOM_SID querying_user_sid;
258 DOM_SID *pquerying_user_sid = NULL;
259 uint32 num_groups = 0;
260 DOM_SID *user_sids = NULL;
261 BOOL u_in_group = False;
263 DEBUG(10,("fill_grent_mem: optimized lookup for sid %s domain %s\n",
264 sid_to_string(sid_string, group_sid), domain->name ));
266 if (state) {
267 uid_t ret_uid = (uid_t)-1;
268 if (sys_getpeereid(state->sock, &ret_uid)==0) {
269 /* We know who's asking - look up their SID if
270 it's one we've mapped before. */
271 status = idmap_uid_to_sid(&querying_user_sid, ret_uid);
272 if (NT_STATUS_IS_OK(status)) {
273 pquerying_user_sid = &querying_user_sid;
274 DEBUG(10,("fill_grent_mem: querying uid %u -> %s\n",
275 (unsigned int)ret_uid,
276 sid_to_string(sid_string, pquerying_user_sid) ));
281 /* Only look up if it was a winbindd user in this domain. */
282 if (pquerying_user_sid &&
283 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
285 DEBUG(10,("fill_grent_mem: querying user = %s\n",
286 sid_to_string(sid_string, pquerying_user_sid) ));
288 status = domain->methods->lookup_usergroups(domain,
289 mem_ctx,
290 pquerying_user_sid,
291 &num_groups,
292 &user_sids);
293 if (!NT_STATUS_IS_OK(status)) {
294 DEBUG(1, ("fill_grent_mem: lookup_usergroups failed "
295 "for sid %s in domain %s (error: %s)\n",
296 sid_to_string(sid_string, pquerying_user_sid),
297 domain->name,
298 nt_errstr(status)));
299 goto done;
302 for (i = 0; i < num_groups; i++) {
303 if (sid_equal(group_sid, &user_sids[i])) {
304 /* User is in Domain Users, add their name
305 as the only group member. */
306 u_in_group = True;
307 break;
312 if (u_in_group) {
313 size_t len = 0;
314 char *domainname = NULL;
315 char *username = NULL;
316 fstring name;
317 enum lsa_SidType type;
319 DEBUG(10,("fill_grent_mem: sid %s in 'Domain Users' in domain %s\n",
320 sid_to_string(sid_string, pquerying_user_sid), domain->name ));
322 status = domain->methods->sid_to_name(domain, mem_ctx,
323 pquerying_user_sid,
324 &domainname,
325 &username,
326 &type);
327 if (!NT_STATUS_IS_OK(status)) {
328 DEBUG(1, ("could not lookup username for user "
329 "sid %s in domain %s (error: %s)\n",
330 sid_to_string(sid_string, pquerying_user_sid),
331 domain->name,
332 nt_errstr(status)));
333 goto done;
335 fill_domain_username(name, domain->name, username, True);
336 len = strlen(name);
337 buf_len = len + 1;
338 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
339 DEBUG(1, ("out of memory\n"));
340 goto done;
342 memcpy(buf, name, buf_len);
344 DEBUG(10,("fill_grent_mem: user %s in 'Domain Users' in domain %s\n",
345 name, domain->name ));
347 /* user is the only member */
348 *num_gr_mem = 1;
351 *gr_mem = buf;
352 *gr_mem_len = buf_len;
354 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
355 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
356 result = True;
357 goto done;
360 /* Lookup group members */
361 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names,
362 &sid_mem, &names, &name_types);
363 if (!NT_STATUS_IS_OK(status)) {
364 DEBUG(1, ("could not lookup membership for group sid %s in domain %s (error: %s)\n",
365 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status)));
366 goto done;
369 DEBUG(10, ("looked up %d names\n", num_names));
371 if (DEBUGLEVEL >= 10) {
372 for (i = 0; i < num_names; i++)
373 DEBUG(10, ("\t%20s %s %d\n", names[i],
374 sid_string_static(&sid_mem[i]),
375 name_types[i]));
378 /* Add members to list */
380 again:
382 for (i = 0; i < num_names; i++) {
383 char *the_name;
384 fstring name;
385 int len;
387 the_name = names[i];
389 DEBUG(10, ("processing name %s\n", the_name));
391 /* FIXME: need to cope with groups within groups. These
392 occur in Universal groups on a Windows 2000 native mode
393 server. */
395 /* make sure to allow machine accounts */
397 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) {
398 DEBUG(3, ("name %s isn't a domain user (%s)\n", the_name, sid_type_lookup(name_types[i])));
399 continue;
402 /* Append domain name */
404 fill_domain_username(name, domain->name, the_name, True);
406 len = strlen(name);
408 /* Add to list or calculate buffer length */
410 if (!buf) {
411 buf_len += len + 1; /* List is comma separated */
412 (*num_gr_mem)++;
413 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
414 } else {
415 DEBUG(10, ("appending %s at ndx %d\n", name, buf_ndx));
416 safe_strcpy(&buf[buf_ndx], name, len);
417 buf_ndx += len;
418 buf[buf_ndx] = ',';
419 buf_ndx++;
423 /* Allocate buffer */
425 if (!buf && buf_len != 0) {
426 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
427 DEBUG(1, ("out of memory\n"));
428 result = False;
429 goto done;
431 memset(buf, 0, buf_len);
432 goto again;
435 if (buf && buf_ndx > 0) {
436 buf[buf_ndx - 1] = '\0';
439 *gr_mem = buf;
440 *gr_mem_len = buf_len;
442 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n", (unsigned int)*num_gr_mem,
443 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
444 result = True;
446 done:
448 talloc_destroy(mem_ctx);
450 DEBUG(10, ("fill_grent_mem returning %d\n", result));
452 return result;
455 /* Return a group structure from a group name */
457 void winbindd_getgrnam(struct winbindd_cli_state *state)
459 DOM_SID group_sid, tmp_sid;
460 uint32 grp_rid;
461 struct winbindd_domain *domain;
462 enum lsa_SidType name_type;
463 fstring name_domain, name_group;
464 char *tmp, *gr_mem;
465 size_t gr_mem_len;
466 size_t num_gr_mem;
467 gid_t gid;
468 union unid_t id;
469 NTSTATUS status;
471 /* Ensure null termination */
472 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
474 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
475 state->request.data.groupname));
477 /* Parse domain and groupname */
479 memset(name_group, 0, sizeof(fstring));
481 tmp = state->request.data.groupname;
483 name_domain[0] = '\0';
484 name_group[0] = '\0';
486 parse_domain_user(tmp, name_domain, name_group);
488 /* if no domain or our local domain and no local tdb group, default to
489 * our local domain for aliases */
491 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
492 fstrcpy(name_domain, get_global_sam_name());
495 /* Get info for the domain */
497 if ((domain = find_domain_from_name(name_domain)) == NULL) {
498 DEBUG(3, ("could not get domain sid for domain %s\n",
499 name_domain));
500 request_error(state);
501 return;
503 /* should we deal with users for our domain? */
505 if ( lp_winbind_trusted_domains_only() && domain->primary) {
506 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
507 "getgrnam() for %s\\%s.\n", name_domain, name_group));
508 request_error(state);
509 return;
512 /* Get rid and name type from name */
514 ws_name_replace( name_group, WB_REPLACE_CHAR );
516 if (!winbindd_lookup_sid_by_name(state->mem_ctx, domain, domain->name,
517 name_group, &group_sid, &name_type)) {
518 DEBUG(1, ("group %s in domain %s does not exist\n",
519 name_group, name_domain));
520 request_error(state);
521 return;
524 if ( !((name_type==SID_NAME_DOM_GRP) ||
525 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
526 ((name_type==SID_NAME_ALIAS) && domain->internal) ||
527 ((name_type==SID_NAME_WKN_GRP) && domain->internal)) )
529 DEBUG(1, ("name '%s' is not a local, domain or builtin "
530 "group: %d\n", name_group, name_type));
531 request_error(state);
532 return;
535 /* Make sure that the group SID is within the domain of the
536 original domain */
538 sid_copy( &tmp_sid, &group_sid );
539 sid_split_rid( &tmp_sid, &grp_rid );
540 if ( !sid_equal( &tmp_sid, &domain->sid ) ) {
541 DEBUG(3,("winbindd_getgrnam: group %s resolves to a SID in the wrong domain [%s]\n",
542 state->request.data.groupname, sid_string_static(&group_sid)));
543 request_error(state);
544 return;
549 /* Try to get the GID */
551 status = idmap_sid_to_gid(&group_sid, &gid);
553 if (NT_STATUS_IS_OK(status)) {
554 goto got_gid;
557 /* Maybe it's one of our aliases in passdb */
559 if (pdb_sid_to_id(&group_sid, &id, &name_type) &&
560 ((name_type == SID_NAME_ALIAS) ||
561 (name_type == SID_NAME_WKN_GRP))) {
562 gid = id.gid;
563 goto got_gid;
566 DEBUG(1, ("error converting unix gid to sid\n"));
567 request_error(state);
568 return;
570 got_gid:
572 if (!fill_grent(&state->response.data.gr, name_domain,
573 name_group, gid) ||
574 !fill_grent_mem(domain, state, &group_sid, name_type,
575 &num_gr_mem,
576 &gr_mem, &gr_mem_len)) {
577 request_error(state);
578 return;
581 state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
583 /* Group membership lives at start of extra data */
585 state->response.data.gr.gr_mem_ofs = 0;
587 state->response.length += gr_mem_len;
588 state->response.extra_data.data = gr_mem;
589 request_ok(state);
592 static void getgrgid_got_sid(struct winbindd_cli_state *state, DOM_SID group_sid)
594 struct winbindd_domain *domain;
595 enum lsa_SidType name_type;
596 char *dom_name;
597 char *group_name;
598 size_t gr_mem_len;
599 size_t num_gr_mem;
600 char *gr_mem;
602 /* Get name from sid */
604 if (!winbindd_lookup_name_by_sid(state->mem_ctx, &group_sid, &dom_name,
605 &group_name, &name_type)) {
606 DEBUG(1, ("could not lookup sid\n"));
607 request_error(state);
608 TALLOC_FREE(group_name);
609 TALLOC_FREE(dom_name);
610 return;
613 /* Fill in group structure */
615 domain = find_domain_from_sid_noinit(&group_sid);
617 if (!domain) {
618 DEBUG(1,("Can't find domain from sid\n"));
619 request_error(state);
620 TALLOC_FREE(group_name);
621 TALLOC_FREE(dom_name);
622 return;
625 if ( !((name_type==SID_NAME_DOM_GRP) ||
626 ((name_type==SID_NAME_ALIAS) && domain->primary) ||
627 ((name_type==SID_NAME_ALIAS) && domain->internal)) )
629 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
630 group_name, name_type));
631 request_error(state);
632 TALLOC_FREE(group_name);
633 TALLOC_FREE(dom_name);
634 return;
637 if (!fill_grent(&state->response.data.gr, dom_name, group_name,
638 state->request.data.gid) ||
639 !fill_grent_mem(domain, state, &group_sid, name_type,
640 &num_gr_mem,
641 &gr_mem, &gr_mem_len)) {
642 request_error(state);
643 TALLOC_FREE(group_name);
644 TALLOC_FREE(dom_name);
645 return;
648 state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
650 /* Group membership lives at start of extra data */
652 state->response.data.gr.gr_mem_ofs = 0;
654 state->response.length += gr_mem_len;
655 state->response.extra_data.data = gr_mem;
657 TALLOC_FREE(group_name);
658 TALLOC_FREE(dom_name);
660 request_ok(state);
663 static void getgrgid_recv(void *private_data, BOOL success, const char *sid)
665 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
666 enum lsa_SidType name_type;
667 DOM_SID group_sid;
669 if (success) {
670 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
671 (unsigned long)(state->request.data.gid), sid));
673 string_to_sid(&group_sid, sid);
674 getgrgid_got_sid(state, group_sid);
675 return;
678 /* Ok, this might be "ours", i.e. an alias */
679 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
680 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
681 (name_type == SID_NAME_ALIAS)) {
682 /* Hey, got an alias */
683 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
684 (unsigned long)(state->request.data.gid), sid));
685 getgrgid_got_sid(state, group_sid);
686 return;
689 DEBUG(1, ("could not convert gid %lu to sid\n",
690 (unsigned long)state->request.data.gid));
691 request_error(state);
694 /* Return a group structure from a gid number */
695 void winbindd_getgrgid(struct winbindd_cli_state *state)
697 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid,
698 (unsigned long)state->request.data.gid));
700 /* always use the async interface */
701 winbindd_gid2sid_async(state->mem_ctx, state->request.data.gid, getgrgid_recv, state);
705 * set/get/endgrent functions
708 /* "Rewind" file pointer for group database enumeration */
710 static BOOL winbindd_setgrent_internal(struct winbindd_cli_state *state)
712 struct winbindd_domain *domain;
714 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
716 /* Check user has enabled this */
718 if (!lp_winbind_enum_groups()) {
719 return False;
722 /* Free old static data if it exists */
724 if (state->getgrent_state != NULL) {
725 free_getent_state(state->getgrent_state);
726 state->getgrent_state = NULL;
729 /* Create sam pipes for each domain we know about */
731 for (domain = domain_list(); domain != NULL; domain = domain->next) {
732 struct getent_state *domain_state;
734 /* Create a state record for this domain */
736 /* don't add our domaina if we are a PDC or if we
737 are a member of a Samba domain */
739 if ( lp_winbind_trusted_domains_only() && domain->primary )
741 continue;
745 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) {
746 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
747 return False;
750 ZERO_STRUCTP(domain_state);
752 fstrcpy(domain_state->domain_name, domain->name);
754 /* Add to list of open domains */
756 DLIST_ADD(state->getgrent_state, domain_state);
759 state->getgrent_initialized = True;
760 return True;
763 void winbindd_setgrent(struct winbindd_cli_state *state)
765 if (winbindd_setgrent_internal(state)) {
766 request_ok(state);
767 } else {
768 request_error(state);
772 /* Close file pointer to ntdom group database */
774 void winbindd_endgrent(struct winbindd_cli_state *state)
776 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
778 free_getent_state(state->getgrent_state);
779 state->getgrent_initialized = False;
780 state->getgrent_state = NULL;
781 request_ok(state);
784 /* Get the list of domain groups and domain aliases for a domain. We fill in
785 the sam_entries and num_sam_entries fields with domain group information.
786 The dispinfo_ndx field is incremented to the index of the next group to
787 fetch. Return True if some groups were returned, False otherwise. */
789 static BOOL get_sam_group_entries(struct getent_state *ent)
791 NTSTATUS status;
792 uint32 num_entries;
793 struct acct_info *name_list = NULL;
794 TALLOC_CTX *mem_ctx;
795 BOOL result = False;
796 struct acct_info *sam_grp_entries = NULL;
797 struct winbindd_domain *domain;
799 if (ent->got_sam_entries)
800 return False;
802 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
803 ent->domain_name))) {
804 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
805 return False;
808 /* Free any existing group info */
810 SAFE_FREE(ent->sam_entries);
811 ent->num_sam_entries = 0;
812 ent->got_sam_entries = True;
814 /* Enumerate domain groups */
816 num_entries = 0;
818 if (!(domain = find_domain_from_name(ent->domain_name))) {
819 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name));
820 goto done;
823 /* always get the domain global groups */
825 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
827 if (!NT_STATUS_IS_OK(status)) {
828 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status)));
829 result = False;
830 goto done;
833 /* Copy entries into return buffer */
835 if (num_entries) {
836 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) {
837 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
838 num_entries));
839 result = False;
840 goto done;
842 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) );
845 ent->num_sam_entries = num_entries;
847 /* get the domain local groups if we are a member of a native win2k domain
848 and are not using LDAP to get the groups */
850 if ( ( lp_security() != SEC_ADS && domain->native_mode
851 && domain->primary) || domain->internal )
853 DEBUG(4,("get_sam_group_entries: %s domain; enumerating local groups as well\n",
854 domain->native_mode ? "Native Mode 2k":"BUILTIN or local"));
856 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries);
858 if ( !NT_STATUS_IS_OK(status) ) {
859 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
860 num_entries = 0;
862 else
863 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries));
865 /* Copy entries into return buffer */
867 if ( num_entries ) {
868 if ( !(name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) )
870 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
871 num_entries));
872 result = False;
873 goto done;
876 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries,
877 num_entries * sizeof(struct acct_info) );
880 ent->num_sam_entries += num_entries;
884 /* Fill in remaining fields */
886 ent->sam_entries = name_list;
887 ent->sam_entry_index = 0;
889 result = (ent->num_sam_entries > 0);
891 done:
892 talloc_destroy(mem_ctx);
894 return result;
897 /* Fetch next group entry from ntdom database */
899 #define MAX_GETGRENT_GROUPS 500
901 void winbindd_getgrent(struct winbindd_cli_state *state)
903 struct getent_state *ent;
904 struct winbindd_gr *group_list = NULL;
905 int num_groups, group_list_ndx, gr_mem_list_len = 0;
906 char *gr_mem_list = NULL;
908 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
910 /* Check user has enabled this */
912 if (!lp_winbind_enum_groups()) {
913 request_error(state);
914 return;
917 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
919 if (num_groups == 0) {
920 request_error(state);
921 return;
924 if ((state->response.extra_data.data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) {
925 request_error(state);
926 return;
929 memset(state->response.extra_data.data, '\0',
930 num_groups * sizeof(struct winbindd_gr) );
932 state->response.data.num_entries = 0;
934 group_list = (struct winbindd_gr *)state->response.extra_data.data;
936 if (!state->getgrent_initialized)
937 winbindd_setgrent_internal(state);
939 if (!(ent = state->getgrent_state)) {
940 request_error(state);
941 return;
944 /* Start sending back groups */
946 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
947 struct acct_info *name_list = NULL;
948 fstring domain_group_name;
949 uint32 result;
950 gid_t group_gid;
951 size_t gr_mem_len;
952 char *gr_mem;
953 DOM_SID group_sid;
954 struct winbindd_domain *domain;
956 /* Do we need to fetch another chunk of groups? */
958 tryagain:
960 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
961 ent->sam_entry_index, ent->num_sam_entries));
963 if (ent->num_sam_entries == ent->sam_entry_index) {
965 while(ent && !get_sam_group_entries(ent)) {
966 struct getent_state *next_ent;
968 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name));
970 /* Free state information for this domain */
972 SAFE_FREE(ent->sam_entries);
974 next_ent = ent->next;
975 DLIST_REMOVE(state->getgrent_state, ent);
977 SAFE_FREE(ent);
978 ent = next_ent;
981 /* No more domains */
983 if (!ent)
984 break;
987 name_list = (struct acct_info *)ent->sam_entries;
989 if (!(domain =
990 find_domain_from_name(ent->domain_name))) {
991 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name));
992 result = False;
993 goto done;
996 /* Lookup group info */
998 sid_copy(&group_sid, &domain->sid);
999 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1001 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid))) {
1002 union unid_t id;
1003 enum lsa_SidType type;
1005 DEBUG(10, ("SID %s not in idmap\n",
1006 sid_string_static(&group_sid)));
1008 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1009 DEBUG(1, ("could not look up gid for group "
1010 "%s\n",
1011 name_list[ent->sam_entry_index].acct_name));
1012 ent->sam_entry_index++;
1013 goto tryagain;
1016 if ((type != SID_NAME_DOM_GRP) &&
1017 (type != SID_NAME_ALIAS) &&
1018 (type != SID_NAME_WKN_GRP)) {
1019 DEBUG(1, ("Group %s is a %s, not a group\n",
1020 sid_type_lookup(type),
1021 name_list[ent->sam_entry_index].acct_name));
1022 ent->sam_entry_index++;
1023 goto tryagain;
1025 group_gid = id.gid;
1028 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid,
1029 (unsigned long)name_list[ent->sam_entry_index].rid));
1031 /* Fill in group entry */
1033 fill_domain_username(domain_group_name, ent->domain_name,
1034 name_list[ent->sam_entry_index].acct_name, True);
1036 result = fill_grent(&group_list[group_list_ndx],
1037 ent->domain_name,
1038 name_list[ent->sam_entry_index].acct_name,
1039 group_gid);
1041 /* Fill in group membership entry */
1043 if (result) {
1044 size_t num_gr_mem = 0;
1045 DOM_SID member_sid;
1046 group_list[group_list_ndx].num_gr_mem = 0;
1047 gr_mem = NULL;
1048 gr_mem_len = 0;
1050 /* Get group membership */
1051 if (state->request.cmd == WINBINDD_GETGRLST) {
1052 result = True;
1053 } else {
1054 sid_copy(&member_sid, &domain->sid);
1055 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1056 result = fill_grent_mem(
1057 domain,
1058 NULL,
1059 &member_sid,
1060 SID_NAME_DOM_GRP,
1061 &num_gr_mem,
1062 &gr_mem, &gr_mem_len);
1064 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1068 if (result) {
1069 /* Append to group membership list */
1070 gr_mem_list = (char *)SMB_REALLOC(
1071 gr_mem_list, gr_mem_list_len + gr_mem_len);
1073 if (!gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) {
1074 DEBUG(0, ("out of memory\n"));
1075 gr_mem_list_len = 0;
1076 break;
1079 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1080 gr_mem_list_len, (unsigned int)gr_mem_len));
1082 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1083 gr_mem_len);
1085 SAFE_FREE(gr_mem);
1087 group_list[group_list_ndx].gr_mem_ofs =
1088 gr_mem_list_len;
1090 gr_mem_list_len += gr_mem_len;
1093 ent->sam_entry_index++;
1095 /* Add group to return list */
1097 if (result) {
1099 DEBUG(10, ("adding group num_entries = %d\n",
1100 state->response.data.num_entries));
1102 group_list_ndx++;
1103 state->response.data.num_entries++;
1105 state->response.length +=
1106 sizeof(struct winbindd_gr);
1108 } else {
1109 DEBUG(0, ("could not lookup domain group %s\n",
1110 domain_group_name));
1114 /* Copy the list of group memberships to the end of the extra data */
1116 if (group_list_ndx == 0)
1117 goto done;
1119 state->response.extra_data.data = SMB_REALLOC(
1120 state->response.extra_data.data,
1121 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1123 if (!state->response.extra_data.data) {
1124 DEBUG(0, ("out of memory\n"));
1125 group_list_ndx = 0;
1126 SAFE_FREE(gr_mem_list);
1127 request_error(state);
1128 return;
1131 memcpy(&((char *)state->response.extra_data.data)
1132 [group_list_ndx * sizeof(struct winbindd_gr)],
1133 gr_mem_list, gr_mem_list_len);
1135 state->response.length += gr_mem_list_len;
1137 DEBUG(10, ("returning %d groups, length = %d\n",
1138 group_list_ndx, gr_mem_list_len));
1140 /* Out of domains */
1142 done:
1144 SAFE_FREE(gr_mem_list);
1146 if (group_list_ndx > 0)
1147 request_ok(state);
1148 else
1149 request_error(state);
1152 /* List domain groups without mapping to unix ids */
1154 void winbindd_list_groups(struct winbindd_cli_state *state)
1156 uint32 total_entries = 0;
1157 struct winbindd_domain *domain;
1158 const char *which_domain;
1159 char *extra_data = NULL;
1160 unsigned int extra_data_len = 0, i;
1162 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid));
1164 /* Ensure null termination */
1165 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0';
1166 which_domain = state->request.domain_name;
1168 /* Enumerate over trusted domains */
1170 for (domain = domain_list(); domain; domain = domain->next) {
1171 struct getent_state groups;
1173 /* if we have a domain name restricting the request and this
1174 one in the list doesn't match, then just bypass the remainder
1175 of the loop */
1177 if ( *which_domain && !strequal(which_domain, domain->name) )
1178 continue;
1180 ZERO_STRUCT(groups);
1182 /* Get list of sam groups */
1184 fstrcpy(groups.domain_name, domain->name);
1186 get_sam_group_entries(&groups);
1188 if (groups.num_sam_entries == 0) {
1189 /* this domain is empty or in an error state */
1190 continue;
1193 /* keep track the of the total number of groups seen so
1194 far over all domains */
1195 total_entries += groups.num_sam_entries;
1197 /* Allocate some memory for extra data. Note that we limit
1198 account names to sizeof(fstring) = 128 characters. */
1199 extra_data = (char *)SMB_REALLOC(
1200 extra_data, sizeof(fstring) * total_entries);
1202 if (!extra_data) {
1203 DEBUG(0,("failed to enlarge buffer!\n"));
1204 request_error(state);
1205 return;
1208 /* Pack group list into extra data fields */
1209 for (i = 0; i < groups.num_sam_entries; i++) {
1210 char *group_name = ((struct acct_info *)
1211 groups.sam_entries)[i].acct_name;
1212 fstring name;
1214 fill_domain_username(name, domain->name, group_name, True);
1215 /* Append to extra data */
1216 memcpy(&extra_data[extra_data_len], name,
1217 strlen(name));
1218 extra_data_len += strlen(name);
1219 extra_data[extra_data_len++] = ',';
1222 SAFE_FREE(groups.sam_entries);
1225 /* Assign extra_data fields in response structure */
1226 if (extra_data) {
1227 extra_data[extra_data_len - 1] = '\0';
1228 state->response.extra_data.data = extra_data;
1229 state->response.length += extra_data_len;
1232 /* No domains may have responded but that's still OK so don't
1233 return an error. */
1235 request_ok(state);
1238 /* Get user supplementary groups. This is much quicker than trying to
1239 invert the groups database. We merge the groups from the gids and
1240 other_sids info3 fields as trusted domain, universal group
1241 memberships, and nested groups (win2k native mode only) are not
1242 returned by the getgroups RPC call but are present in the info3. */
1244 struct getgroups_state {
1245 struct winbindd_cli_state *state;
1246 struct winbindd_domain *domain;
1247 char *domname;
1248 char *username;
1249 DOM_SID user_sid;
1251 const DOM_SID *token_sids;
1252 size_t i, num_token_sids;
1254 gid_t *token_gids;
1255 size_t num_token_gids;
1258 static void getgroups_usersid_recv(void *private_data, BOOL success,
1259 const DOM_SID *sid, enum lsa_SidType type);
1260 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1261 DOM_SID *token_sids, size_t num_token_sids);
1262 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid);
1264 void winbindd_getgroups(struct winbindd_cli_state *state)
1266 struct getgroups_state *s;
1268 /* Ensure null termination */
1269 state->request.data.username
1270 [sizeof(state->request.data.username)-1]='\0';
1272 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1273 state->request.data.username));
1275 /* Parse domain and username */
1277 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1278 if (s == NULL) {
1279 DEBUG(0, ("talloc failed\n"));
1280 request_error(state);
1281 return;
1284 s->state = state;
1286 ws_name_return( state->request.data.username, WB_REPLACE_CHAR );
1288 if (!parse_domain_user_talloc(state->mem_ctx,
1289 state->request.data.username,
1290 &s->domname, &s->username)) {
1291 DEBUG(5, ("Could not parse domain user: %s\n",
1292 state->request.data.username));
1294 /* error out if we do not have nested group support */
1296 if ( !lp_winbind_nested_groups() ) {
1297 request_error(state);
1298 return;
1301 s->domname = talloc_strdup( state->mem_ctx, get_global_sam_name() );
1302 s->username = talloc_strdup( state->mem_ctx, state->request.data.username );
1305 /* Get info for the domain */
1307 s->domain = find_domain_from_name_noinit(s->domname);
1309 if (s->domain == NULL) {
1310 DEBUG(7, ("could not find domain entry for domain %s\n",
1311 s->domname));
1312 request_error(state);
1313 return;
1316 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1317 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1318 "getgroups() for %s\\%s.\n", s->domname,
1319 s->username));
1320 request_error(state);
1321 return;
1324 /* Get rid and name type from name. The following costs 1 packet */
1326 winbindd_lookupname_async(state->mem_ctx, s->domname, s->username,
1327 getgroups_usersid_recv, s);
1330 static void getgroups_usersid_recv(void *private_data, BOOL success,
1331 const DOM_SID *sid, enum lsa_SidType type)
1333 struct getgroups_state *s =
1334 (struct getgroups_state *)private_data;
1336 if ((!success) ||
1337 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1338 request_error(s->state);
1339 return;
1342 sid_copy(&s->user_sid, sid);
1344 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1345 getgroups_tokensids_recv, s);
1348 static void getgroups_tokensids_recv(void *private_data, BOOL success,
1349 DOM_SID *token_sids, size_t num_token_sids)
1351 struct getgroups_state *s =
1352 (struct getgroups_state *)private_data;
1354 /* We need at least the user sid and the primary group in the token,
1355 * otherwise it's an error */
1357 if ((!success) || (num_token_sids < 2)) {
1358 request_error(s->state);
1359 return;
1362 s->token_sids = token_sids;
1363 s->num_token_sids = num_token_sids;
1364 s->i = 0;
1366 s->token_gids = NULL;
1367 s->num_token_gids = 0;
1369 getgroups_sid2gid_recv(s, False, 0);
1372 static void getgroups_sid2gid_recv(void *private_data, BOOL success, gid_t gid)
1374 struct getgroups_state *s =
1375 (struct getgroups_state *)private_data;
1377 if (success) {
1378 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1379 &s->token_gids,
1380 &s->num_token_gids)) {
1381 return;
1385 if (s->i < s->num_token_sids) {
1386 const DOM_SID *sid = &s->token_sids[s->i];
1387 s->i += 1;
1389 if (sid_equal(sid, &s->user_sid)) {
1390 getgroups_sid2gid_recv(s, False, 0);
1391 return;
1394 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1395 getgroups_sid2gid_recv, s);
1396 return;
1399 s->state->response.data.num_entries = s->num_token_gids;
1400 /* s->token_gids are talloced */
1401 if (s->num_token_gids != 0) {
1402 s->state->response.extra_data.data = smb_xmemdup(
1403 s->token_gids, s->num_token_gids * sizeof(gid_t));
1404 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1406 request_ok(s->state);
1409 /* Get user supplementary sids. This is equivalent to the
1410 winbindd_getgroups() function but it involves a SID->SIDs mapping
1411 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1412 idmap. This call is designed to be used with applications that need
1413 to do ACL evaluation themselves. Note that the cached info3 data is
1414 not used
1416 this function assumes that the SID that comes in is a user SID. If
1417 you pass in another type of SID then you may get unpredictable
1418 results.
1421 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1422 size_t num_sids);
1424 void winbindd_getusersids(struct winbindd_cli_state *state)
1426 DOM_SID *user_sid;
1428 /* Ensure null termination */
1429 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1431 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1432 if (user_sid == NULL) {
1433 DEBUG(1, ("talloc failed\n"));
1434 request_error(state);
1435 return;
1438 if (!string_to_sid(user_sid, state->request.data.sid)) {
1439 DEBUG(1, ("Could not get convert sid %s from string\n",
1440 state->request.data.sid));
1441 request_error(state);
1442 return;
1445 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1446 state);
1449 static void getusersids_recv(void *private_data, BOOL success, DOM_SID *sids,
1450 size_t num_sids)
1452 struct winbindd_cli_state *state =
1453 (struct winbindd_cli_state *)private_data;
1454 char *ret = NULL;
1455 unsigned ofs, ret_size = 0;
1456 size_t i;
1458 if (!success) {
1459 request_error(state);
1460 return;
1463 /* work out the response size */
1464 for (i = 0; i < num_sids; i++) {
1465 const char *s = sid_string_static(&sids[i]);
1466 ret_size += strlen(s) + 1;
1469 /* build the reply */
1470 ret = (char *)SMB_MALLOC(ret_size);
1471 if (!ret) {
1472 DEBUG(0, ("malloc failed\n"));
1473 request_error(state);
1474 return;
1476 ofs = 0;
1477 for (i = 0; i < num_sids; i++) {
1478 const char *s = sid_string_static(&sids[i]);
1479 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1480 ofs += strlen(ret+ofs) + 1;
1483 /* Send data back to client */
1484 state->response.data.num_entries = num_sids;
1485 state->response.extra_data.data = ret;
1486 state->response.length += ret_size;
1487 request_ok(state);
1490 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1492 DOM_SID user_sid;
1493 struct winbindd_domain *domain;
1495 /* Ensure null termination */
1496 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1498 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1499 DEBUG(1, ("Could not get convert sid %s from string\n",
1500 state->request.data.sid));
1501 request_error(state);
1502 return;
1505 /* Get info for the domain */
1506 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1507 DEBUG(0,("could not find domain entry for sid %s\n",
1508 sid_string_static(&user_sid)));
1509 request_error(state);
1510 return;
1513 sendto_domain(state, domain);
1516 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1517 struct winbindd_cli_state *state)
1519 DOM_SID user_sid;
1520 NTSTATUS status;
1522 char *sidstring;
1523 ssize_t len;
1524 DOM_SID *groups;
1525 uint32 num_groups;
1527 /* Ensure null termination */
1528 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1530 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1531 DEBUG(1, ("Could not get convert sid %s from string\n",
1532 state->request.data.sid));
1533 return WINBINDD_ERROR;
1536 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1537 &user_sid, &num_groups,
1538 &groups);
1539 if (!NT_STATUS_IS_OK(status))
1540 return WINBINDD_ERROR;
1542 if (num_groups == 0) {
1543 state->response.data.num_entries = 0;
1544 state->response.extra_data.data = NULL;
1545 return WINBINDD_OK;
1548 if (!print_sidlist(state->mem_ctx, groups, num_groups, &sidstring, &len)) {
1549 DEBUG(0, ("talloc failed\n"));
1550 return WINBINDD_ERROR;
1553 state->response.extra_data.data = SMB_STRDUP(sidstring);
1554 if (!state->response.extra_data.data) {
1555 return WINBINDD_ERROR;
1557 state->response.length += len+1;
1558 state->response.data.num_entries = num_groups;
1560 return WINBINDD_OK;