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 3 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, see <http://www.gnu.org/licenses/>.
29 #define DBGC_CLASS DBGC_WINBIND
31 static void add_member(const char *domain, const char *user,
32 char **pp_members, size_t *p_num_members)
37 fill_domain_username(name, domain, user, True);
41 safe_strcat(name, ",", sizeof(name)-1);
42 string_append(pp_members, name);
46 /**********************************************************************
47 Add member users resulting from sid. Expand if it is a domain group.
48 **********************************************************************/
50 static void add_expanded_sid(const DOM_SID *sid,
52 size_t *p_num_members)
56 struct winbindd_domain *domain;
59 char *domain_name = NULL;
61 enum lsa_SidType type;
70 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
72 if (mem_ctx == NULL) {
73 DEBUG(1, ("talloc_init failed\n"));
77 sid_copy(&dom_sid, sid);
78 sid_split_rid(&dom_sid, &rid);
80 domain = find_lookup_domain_from_sid(sid);
83 DEBUG(3, ("Could not find domain for sid %s\n",
84 sid_string_dbg(sid)));
88 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
89 &domain_name, &name, &type);
91 if (!NT_STATUS_IS_OK(result)) {
92 DEBUG(3, ("sid_to_name failed for sid %s\n",
93 sid_string_dbg(sid)));
97 DEBUG(10, ("Found name %s, type %d\n", name, type));
99 if (type == SID_NAME_USER) {
100 add_member(domain_name, name, pp_members, p_num_members);
104 if (type != SID_NAME_DOM_GRP) {
105 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
110 /* Expand the domain group, this must be done via the target domain */
112 domain = find_domain_from_sid(sid);
114 if (domain == NULL) {
115 DEBUG(3, ("Could not find domain from SID %s\n",
116 sid_string_dbg(sid)));
120 result = domain->methods->lookup_groupmem(domain, mem_ctx,
125 if (!NT_STATUS_IS_OK(result)) {
126 DEBUG(10, ("Could not lookup group members for %s: %s\n",
127 name, nt_errstr(result)));
131 for (i=0; i<num_names; i++) {
132 DEBUG(10, ("Adding group member SID %s\n",
133 sid_string_dbg(&sid_mem[i])));
135 if (types[i] != SID_NAME_USER) {
136 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
137 "Ignoring.\n", names[i], name));
141 add_member(NULL, names[i], pp_members, p_num_members);
145 talloc_destroy(mem_ctx);
149 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
150 DOM_SID *group_sid, size_t *num_gr_mem,
151 char **gr_mem, size_t *gr_mem_len)
154 size_t i, num_members;
160 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, talloc_tos(),
161 &members, &num_members)))
164 for (i=0; i<num_members; i++) {
165 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
168 TALLOC_FREE(members);
170 if (*gr_mem != NULL) {
173 /* We have at least one member, strip off the last "," */
174 len = strlen(*gr_mem);
175 (*gr_mem)[len-1] = '\0';
182 /* Fill a grent structure from various other information */
184 bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
185 const char *dom_name, const char *gr_name, gid_t unix_gid)
187 fstring full_group_name;
188 char *mapped_name = NULL;
189 struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
190 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
192 nt_status = normalize_name_map(mem_ctx, domain, gr_name,
195 /* Basic whitespace replacement */
196 if (NT_STATUS_IS_OK(nt_status)) {
197 fill_domain_username(full_group_name, dom_name,
200 /* Mapped to an aliase */
201 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
202 fstrcpy(full_group_name, mapped_name);
206 fill_domain_username( full_group_name, dom_name,
210 gr->gr_gid = unix_gid;
212 /* Group name and password */
214 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
215 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
220 /***********************************************************************
221 If "enum users" is set to false, and the group being looked
222 up is the Domain Users SID: S-1-5-domain-513, then for the
223 list of members check if the querying user is in that group,
224 and if so only return that user as the gr_mem array.
225 We can change this to a different parameter than "enum users"
226 if neccessaey, or parameterize the group list we do this for.
227 ***********************************************************************/
229 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
230 struct winbindd_domain *domain,
231 struct winbindd_cli_state *state,
233 enum lsa_SidType group_name_type,
234 size_t *num_gr_mem, char **gr_mem,
237 DOM_SID querying_user_sid;
238 DOM_SID *pquerying_user_sid = NULL;
239 uint32 num_groups = 0;
240 DOM_SID *user_sids = NULL;
241 bool u_in_group = False;
244 unsigned int buf_len = 0;
247 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
251 uid_t ret_uid = (uid_t)-1;
252 if (sys_getpeereid(state->sock, &ret_uid)==0) {
253 /* We know who's asking - look up their SID if
254 it's one we've mapped before. */
255 status = idmap_uid_to_sid(domain->name,
256 &querying_user_sid, ret_uid);
257 if (NT_STATUS_IS_OK(status)) {
258 pquerying_user_sid = &querying_user_sid;
259 DEBUG(10,("fill_grent_mem_domain_users: "
260 "querying uid %u -> %s\n",
261 (unsigned int)ret_uid,
262 sid_string_dbg(pquerying_user_sid)));
267 /* Only look up if it was a winbindd user in this domain. */
268 if (pquerying_user_sid &&
269 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
271 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
272 sid_string_dbg(pquerying_user_sid) ));
274 status = domain->methods->lookup_usergroups(domain,
279 if (!NT_STATUS_IS_OK(status)) {
280 DEBUG(1, ("fill_grent_mem_domain_users: "
281 "lookup_usergroups failed "
282 "for sid %s in domain %s (error: %s)\n",
283 sid_string_dbg(pquerying_user_sid),
289 for (i = 0; i < num_groups; i++) {
290 if (sid_equal(group_sid, &user_sids[i])) {
291 /* User is in Domain Users, add their name
292 as the only group member. */
301 char *domainname = NULL;
302 char *username = NULL;
304 char *mapped_name = NULL;
305 enum lsa_SidType type;
306 struct winbindd_domain *target_domain = NULL;
307 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
309 DEBUG(10,("fill_grent_mem_domain_users: "
310 "sid %s in 'Domain Users' in domain %s\n",
311 sid_string_dbg(pquerying_user_sid),
314 status = domain->methods->sid_to_name(domain, mem_ctx,
319 if (!NT_STATUS_IS_OK(status)) {
320 DEBUG(1, ("could not lookup username for user "
321 "sid %s in domain %s (error: %s)\n",
322 sid_string_dbg(pquerying_user_sid),
328 target_domain = find_domain_from_name_noinit(domainname);
329 name_map_status = normalize_name_map(mem_ctx, target_domain,
330 username, &mapped_name);
332 /* Basic whitespace replacement */
333 if (NT_STATUS_IS_OK(name_map_status)) {
334 fill_domain_username(name, domainname, mapped_name, true);
336 /* Mapped to an alias */
337 else if (NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
338 fstrcpy(name, mapped_name);
340 /* no mapping done...use original name */
342 fill_domain_username(name, domainname, username, true);
347 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
348 DEBUG(1, ("out of memory\n"));
351 memcpy(buf, name, buf_len);
353 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
354 "'Domain Users' in domain %s\n",
355 name, domain->name ));
357 /* user is the only member */
362 *gr_mem_len = buf_len;
364 DEBUG(10, ("fill_grent_mem_domain_users: "
365 "num_mem = %u, len = %u, mem = %s\n",
366 (unsigned int)*num_gr_mem,
367 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
372 /***********************************************************************
373 Add names to a list. Assumes a canonical version of the string
375 ***********************************************************************/
377 static int namecmp( const void *a, const void *b )
379 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
382 static void sort_unique_list(char ***list, uint32 *n_list)
386 /* search for duplicates for sorting and looking for matching
389 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
391 for (i=1; i < *n_list; i++) {
392 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
393 memmove(&((*list)[i-1]), &((*list)[i]),
394 sizeof(char*)*((*n_list)-i));
400 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
401 char ***list, uint32 *n_list,
402 char **names, uint32 n_names )
404 char **new_list = NULL;
405 uint32 n_new_list = 0;
408 if ( !names || (n_names == 0) )
411 /* Alloc the maximum size we'll need */
413 if ( *list == NULL ) {
414 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
415 return NT_STATUS_NO_MEMORY;
417 n_new_list = n_names;
419 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
420 (*n_list) + n_names );
422 return NT_STATUS_NO_MEMORY;
423 n_new_list = (*n_list) + n_names;
428 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
429 new_list[i] = talloc_strdup( new_list, names[j] );
433 *n_list = n_new_list;
438 /***********************************************************************
439 ***********************************************************************/
441 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
442 struct winbindd_domain *d,
443 DOM_SID *glist, uint32 n_glist,
444 DOM_SID **new_glist, uint32 *n_new_glist,
445 char ***members, uint32 *n_members )
448 NTSTATUS status = NT_STATUS_OK;
449 uint32 num_names = 0;
450 uint32 *name_types = NULL;
452 DOM_SID *sid_mem = NULL;
453 TALLOC_CTX *tmp_ctx = NULL;
454 DOM_SID *new_groups = NULL;
455 size_t new_groups_size = 0;
462 DEBUG(10,("expand_groups:\n"));
464 for ( i=0; i<n_glist; i++ ) {
466 NTSTATUS lookup_status;
468 tmp_ctx = talloc_new( ctx );
470 /* Lookup the group membership */
472 lookup_status = d->methods->lookup_groupmem(d, tmp_ctx,
473 &glist[i], &num_names,
476 if (!NT_STATUS_IS_OK(lookup_status)) {
477 DEBUG(10,("expand_groups: lookup_groupmem for "
478 "sid %s failed with: %s\n",
479 sid_string_dbg(&glist[i]),
480 nt_errstr(lookup_status)));
482 /* we might have hit a logic error when called for an
483 * alias, in that case just continue with group
484 * expansion - Guenther */
486 if (NT_STATUS_EQUAL(lookup_status, NT_STATUS_NO_SUCH_GROUP)) {
489 status = lookup_status;
493 /* Separate users and groups into two lists */
495 for ( j=0; j<num_names; j++ ) {
498 if ( name_types[j] == SID_NAME_USER ||
499 name_types[j] == SID_NAME_COMPUTER )
501 status = add_names_to_list( ctx, members,
504 if ( !NT_STATUS_IS_OK(status) )
511 if ( name_types[j] == SID_NAME_DOM_GRP ||
512 name_types[j] == SID_NAME_ALIAS )
514 status = add_sid_to_array_unique(ctx,
518 if (!NT_STATUS_IS_OK(status)) {
526 TALLOC_FREE( tmp_ctx );
529 *new_glist = new_groups;
530 *n_new_glist = (uint32)new_groups_size;
533 TALLOC_FREE( tmp_ctx );
535 if (!NT_STATUS_IS_OK(status)) {
536 DEBUG(10,("expand_groups: returning with %s\n",
543 /***********************************************************************
544 Fill in the group membership field of a NT group given by group_sid
545 ***********************************************************************/
547 static bool fill_grent_mem(struct winbindd_domain *domain,
548 struct winbindd_cli_state *state,
550 enum lsa_SidType group_name_type,
551 size_t *num_gr_mem, char **gr_mem,
554 uint32 num_names = 0;
555 unsigned int buf_len = 0, buf_ndx = 0, i;
556 char **names = NULL, *buf = NULL;
560 DOM_SID *glist = NULL;
561 DOM_SID *new_glist = NULL;
562 uint32 n_glist, n_new_glist;
563 int max_depth = lp_winbind_expand_groups();
565 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
568 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
570 /* Initialize with no members */
574 /* HACK ALERT!! This whole routine does not cope with group members
575 * from more than one domain, ie aliases. Thus we have to work it out
576 * ourselves in a special routine. */
578 if (domain->internal) {
579 result = fill_passdb_alias_grmem(domain, group_sid,
585 /* Verify name type */
587 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
588 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
590 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
591 sid_string_dbg(group_sid),
592 domain->name, group_name_type));
596 /* OPTIMIZATION / HACK. See comment in
597 fill_grent_mem_domusers() */
599 sid_peek_rid( group_sid, &group_rid );
600 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
601 result = fill_grent_mem_domusers( mem_ctx, domain, state,
602 group_sid, group_name_type,
608 /* Real work goes here. Create a list of group names to
609 expand starting with the initial one. Pass that to
610 expand_groups() which returns a list of more group names
611 to expand. Do this up to the max search depth. */
613 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
615 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
618 sid_copy( &glist[0], group_sid );
621 for ( i=0; i<max_depth && glist; i++ ) {
622 uint32 n_members = 0;
623 char **members = NULL;
627 nt_status = expand_groups( mem_ctx, domain,
629 &new_glist, &n_new_glist,
630 &members, &n_members);
631 if ( !NT_STATUS_IS_OK(nt_status) ) {
636 /* Add new group members to list. Pass through the
637 alias mapping function */
639 for (j=0; j<n_members; j++) {
640 fstring name_domain, name_acct;
641 fstring qualified_name;
642 char *mapped_name = NULL;
643 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
644 struct winbindd_domain *target_domain = NULL;
646 if (parse_domain_user(members[j], name_domain, name_acct)) {
647 target_domain = find_domain_from_name_noinit(name_domain);
650 if (!target_domain) {
651 target_domain = domain;
654 name_map_status = normalize_name_map(members, target_domain,
655 name_acct, &mapped_name);
657 /* Basic whitespace replacement */
658 if (NT_STATUS_IS_OK(name_map_status)) {
659 fill_domain_username(qualified_name, name_domain,
661 mapped_name = qualified_name;
663 /* no mapping at all */
664 else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
665 mapped_name = members[j];
668 nt_status = add_names_to_list( mem_ctx, &names,
671 if ( !NT_STATUS_IS_OK(nt_status) ) {
677 TALLOC_FREE( members );
679 /* If we have no more groups to expand, break out
682 if (new_glist == NULL)
688 n_glist = n_new_glist;
690 TALLOC_FREE( glist );
692 sort_unique_list(&names, &num_names);
694 DEBUG(10, ("looked up %d names\n", num_names));
697 /* Add members to list */
699 for (i = 0; i < num_names; i++) {
702 DEBUG(10, ("processing name %s\n", names[i]));
704 len = strlen(names[i]);
706 /* Add to list or calculate buffer length */
709 buf_len += len + 1; /* List is comma separated */
711 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
713 DEBUG(10, ("appending %s at ndx %d\n",
715 parse_add_domuser(&buf[buf_ndx], names[i], &len);
722 /* Allocate buffer */
724 if (!buf && buf_len != 0) {
725 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
726 DEBUG(1, ("out of memory\n"));
730 memset(buf, 0, buf_len);
736 if (buf && buf_ndx > 0) {
737 buf[buf_ndx - 1] = '\0';
741 *gr_mem_len = buf_len;
743 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
744 (unsigned int)*num_gr_mem,
745 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
750 talloc_destroy(mem_ctx);
752 DEBUG(10,("fill_grent_mem returning %s\n",
753 result == true ? "true" : "false"));
758 static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
760 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
761 enum lsa_SidType type )
763 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
766 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
767 request_error(state);
771 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
772 DEBUG(5,("getgrnam_recv: not a group!\n"));
773 request_error(state);
777 winbindd_getgrsid( state, *sid );
781 /* Return a group structure from a group name */
783 void winbindd_getgrnam(struct winbindd_cli_state *state)
785 struct winbindd_domain *domain;
786 fstring name_domain, name_group;
788 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
790 /* Ensure null termination */
791 state->request->data.groupname[sizeof(state->request->data.groupname)-1]='\0';
793 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
794 state->request->data.groupname));
796 nt_status = normalize_name_unmap(state->mem_ctx,
797 state->request->data.groupname,
799 /* If we didn't map anything in the above call, just reset the
800 tmp pointer to the original string */
801 if (!NT_STATUS_IS_OK(nt_status) &&
802 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
804 tmp = state->request->data.groupname;
807 /* Parse domain and groupname */
809 memset(name_group, 0, sizeof(name_group));
811 name_domain[0] = '\0';
812 name_group[0] = '\0';
814 parse_domain_user(tmp, name_domain, name_group);
816 /* if no domain or our local domain and no local tdb group, default to
817 * our local domain for aliases */
819 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
820 fstrcpy(name_domain, get_global_sam_name());
823 /* Get info for the domain */
825 if ((domain = find_domain_from_name_noinit(name_domain)) == NULL) {
826 DEBUG(3, ("could not get domain sid for domain %s\n",
828 request_error(state);
831 /* should we deal with users for our domain? */
833 if ( lp_winbind_trusted_domains_only() && domain->primary) {
834 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
835 "getgrnam() for %s\\%s.\n", name_domain, name_group));
836 request_error(state);
840 /* Get rid and name type from name */
842 fstrcpy( name_group, tmp );
844 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
845 getgrnam_recv, WINBINDD_GETGRNAM, state );
848 struct getgrsid_state {
849 struct winbindd_cli_state *state;
850 struct winbindd_domain *domain;
852 enum lsa_SidType group_type;
857 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
859 struct getgrsid_state *s =
860 (struct getgrsid_state *)private_data;
861 struct winbindd_domain *domain;
865 fstring dom_name, group_name;
868 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
869 request_error(s->state);
875 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
876 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
877 request_error(s->state);
882 /* Fill in group structure */
884 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
885 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
886 request_error(s->state);
890 if (!fill_grent(s->state->mem_ctx, &s->state->response->data.gr,
891 dom_name, group_name, gid) ||
892 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
893 &num_gr_mem, &gr_mem, &gr_mem_len))
895 request_error(s->state);
899 s->state->response->data.gr.num_gr_mem = (uint32)num_gr_mem;
901 /* Group membership lives at start of extra data */
903 s->state->response->data.gr.gr_mem_ofs = 0;
905 s->state->response->length += gr_mem_len;
906 s->state->response->extra_data.data = talloc_memdup(
907 s->state->mem_ctx, gr_mem, gr_mem_len);
908 if (s->state->response->extra_data.data == NULL) {
909 request_error(s->state);
913 request_ok(s->state);
916 static void getgrsid_lookupsid_recv( void *private_data, bool success,
917 const char *dom_name, const char *name,
918 enum lsa_SidType name_type )
920 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
921 char *mapped_name = NULL;
923 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
926 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
927 request_error(s->state);
931 /* either it's a domain group, a domain local group, or a
932 local group in an internal domain */
934 if ( !( (name_type==SID_NAME_DOM_GRP) ||
935 ((name_type==SID_NAME_ALIAS) &&
936 (s->domain->primary || s->domain->internal)) ) )
938 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
939 dom_name, name, name_type));
940 request_error(s->state);
944 /* normalize the name and ensure that we have the DOM\name
945 coming out of here */
947 fstrcpy(raw_name, name);
949 nt_status = normalize_name_unmap(s->state->mem_ctx, raw_name,
952 /* basic whitespace reversal */
953 if (NT_STATUS_IS_OK(nt_status)) {
954 s->group_name = talloc_asprintf(s->state->mem_ctx,
957 *lp_winbind_separator(),
960 /* mapped from alias */
961 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
962 s->group_name = mapped_name;
964 /* no mapping at all. use original string */
966 s->group_name = talloc_asprintf(s->state->mem_ctx,
969 *lp_winbind_separator(),
973 if (s->group_name == NULL) {
974 DEBUG(1, ("getgrsid_lookupsid_recv: group_name is NULL!\n"));
975 request_error(s->state);
979 s->group_type = name_type;
981 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
982 getgrsid_sid2gid_recv, s);
985 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
987 struct getgrsid_state *s;
989 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
990 DEBUG(0, ("talloc failed\n"));
991 request_error(state);
997 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
998 DEBUG(3, ("Could not find domain for sid %s\n",
999 sid_string_dbg(&group_sid)));
1000 request_error(state);
1004 sid_copy(&s->group_sid, &group_sid);
1006 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
1007 getgrsid_lookupsid_recv, s );
1011 * set/get/endgrent functions
1014 /* "Rewind" file pointer for group database enumeration */
1016 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1018 struct winbindd_domain *domain;
1020 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1022 /* Check user has enabled this */
1024 if (!lp_winbind_enum_groups()) {
1028 /* Free old static data if it exists */
1030 if (state->getgrent_state != NULL) {
1031 free_getent_state(state->getgrent_state);
1032 state->getgrent_state = NULL;
1035 /* Create sam pipes for each domain we know about */
1037 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1038 struct getent_state *domain_state;
1040 /* Create a state record for this domain */
1042 /* don't add our domaina if we are a PDC or if we
1043 are a member of a Samba domain */
1045 if ( lp_winbind_trusted_domains_only() && domain->primary )
1050 domain_state = SMB_MALLOC_P(struct getent_state);
1051 if (!domain_state) {
1052 DEBUG(1, ("winbindd_setgrent: "
1053 "malloc failed for domain_state!\n"));
1057 ZERO_STRUCTP(domain_state);
1059 fstrcpy(domain_state->domain_name, domain->name);
1061 /* Add to list of open domains */
1063 DLIST_ADD(state->getgrent_state, domain_state);
1066 state->getgrent_initialized = True;
1070 void winbindd_setgrent(struct winbindd_cli_state *state)
1072 if (winbindd_setgrent_internal(state)) {
1075 request_error(state);
1079 /* Close file pointer to ntdom group database */
1081 void winbindd_endgrent(struct winbindd_cli_state *state)
1083 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1085 free_getent_state(state->getgrent_state);
1086 state->getgrent_initialized = False;
1087 state->getgrent_state = NULL;
1091 /* Get the list of domain groups and domain aliases for a domain. We fill in
1092 the sam_entries and num_sam_entries fields with domain group information.
1093 Return True if some groups were returned, False otherwise. */
1095 bool get_sam_group_entries(struct getent_state *ent)
1099 struct acct_info *name_list = NULL;
1100 TALLOC_CTX *mem_ctx;
1101 bool result = False;
1102 struct acct_info *sam_grp_entries = NULL;
1103 struct winbindd_domain *domain;
1105 if (ent->got_sam_entries)
1108 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1109 ent->domain_name))) {
1110 DEBUG(1, ("get_sam_group_entries: "
1111 "could not create talloc context!\n"));
1115 /* Free any existing group info */
1117 SAFE_FREE(ent->sam_entries);
1118 ent->num_sam_entries = 0;
1119 ent->got_sam_entries = True;
1121 /* Enumerate domain groups */
1125 if (!(domain = find_domain_from_name(ent->domain_name))) {
1126 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1131 /* always get the domain global groups */
1133 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1136 if (!NT_STATUS_IS_OK(status)) {
1137 DEBUG(3, ("get_sam_group_entries: "
1138 "could not enumerate domain groups! Error: %s\n",
1139 nt_errstr(status)));
1144 /* Copy entries into return buffer */
1147 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1149 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1150 "memory for %d domain groups!\n",
1155 memcpy(name_list, sam_grp_entries,
1156 num_entries * sizeof(struct acct_info));
1159 ent->num_sam_entries = num_entries;
1161 /* get the domain local groups if we are a member of a native win2k
1162 * domain and are not using LDAP to get the groups */
1164 if ( ( lp_security() != SEC_ADS && domain->native_mode
1165 && domain->primary) || domain->internal )
1167 DEBUG(4,("get_sam_group_entries: %s domain; "
1168 "enumerating local groups as well\n",
1169 domain->native_mode ? "Native Mode 2k":
1170 "BUILTIN or local"));
1172 status = domain->methods->enum_local_groups(domain, mem_ctx,
1176 if ( !NT_STATUS_IS_OK(status) ) {
1177 DEBUG(3,("get_sam_group_entries: "
1178 "Failed to enumerate "
1179 "domain local groups with error %s!\n",
1180 nt_errstr(status)));
1184 DEBUG(4,("get_sam_group_entries: "
1185 "Returned %d local groups\n",
1188 /* Copy entries into return buffer */
1190 if ( num_entries ) {
1191 name_list = SMB_REALLOC_ARRAY(name_list,
1193 ent->num_sam_entries+
1196 DEBUG(0,("get_sam_group_entries: "
1197 "Failed to realloc more memory "
1198 "for %d local groups!\n",
1204 memcpy(&name_list[ent->num_sam_entries],
1206 num_entries * sizeof(struct acct_info));
1209 ent->num_sam_entries += num_entries;
1213 /* Fill in remaining fields */
1215 ent->sam_entries = name_list;
1216 ent->sam_entry_index = 0;
1218 result = (ent->num_sam_entries > 0);
1221 talloc_destroy(mem_ctx);
1226 /* Fetch next group entry from ntdom database */
1228 #define MAX_GETGRENT_GROUPS 500
1230 void winbindd_getgrent(struct winbindd_cli_state *state)
1232 struct getent_state *ent;
1233 struct winbindd_gr *group_list = NULL;
1234 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1235 char *gr_mem_list = NULL;
1237 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1239 /* Check user has enabled this */
1241 if (!lp_winbind_enum_groups()) {
1242 request_error(state);
1246 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request->data.num_entries);
1248 if (num_groups == 0) {
1249 request_error(state);
1253 group_list = talloc_zero_array(state->mem_ctx, struct winbindd_gr,
1256 request_error(state);
1259 state->response->extra_data.data = group_list;
1261 state->response->data.num_entries = 0;
1263 if (!state->getgrent_initialized)
1264 winbindd_setgrent_internal(state);
1266 if (!(ent = state->getgrent_state)) {
1267 request_error(state);
1271 /* Start sending back groups */
1273 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1274 struct acct_info *name_list = NULL;
1275 fstring domain_group_name;
1281 struct winbindd_domain *domain;
1283 /* Do we need to fetch another chunk of groups? */
1287 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1288 ent->sam_entry_index, ent->num_sam_entries));
1290 if (ent->num_sam_entries == ent->sam_entry_index) {
1292 while(ent && !get_sam_group_entries(ent)) {
1293 struct getent_state *next_ent;
1295 DEBUG(10, ("freeing state info for domain %s\n",
1298 /* Free state information for this domain */
1300 SAFE_FREE(ent->sam_entries);
1302 next_ent = ent->next;
1303 DLIST_REMOVE(state->getgrent_state, ent);
1309 /* No more domains */
1315 name_list = (struct acct_info *)ent->sam_entries;
1317 if (!(domain = find_domain_from_name(ent->domain_name))) {
1318 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1324 /* Lookup group info */
1326 sid_copy(&group_sid, &domain->sid);
1327 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1329 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config
1330 ? domain->name : "",
1331 &group_sid, &group_gid)))
1334 enum lsa_SidType type;
1336 DEBUG(10, ("SID %s not in idmap\n",
1337 sid_string_dbg(&group_sid)));
1339 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1340 DEBUG(1,("could not look up gid for group %s\n",
1341 name_list[ent->sam_entry_index].acct_name));
1342 ent->sam_entry_index++;
1346 if ((type != SID_NAME_DOM_GRP) &&
1347 (type != SID_NAME_ALIAS) &&
1348 (type != SID_NAME_WKN_GRP)) {
1349 DEBUG(1, ("Group %s is a %s, not a group\n",
1350 sid_type_lookup(type),
1351 name_list[ent->sam_entry_index].acct_name));
1352 ent->sam_entry_index++;
1358 DEBUG(10, ("got gid %lu for group %lu\n",
1359 (unsigned long)group_gid,
1360 (unsigned long)name_list[ent->sam_entry_index].rid));
1362 /* Fill in group entry */
1364 fill_domain_username(domain_group_name, ent->domain_name,
1365 name_list[ent->sam_entry_index].acct_name, True);
1367 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1369 name_list[ent->sam_entry_index].acct_name,
1372 /* Fill in group membership entry */
1375 size_t num_gr_mem = 0;
1377 group_list[group_list_ndx].num_gr_mem = 0;
1381 /* Get group membership */
1382 if (state->request->cmd == WINBINDD_GETGRLST) {
1385 sid_copy(&member_sid, &domain->sid);
1386 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1387 result = fill_grent_mem(
1393 &gr_mem, &gr_mem_len);
1395 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1400 /* Append to group membership list */
1401 gr_mem_list = (char *)SMB_REALLOC(
1402 gr_mem_list, gr_mem_list_len + gr_mem_len);
1405 (group_list[group_list_ndx].num_gr_mem != 0)) {
1406 DEBUG(0, ("out of memory\n"));
1407 gr_mem_list_len = 0;
1411 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1412 gr_mem_list_len, (unsigned int)gr_mem_len));
1414 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1419 group_list[group_list_ndx].gr_mem_ofs =
1422 gr_mem_list_len += gr_mem_len;
1425 ent->sam_entry_index++;
1427 /* Add group to return list */
1431 DEBUG(10, ("adding group num_entries = %d\n",
1432 state->response->data.num_entries));
1435 state->response->data.num_entries++;
1437 state->response->length +=
1438 sizeof(struct winbindd_gr);
1441 DEBUG(0, ("could not lookup domain group %s\n",
1442 domain_group_name));
1446 /* Copy the list of group memberships to the end of the extra data */
1448 if (group_list_ndx == 0)
1451 state->response->extra_data.data = talloc_realloc_size(
1452 state->mem_ctx, state->response->extra_data.data,
1453 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1455 if (!state->response->extra_data.data) {
1456 DEBUG(0, ("out of memory\n"));
1458 SAFE_FREE(gr_mem_list);
1459 request_error(state);
1463 memcpy(&((char *)state->response->extra_data.data)
1464 [group_list_ndx * sizeof(struct winbindd_gr)],
1465 gr_mem_list, gr_mem_list_len);
1467 state->response->length += gr_mem_list_len;
1469 DEBUG(10, ("returning %d groups, length = %d\n",
1470 group_list_ndx, gr_mem_list_len));
1472 /* Out of domains */
1476 SAFE_FREE(gr_mem_list);
1478 if (group_list_ndx > 0)
1481 request_error(state);
1484 /* List domain groups without mapping to unix ids */
1485 void winbindd_list_groups(struct winbindd_cli_state *state)
1487 winbindd_list_ent(state, LIST_GROUPS);
1490 /* Get user supplementary groups. This is much quicker than trying to
1491 invert the groups database. We merge the groups from the gids and
1492 other_sids info3 fields as trusted domain, universal group
1493 memberships, and nested groups (win2k native mode only) are not
1494 returned by the getgroups RPC call but are present in the info3. */
1496 struct getgroups_state {
1497 struct winbindd_cli_state *state;
1498 struct winbindd_domain *domain;
1503 const DOM_SID *token_sids;
1504 size_t i, num_token_sids;
1507 size_t num_token_gids;
1511 /* Get user supplementary sids. This is equivalent to the
1512 winbindd_getgroups() function but it involves a SID->SIDs mapping
1513 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1514 idmap. This call is designed to be used with applications that need
1515 to do ACL evaluation themselves. Note that the cached info3 data is
1518 this function assumes that the SID that comes in is a user SID. If
1519 you pass in another type of SID then you may get unpredictable
1523 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1526 void winbindd_getusersids(struct winbindd_cli_state *state)
1530 /* Ensure null termination */
1531 state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
1533 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1534 if (user_sid == NULL) {
1535 DEBUG(1, ("talloc failed\n"));
1536 request_error(state);
1540 if (!string_to_sid(user_sid, state->request->data.sid)) {
1541 DEBUG(1, ("Could not get convert sid %s from string\n",
1542 state->request->data.sid));
1543 request_error(state);
1547 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1551 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1554 struct winbindd_cli_state *state =
1555 (struct winbindd_cli_state *)private_data;
1557 unsigned ofs, ret_size = 0;
1561 request_error(state);
1565 /* work out the response size */
1566 for (i = 0; i < num_sids; i++) {
1568 sid_to_fstring(s, &sids[i]);
1569 ret_size += strlen(s) + 1;
1572 /* build the reply */
1573 ret = talloc_array(state->mem_ctx, char, ret_size);
1575 DEBUG(0, ("malloc failed\n"));
1576 request_error(state);
1580 for (i = 0; i < num_sids; i++) {
1582 sid_to_fstring(s, &sids[i]);
1583 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1584 ofs += strlen(ret+ofs) + 1;
1587 /* Send data back to client */
1588 state->response->data.num_entries = num_sids;
1589 state->response->extra_data.data = ret;
1590 state->response->length += ret_size;
1594 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1595 struct winbindd_cli_state *state)
1605 /* Ensure null termination */
1606 state->request->data.sid[sizeof(state->request->data.sid)-1]='\0';
1608 if (!string_to_sid(&user_sid, state->request->data.sid)) {
1609 DEBUG(1, ("Could not get convert sid %s from string\n",
1610 state->request->data.sid));
1611 return WINBINDD_ERROR;
1614 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1615 &user_sid, &num_groups,
1617 if (!NT_STATUS_IS_OK(status))
1618 return WINBINDD_ERROR;
1620 if (num_groups == 0) {
1621 state->response->data.num_entries = 0;
1622 state->response->extra_data.data = NULL;
1626 if (!print_sidlist(state->mem_ctx,
1628 &sidstring, &len)) {
1629 DEBUG(0, ("talloc failed\n"));
1630 return WINBINDD_ERROR;
1633 state->response->extra_data.data = sidstring;
1634 state->response->length += len+1;
1635 state->response->data.num_entries = num_groups;
1640 enum winbindd_result winbindd_dual_getsidaliases(struct winbindd_domain *domain,
1641 struct winbindd_cli_state *state)
1643 DOM_SID *sids = NULL;
1644 size_t num_sids = 0;
1645 char *sidstr = NULL;
1652 DEBUG(3, ("[%5lu]: getsidaliases\n", (unsigned long)state->pid));
1654 sidstr = state->request->extra_data.data;
1655 if (sidstr == NULL) {
1656 sidstr = talloc_strdup(state->mem_ctx, "\n"); /* No SID */
1658 DEBUG(0, ("Out of memory\n"));
1659 return WINBINDD_ERROR;
1663 DEBUG(10, ("Sidlist: %s\n", sidstr));
1665 if (!parse_sidlist(state->mem_ctx, sidstr, &sids, &num_sids)) {
1666 DEBUG(0, ("Could not parse SID list: %s\n", sidstr));
1667 return WINBINDD_ERROR;
1673 result = domain->methods->lookup_useraliases(domain,
1679 if (!NT_STATUS_IS_OK(result)) {
1680 DEBUG(3, ("Could not lookup_useraliases: %s\n",
1681 nt_errstr(result)));
1682 return WINBINDD_ERROR;
1689 DEBUG(10, ("Got %d aliases\n", num_aliases));
1691 for (i=0; i<num_aliases; i++) {
1693 DEBUGADD(10, (" rid %d\n", alias_rids[i]));
1694 sid_copy(&sid, &domain->sid);
1695 sid_append_rid(&sid, alias_rids[i]);
1696 result = add_sid_to_array(state->mem_ctx, &sid, &sids,
1698 if (!NT_STATUS_IS_OK(result)) {
1699 return WINBINDD_ERROR;
1704 if (!print_sidlist(state->mem_ctx, sids, num_sids, &sidstr, &len)) {
1705 DEBUG(0, ("Could not print_sidlist\n"));
1706 state->response->extra_data.data = NULL;
1707 return WINBINDD_ERROR;
1710 state->response->extra_data.data = NULL;
1713 state->response->extra_data.data = sidstr;
1714 DEBUG(10, ("aliases_list: %s\n",
1715 (char *)state->response->extra_data.data));
1716 state->response->length += len+1;
1717 state->response->data.num_entries = num_sids;
1723 struct getgr_countmem {
1728 static int getgr_calc_memberlen(DATA_BLOB key, void *data, void *priv)
1730 struct wbint_GroupMember *m = talloc_get_type_abort(
1731 data, struct wbint_GroupMember);
1732 struct getgr_countmem *buf = (struct getgr_countmem *)priv;
1735 buf->len += strlen(m->name) + 1;
1739 struct getgr_stringmem {
1744 static int getgr_unparse_members(DATA_BLOB key, void *data, void *priv)
1746 struct wbint_GroupMember *m = talloc_get_type_abort(
1747 data, struct wbint_GroupMember);
1748 struct getgr_stringmem *buf = (struct getgr_stringmem *)priv;
1751 len = strlen(m->name);
1753 memcpy(buf->buf + buf->ofs, m->name, len);
1755 buf->buf[buf->ofs] = ',';
1760 NTSTATUS winbindd_print_groupmembers(struct talloc_dict *members,
1761 TALLOC_CTX *mem_ctx,
1762 int *num_members, char **result)
1764 struct getgr_countmem c;
1765 struct getgr_stringmem m;
1771 res = talloc_dict_traverse(members, getgr_calc_memberlen, &c);
1773 DEBUG(5, ("talloc_dict_traverse failed\n"));
1774 return NT_STATUS_INTERNAL_ERROR;
1778 m.buf = talloc_array(mem_ctx, char, c.len);
1779 if (m.buf == NULL) {
1780 DEBUG(5, ("talloc failed\n"));
1781 return NT_STATUS_NO_MEMORY;
1784 res = talloc_dict_traverse(members, getgr_unparse_members, &m);
1786 DEBUG(5, ("talloc_dict_traverse failed\n"));
1788 return NT_STATUS_INTERNAL_ERROR;
1790 m.buf[c.len-1] = '\0';
1792 *num_members = c.num;
1794 return NT_STATUS_OK;