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/>.
28 extern bool opt_nocache;
31 #define DBGC_CLASS DBGC_WINBIND
33 static void add_member(const char *domain, const char *user,
34 char **pp_members, size_t *p_num_members)
39 fill_domain_username(name, domain, user, True);
43 safe_strcat(name, ",", sizeof(name)-1);
44 string_append(pp_members, name);
48 /**********************************************************************
49 Add member users resulting from sid. Expand if it is a domain group.
50 **********************************************************************/
52 static void add_expanded_sid(const DOM_SID *sid,
54 size_t *p_num_members)
58 struct winbindd_domain *domain;
61 char *domain_name = NULL;
63 enum lsa_SidType type;
72 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
74 if (mem_ctx == NULL) {
75 DEBUG(1, ("talloc_init failed\n"));
79 sid_copy(&dom_sid, sid);
80 sid_split_rid(&dom_sid, &rid);
82 domain = find_lookup_domain_from_sid(sid);
85 DEBUG(3, ("Could not find domain for sid %s\n",
86 sid_string_dbg(sid)));
90 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
91 &domain_name, &name, &type);
93 if (!NT_STATUS_IS_OK(result)) {
94 DEBUG(3, ("sid_to_name failed for sid %s\n",
95 sid_string_dbg(sid)));
99 DEBUG(10, ("Found name %s, type %d\n", name, type));
101 if (type == SID_NAME_USER) {
102 add_member(domain_name, name, pp_members, p_num_members);
106 if (type != SID_NAME_DOM_GRP) {
107 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
112 /* Expand the domain group, this must be done via the target domain */
114 domain = find_domain_from_sid(sid);
116 if (domain == NULL) {
117 DEBUG(3, ("Could not find domain from SID %s\n",
118 sid_string_dbg(sid)));
122 result = domain->methods->lookup_groupmem(domain, mem_ctx,
127 if (!NT_STATUS_IS_OK(result)) {
128 DEBUG(10, ("Could not lookup group members for %s: %s\n",
129 name, nt_errstr(result)));
133 for (i=0; i<num_names; i++) {
134 DEBUG(10, ("Adding group member SID %s\n",
135 sid_string_dbg(&sid_mem[i])));
137 if (types[i] != SID_NAME_USER) {
138 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
139 "Ignoring.\n", names[i], name));
143 add_member(NULL, names[i], pp_members, p_num_members);
147 talloc_destroy(mem_ctx);
151 static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
152 DOM_SID *group_sid, size_t *num_gr_mem,
153 char **gr_mem, size_t *gr_mem_len)
156 size_t i, num_members;
162 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
166 for (i=0; i<num_members; i++) {
167 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
170 TALLOC_FREE(members);
172 if (*gr_mem != NULL) {
175 /* We have at least one member, strip off the last "," */
176 len = strlen(*gr_mem);
177 (*gr_mem)[len-1] = '\0';
184 /* Fill a grent structure from various other information */
186 static bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
187 const char *dom_name,
188 char *gr_name, gid_t unix_gid)
190 fstring full_group_name;
191 char *mapped_name = NULL;
192 struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
193 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
195 nt_status = normalize_name_map(mem_ctx, domain, gr_name,
198 /* Basic whitespace replacement */
199 if (NT_STATUS_IS_OK(nt_status)) {
200 fill_domain_username(full_group_name, dom_name,
203 /* Mapped to an aliase */
204 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
205 fstrcpy(full_group_name, mapped_name);
209 fill_domain_username( full_group_name, dom_name,
213 gr->gr_gid = unix_gid;
215 /* Group name and password */
217 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
218 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
223 /***********************************************************************
224 If "enum users" is set to false, and the group being looked
225 up is the Domain Users SID: S-1-5-domain-513, then for the
226 list of members check if the querying user is in that group,
227 and if so only return that user as the gr_mem array.
228 We can change this to a different parameter than "enum users"
229 if neccessaey, or parameterize the group list we do this for.
230 ***********************************************************************/
232 static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
233 struct winbindd_domain *domain,
234 struct winbindd_cli_state *state,
236 enum lsa_SidType group_name_type,
237 size_t *num_gr_mem, char **gr_mem,
240 DOM_SID querying_user_sid;
241 DOM_SID *pquerying_user_sid = NULL;
242 uint32 num_groups = 0;
243 DOM_SID *user_sids = NULL;
244 bool u_in_group = False;
247 unsigned int buf_len = 0;
250 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
254 uid_t ret_uid = (uid_t)-1;
255 if (sys_getpeereid(state->sock, &ret_uid)==0) {
256 /* We know who's asking - look up their SID if
257 it's one we've mapped before. */
258 status = idmap_uid_to_sid(domain->name,
259 &querying_user_sid, ret_uid);
260 if (NT_STATUS_IS_OK(status)) {
261 pquerying_user_sid = &querying_user_sid;
262 DEBUG(10,("fill_grent_mem_domain_users: "
263 "querying uid %u -> %s\n",
264 (unsigned int)ret_uid,
265 sid_string_dbg(pquerying_user_sid)));
270 /* Only look up if it was a winbindd user in this domain. */
271 if (pquerying_user_sid &&
272 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
274 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
275 sid_string_dbg(pquerying_user_sid) ));
277 status = domain->methods->lookup_usergroups(domain,
282 if (!NT_STATUS_IS_OK(status)) {
283 DEBUG(1, ("fill_grent_mem_domain_users: "
284 "lookup_usergroups failed "
285 "for sid %s in domain %s (error: %s)\n",
286 sid_string_dbg(pquerying_user_sid),
292 for (i = 0; i < num_groups; i++) {
293 if (sid_equal(group_sid, &user_sids[i])) {
294 /* User is in Domain Users, add their name
295 as the only group member. */
304 char *domainname = NULL;
305 char *username = NULL;
307 char *mapped_name = NULL;
308 enum lsa_SidType type;
309 struct winbindd_domain *target_domain = NULL;
310 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
312 DEBUG(10,("fill_grent_mem_domain_users: "
313 "sid %s in 'Domain Users' in domain %s\n",
314 sid_string_dbg(pquerying_user_sid),
317 status = domain->methods->sid_to_name(domain, mem_ctx,
322 if (!NT_STATUS_IS_OK(status)) {
323 DEBUG(1, ("could not lookup username for user "
324 "sid %s in domain %s (error: %s)\n",
325 sid_string_dbg(pquerying_user_sid),
331 target_domain = find_domain_from_name_noinit(domainname);
332 name_map_status = normalize_name_map(mem_ctx, target_domain,
333 username, &mapped_name);
335 /* Basic whitespace replacement */
336 if (NT_STATUS_IS_OK(name_map_status)) {
337 fill_domain_username(name, domainname, mapped_name, true);
339 /* Mapped to an alias */
340 else if (NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
341 fstrcpy(name, mapped_name);
343 /* no mapping done...use original name */
345 fill_domain_username(name, domainname, username, true);
350 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
351 DEBUG(1, ("out of memory\n"));
354 memcpy(buf, name, buf_len);
356 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
357 "'Domain Users' in domain %s\n",
358 name, domain->name ));
360 /* user is the only member */
365 *gr_mem_len = buf_len;
367 DEBUG(10, ("fill_grent_mem_domain_users: "
368 "num_mem = %u, len = %u, mem = %s\n",
369 (unsigned int)*num_gr_mem,
370 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
375 /***********************************************************************
376 Add names to a list. Assumes a canonical version of the string
378 ***********************************************************************/
380 static int namecmp( const void *a, const void *b )
382 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
385 static void sort_unique_list(char ***list, uint32 *n_list)
389 /* search for duplicates for sorting and looking for matching
392 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
394 for (i=1; i < *n_list; i++) {
395 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
396 memmove(&((*list)[i-1]), &((*list)[i]),
397 sizeof(char*)*((*n_list)-i));
403 static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
404 char ***list, uint32 *n_list,
405 char **names, uint32 n_names )
407 char **new_list = NULL;
408 uint32 n_new_list = 0;
411 if ( !names || (n_names == 0) )
414 /* Alloc the maximum size we'll need */
416 if ( *list == NULL ) {
417 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
418 return NT_STATUS_NO_MEMORY;
420 n_new_list = n_names;
422 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
423 (*n_list) + n_names );
425 return NT_STATUS_NO_MEMORY;
426 n_new_list = (*n_list) + n_names;
431 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
432 new_list[i] = talloc_strdup( new_list, names[j] );
436 *n_list = n_new_list;
441 /***********************************************************************
442 ***********************************************************************/
444 static NTSTATUS expand_groups( TALLOC_CTX *ctx,
445 struct winbindd_domain *d,
446 DOM_SID *glist, uint32 n_glist,
447 DOM_SID **new_glist, uint32 *n_new_glist,
448 char ***members, uint32 *n_members )
451 NTSTATUS status = NT_STATUS_OK;
452 uint32 num_names = 0;
453 uint32 *name_types = NULL;
455 DOM_SID *sid_mem = NULL;
456 TALLOC_CTX *tmp_ctx = NULL;
457 DOM_SID *new_groups = NULL;
458 size_t new_groups_size = 0;
465 for ( i=0; i<n_glist; i++ ) {
466 tmp_ctx = talloc_new( ctx );
468 /* Lookup the group membership */
470 status = d->methods->lookup_groupmem(d, tmp_ctx,
471 &glist[i], &num_names,
474 if ( !NT_STATUS_IS_OK(status) )
477 /* Separate users and groups into two lists */
479 for ( j=0; j<num_names; j++ ) {
482 if ( name_types[j] == SID_NAME_USER ||
483 name_types[j] == SID_NAME_COMPUTER )
485 status = add_names_to_list( ctx, members,
488 if ( !NT_STATUS_IS_OK(status) )
495 if ( name_types[j] == SID_NAME_DOM_GRP ||
496 name_types[j] == SID_NAME_ALIAS )
498 status = add_sid_to_array_unique(ctx,
502 if (!NT_STATUS_IS_OK(status)) {
510 TALLOC_FREE( tmp_ctx );
513 *new_glist = new_groups;
514 *n_new_glist = (uint32)new_groups_size;
517 TALLOC_FREE( tmp_ctx );
522 /***********************************************************************
523 Fill in the group membership field of a NT group given by group_sid
524 ***********************************************************************/
526 static bool fill_grent_mem(struct winbindd_domain *domain,
527 struct winbindd_cli_state *state,
529 enum lsa_SidType group_name_type,
530 size_t *num_gr_mem, char **gr_mem,
533 uint32 num_names = 0;
534 unsigned int buf_len = 0, buf_ndx = 0, i;
535 char **names = NULL, *buf = NULL;
539 DOM_SID *glist = NULL;
540 DOM_SID *new_glist = NULL;
541 uint32 n_glist, n_new_glist;
542 int max_depth = lp_winbind_expand_groups();
544 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
547 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
549 /* Initialize with no members */
553 /* HACK ALERT!! This whole routine does not cope with group members
554 * from more than one domain, ie aliases. Thus we have to work it out
555 * ourselves in a special routine. */
557 if (domain->internal) {
558 result = fill_passdb_alias_grmem(domain, group_sid,
564 /* Verify name type */
566 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
567 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
569 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
570 sid_string_dbg(group_sid),
571 domain->name, group_name_type));
575 /* OPTIMIZATION / HACK. See comment in
576 fill_grent_mem_domusers() */
578 sid_peek_rid( group_sid, &group_rid );
579 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
580 result = fill_grent_mem_domusers( mem_ctx, domain, state,
581 group_sid, group_name_type,
587 /* Real work goes here. Create a list of group names to
588 expand starting with the initial one. Pass that to
589 expand_groups() which returns a list of more group names
590 to expand. Do this up to the max search depth. */
592 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
594 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
597 sid_copy( &glist[0], group_sid );
600 for ( i=0; i<max_depth && glist; i++ ) {
601 uint32 n_members = 0;
602 char **members = NULL;
606 nt_status = expand_groups( mem_ctx, domain,
608 &new_glist, &n_new_glist,
609 &members, &n_members);
610 if ( !NT_STATUS_IS_OK(nt_status) ) {
615 /* Add new group members to list. Pass through the
616 alias mapping function */
618 for (j=0; j<n_members; j++) {
619 fstring name_domain, name_acct;
620 fstring qualified_name;
621 char *mapped_name = NULL;
622 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
623 struct winbindd_domain *target_domain = NULL;
625 if (parse_domain_user(members[j], name_domain, name_acct)) {
626 target_domain = find_domain_from_name_noinit(name_domain);
629 if (!target_domain) {
630 target_domain = domain;
633 name_map_status = normalize_name_map(members, target_domain,
634 name_acct, &mapped_name);
636 /* Basic whitespace replacement */
637 if (NT_STATUS_IS_OK(name_map_status)) {
638 fill_domain_username(qualified_name, name_domain,
640 mapped_name = qualified_name;
642 /* no mapping at all */
643 else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
644 mapped_name = members[j];
647 nt_status = add_names_to_list( mem_ctx, &names,
650 if ( !NT_STATUS_IS_OK(nt_status) ) {
656 TALLOC_FREE( members );
658 /* If we have no more groups to expand, break out
661 if (new_glist == NULL)
667 n_glist = n_new_glist;
669 TALLOC_FREE( glist );
671 sort_unique_list(&names, &num_names);
673 DEBUG(10, ("looked up %d names\n", num_names));
676 /* Add members to list */
678 for (i = 0; i < num_names; i++) {
681 DEBUG(10, ("processing name %s\n", names[i]));
683 len = strlen(names[i]);
685 /* Add to list or calculate buffer length */
688 buf_len += len + 1; /* List is comma separated */
690 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
692 DEBUG(10, ("appending %s at ndx %d\n",
694 parse_add_domuser(&buf[buf_ndx], names[i], &len);
701 /* Allocate buffer */
703 if (!buf && buf_len != 0) {
704 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
705 DEBUG(1, ("out of memory\n"));
709 memset(buf, 0, buf_len);
715 if (buf && buf_ndx > 0) {
716 buf[buf_ndx - 1] = '\0';
720 *gr_mem_len = buf_len;
722 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
723 (unsigned int)*num_gr_mem,
724 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
729 talloc_destroy(mem_ctx);
731 DEBUG(10, ("fill_grent_mem returning %d\n", result));
736 static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
738 static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
739 enum lsa_SidType type )
741 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
744 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
745 request_error(state);
749 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
750 DEBUG(5,("getgrnam_recv: not a group!\n"));
751 request_error(state);
755 winbindd_getgrsid( state, *sid );
759 /* Return a group structure from a group name */
761 void winbindd_getgrnam(struct winbindd_cli_state *state)
763 struct winbindd_domain *domain;
764 fstring name_domain, name_group;
766 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
768 /* Ensure null termination */
769 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
771 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
772 state->request.data.groupname));
774 nt_status = normalize_name_unmap(state->mem_ctx,
775 state->request.data.groupname,
777 /* If we didn't map anything in the above call, just reset the
778 tmp pointer to the original string */
779 if (!NT_STATUS_IS_OK(nt_status) &&
780 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
782 tmp = state->request.data.groupname;
785 /* Parse domain and groupname */
787 memset(name_group, 0, sizeof(name_group));
789 name_domain[0] = '\0';
790 name_group[0] = '\0';
792 parse_domain_user(tmp, name_domain, name_group);
794 /* if no domain or our local domain and no local tdb group, default to
795 * our local domain for aliases */
797 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
798 fstrcpy(name_domain, get_global_sam_name());
801 /* Get info for the domain */
803 if ((domain = find_domain_from_name_noinit(name_domain)) == NULL) {
804 DEBUG(3, ("could not get domain sid for domain %s\n",
806 request_error(state);
809 /* should we deal with users for our domain? */
811 if ( lp_winbind_trusted_domains_only() && domain->primary) {
812 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
813 "getgrnam() for %s\\%s.\n", name_domain, name_group));
814 request_error(state);
818 /* Get rid and name type from name */
820 fstrcpy( name_group, tmp );
822 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
823 getgrnam_recv, WINBINDD_GETGRNAM, state );
826 struct getgrsid_state {
827 struct winbindd_cli_state *state;
828 struct winbindd_domain *domain;
830 enum lsa_SidType group_type;
835 static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
837 struct getgrsid_state *s =
838 (struct getgrsid_state *)private_data;
839 struct winbindd_domain *domain;
843 fstring dom_name, group_name;
846 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
847 request_error(s->state);
853 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
854 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
855 request_error(s->state);
860 /* Fill in group structure */
862 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
863 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
864 request_error(s->state);
868 if (!fill_grent(s->state->mem_ctx, &s->state->response.data.gr,
869 dom_name, group_name, gid) ||
870 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
871 &num_gr_mem, &gr_mem, &gr_mem_len))
873 request_error(s->state);
877 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
879 /* Group membership lives at start of extra data */
881 s->state->response.data.gr.gr_mem_ofs = 0;
883 s->state->response.length += gr_mem_len;
884 s->state->response.extra_data.data = gr_mem;
886 request_ok(s->state);
889 static void getgrsid_lookupsid_recv( void *private_data, bool success,
890 const char *dom_name, const char *name,
891 enum lsa_SidType name_type )
893 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
894 char *mapped_name = NULL;
896 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
899 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
900 request_error(s->state);
904 /* either it's a domain group, a domain local group, or a
905 local group in an internal domain */
907 if ( !( (name_type==SID_NAME_DOM_GRP) ||
908 ((name_type==SID_NAME_ALIAS) &&
909 (s->domain->primary || s->domain->internal)) ) )
911 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
912 dom_name, name, name_type));
913 request_error(s->state);
917 /* normalize the name and ensure that we have the DOM\name
918 coming out of here */
920 fstrcpy(raw_name, name);
922 nt_status = normalize_name_unmap(s->state->mem_ctx, raw_name,
925 /* basic whitespace reversal */
926 if (NT_STATUS_IS_OK(nt_status)) {
927 s->group_name = talloc_asprintf(s->state->mem_ctx,
930 *lp_winbind_separator(),
933 /* mapped from alias */
934 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
935 s->group_name = mapped_name;
937 /* no mapping at all. use original string */
939 s->group_name = talloc_asprintf(s->state->mem_ctx,
942 *lp_winbind_separator(),
946 if (s->group_name == NULL) {
947 DEBUG(1, ("getgrsid_lookupsid_recv: group_name is NULL!\n"));
948 request_error(s->state);
952 s->group_type = name_type;
954 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
955 getgrsid_sid2gid_recv, s);
958 static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
960 struct getgrsid_state *s;
962 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
963 DEBUG(0, ("talloc failed\n"));
964 request_error(state);
970 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
971 DEBUG(3, ("Could not find domain for sid %s\n",
972 sid_string_dbg(&group_sid)));
973 request_error(state);
977 sid_copy(&s->group_sid, &group_sid);
979 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
980 getgrsid_lookupsid_recv, s );
984 static void getgrgid_recv(void *private_data, bool success, const char *sid)
986 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
987 enum lsa_SidType name_type;
991 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
992 (unsigned long)(state->request.data.gid), sid));
994 if (!string_to_sid(&group_sid, sid)) {
995 DEBUG(1,("getgrgid_recv: Could not convert sid %s "
996 "from string\n", sid));
997 request_error(state);
1001 winbindd_getgrsid(state, group_sid);
1005 /* Ok, this might be "ours", i.e. an alias */
1006 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
1007 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
1008 (name_type == SID_NAME_ALIAS)) {
1009 /* Hey, got an alias */
1010 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
1011 (unsigned long)(state->request.data.gid), sid));
1012 winbindd_getgrsid(state, group_sid);
1016 DEBUG(1, ("could not convert gid %lu to sid\n",
1017 (unsigned long)state->request.data.gid));
1018 request_error(state);
1021 /* Return a group structure from a gid number */
1022 void winbindd_getgrgid(struct winbindd_cli_state *state)
1024 gid_t gid = state->request.data.gid;
1026 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
1027 (unsigned long)state->pid,
1028 (unsigned long)gid));
1030 /* always use the async interface */
1031 winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
1035 * set/get/endgrent functions
1038 /* "Rewind" file pointer for group database enumeration */
1040 static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1042 struct winbindd_domain *domain;
1044 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1046 /* Check user has enabled this */
1048 if (!lp_winbind_enum_groups()) {
1052 /* Free old static data if it exists */
1054 if (state->getgrent_state != NULL) {
1055 free_getent_state(state->getgrent_state);
1056 state->getgrent_state = NULL;
1059 /* Create sam pipes for each domain we know about */
1061 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1062 struct getent_state *domain_state;
1064 /* Create a state record for this domain */
1066 /* don't add our domaina if we are a PDC or if we
1067 are a member of a Samba domain */
1069 if ( lp_winbind_trusted_domains_only() && domain->primary )
1074 domain_state = SMB_MALLOC_P(struct getent_state);
1075 if (!domain_state) {
1076 DEBUG(1, ("winbindd_setgrent: "
1077 "malloc failed for domain_state!\n"));
1081 ZERO_STRUCTP(domain_state);
1083 fstrcpy(domain_state->domain_name, domain->name);
1085 /* Add to list of open domains */
1087 DLIST_ADD(state->getgrent_state, domain_state);
1090 state->getgrent_initialized = True;
1094 void winbindd_setgrent(struct winbindd_cli_state *state)
1096 if (winbindd_setgrent_internal(state)) {
1099 request_error(state);
1103 /* Close file pointer to ntdom group database */
1105 void winbindd_endgrent(struct winbindd_cli_state *state)
1107 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1109 free_getent_state(state->getgrent_state);
1110 state->getgrent_initialized = False;
1111 state->getgrent_state = NULL;
1115 /* Get the list of domain groups and domain aliases for a domain. We fill in
1116 the sam_entries and num_sam_entries fields with domain group information.
1117 Return True if some groups were returned, False otherwise. */
1119 bool get_sam_group_entries(struct getent_state *ent)
1123 struct acct_info *name_list = NULL;
1124 TALLOC_CTX *mem_ctx;
1125 bool result = False;
1126 struct acct_info *sam_grp_entries = NULL;
1127 struct winbindd_domain *domain;
1129 if (ent->got_sam_entries)
1132 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1133 ent->domain_name))) {
1134 DEBUG(1, ("get_sam_group_entries: "
1135 "could not create talloc context!\n"));
1139 /* Free any existing group info */
1141 SAFE_FREE(ent->sam_entries);
1142 ent->num_sam_entries = 0;
1143 ent->got_sam_entries = True;
1145 /* Enumerate domain groups */
1149 if (!(domain = find_domain_from_name(ent->domain_name))) {
1150 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1155 /* always get the domain global groups */
1157 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1160 if (!NT_STATUS_IS_OK(status)) {
1161 DEBUG(3, ("get_sam_group_entries: "
1162 "could not enumerate domain groups! Error: %s\n",
1163 nt_errstr(status)));
1168 /* Copy entries into return buffer */
1171 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1173 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1174 "memory for %d domain groups!\n",
1179 memcpy(name_list, sam_grp_entries,
1180 num_entries * sizeof(struct acct_info));
1183 ent->num_sam_entries = num_entries;
1185 /* get the domain local groups if we are a member of a native win2k
1186 * domain and are not using LDAP to get the groups */
1188 if ( ( lp_security() != SEC_ADS && domain->native_mode
1189 && domain->primary) || domain->internal )
1191 DEBUG(4,("get_sam_group_entries: %s domain; "
1192 "enumerating local groups as well\n",
1193 domain->native_mode ? "Native Mode 2k":
1194 "BUILTIN or local"));
1196 status = domain->methods->enum_local_groups(domain, mem_ctx,
1200 if ( !NT_STATUS_IS_OK(status) ) {
1201 DEBUG(3,("get_sam_group_entries: "
1202 "Failed to enumerate "
1203 "domain local groups with error %s!\n",
1204 nt_errstr(status)));
1208 DEBUG(4,("get_sam_group_entries: "
1209 "Returned %d local groups\n",
1212 /* Copy entries into return buffer */
1214 if ( num_entries ) {
1215 name_list = SMB_REALLOC_ARRAY(name_list,
1217 ent->num_sam_entries+
1220 DEBUG(0,("get_sam_group_entries: "
1221 "Failed to realloc more memory "
1222 "for %d local groups!\n",
1228 memcpy(&name_list[ent->num_sam_entries],
1230 num_entries * sizeof(struct acct_info));
1233 ent->num_sam_entries += num_entries;
1237 /* Fill in remaining fields */
1239 ent->sam_entries = name_list;
1240 ent->sam_entry_index = 0;
1242 result = (ent->num_sam_entries > 0);
1245 talloc_destroy(mem_ctx);
1250 /* Fetch next group entry from ntdom database */
1252 #define MAX_GETGRENT_GROUPS 500
1254 void winbindd_getgrent(struct winbindd_cli_state *state)
1256 struct getent_state *ent;
1257 struct winbindd_gr *group_list = NULL;
1258 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1259 char *gr_mem_list = NULL;
1261 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1263 /* Check user has enabled this */
1265 if (!lp_winbind_enum_groups()) {
1266 request_error(state);
1270 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1272 if (num_groups == 0) {
1273 request_error(state);
1277 group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1279 request_error(state);
1282 /* will be freed by process_request() */
1283 state->response.extra_data.data = group_list;
1285 memset(state->response.extra_data.data, '\0',
1286 num_groups * sizeof(struct winbindd_gr) );
1288 state->response.data.num_entries = 0;
1290 if (!state->getgrent_initialized)
1291 winbindd_setgrent_internal(state);
1293 if (!(ent = state->getgrent_state)) {
1294 request_error(state);
1298 /* Start sending back groups */
1300 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1301 struct acct_info *name_list = NULL;
1302 fstring domain_group_name;
1308 struct winbindd_domain *domain;
1310 /* Do we need to fetch another chunk of groups? */
1314 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1315 ent->sam_entry_index, ent->num_sam_entries));
1317 if (ent->num_sam_entries == ent->sam_entry_index) {
1319 while(ent && !get_sam_group_entries(ent)) {
1320 struct getent_state *next_ent;
1322 DEBUG(10, ("freeing state info for domain %s\n",
1325 /* Free state information for this domain */
1327 SAFE_FREE(ent->sam_entries);
1329 next_ent = ent->next;
1330 DLIST_REMOVE(state->getgrent_state, ent);
1336 /* No more domains */
1342 name_list = (struct acct_info *)ent->sam_entries;
1344 if (!(domain = find_domain_from_name(ent->domain_name))) {
1345 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1351 /* Lookup group info */
1353 sid_copy(&group_sid, &domain->sid);
1354 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1356 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config
1357 ? domain->name : "",
1358 &group_sid, &group_gid)))
1361 enum lsa_SidType type;
1363 DEBUG(10, ("SID %s not in idmap\n",
1364 sid_string_dbg(&group_sid)));
1366 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1367 DEBUG(1,("could not look up gid for group %s\n",
1368 name_list[ent->sam_entry_index].acct_name));
1369 ent->sam_entry_index++;
1373 if ((type != SID_NAME_DOM_GRP) &&
1374 (type != SID_NAME_ALIAS) &&
1375 (type != SID_NAME_WKN_GRP)) {
1376 DEBUG(1, ("Group %s is a %s, not a group\n",
1377 sid_type_lookup(type),
1378 name_list[ent->sam_entry_index].acct_name));
1379 ent->sam_entry_index++;
1385 DEBUG(10, ("got gid %lu for group %lu\n",
1386 (unsigned long)group_gid,
1387 (unsigned long)name_list[ent->sam_entry_index].rid));
1389 /* Fill in group entry */
1391 fill_domain_username(domain_group_name, ent->domain_name,
1392 name_list[ent->sam_entry_index].acct_name, True);
1394 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1396 name_list[ent->sam_entry_index].acct_name,
1399 /* Fill in group membership entry */
1402 size_t num_gr_mem = 0;
1404 group_list[group_list_ndx].num_gr_mem = 0;
1408 /* Get group membership */
1409 if (state->request.cmd == WINBINDD_GETGRLST) {
1412 sid_copy(&member_sid, &domain->sid);
1413 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1414 result = fill_grent_mem(
1420 &gr_mem, &gr_mem_len);
1422 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1427 /* Append to group membership list */
1428 gr_mem_list = (char *)SMB_REALLOC(
1429 gr_mem_list, gr_mem_list_len + gr_mem_len);
1432 (group_list[group_list_ndx].num_gr_mem != 0)) {
1433 DEBUG(0, ("out of memory\n"));
1434 gr_mem_list_len = 0;
1438 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1439 gr_mem_list_len, (unsigned int)gr_mem_len));
1441 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1446 group_list[group_list_ndx].gr_mem_ofs =
1449 gr_mem_list_len += gr_mem_len;
1452 ent->sam_entry_index++;
1454 /* Add group to return list */
1458 DEBUG(10, ("adding group num_entries = %d\n",
1459 state->response.data.num_entries));
1462 state->response.data.num_entries++;
1464 state->response.length +=
1465 sizeof(struct winbindd_gr);
1468 DEBUG(0, ("could not lookup domain group %s\n",
1469 domain_group_name));
1473 /* Copy the list of group memberships to the end of the extra data */
1475 if (group_list_ndx == 0)
1478 state->response.extra_data.data = SMB_REALLOC(
1479 state->response.extra_data.data,
1480 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1482 if (!state->response.extra_data.data) {
1483 DEBUG(0, ("out of memory\n"));
1485 SAFE_FREE(gr_mem_list);
1486 request_error(state);
1490 memcpy(&((char *)state->response.extra_data.data)
1491 [group_list_ndx * sizeof(struct winbindd_gr)],
1492 gr_mem_list, gr_mem_list_len);
1494 state->response.length += gr_mem_list_len;
1496 DEBUG(10, ("returning %d groups, length = %d\n",
1497 group_list_ndx, gr_mem_list_len));
1499 /* Out of domains */
1503 SAFE_FREE(gr_mem_list);
1505 if (group_list_ndx > 0)
1508 request_error(state);
1511 /* List domain groups without mapping to unix ids */
1512 void winbindd_list_groups(struct winbindd_cli_state *state)
1514 winbindd_list_ent(state, LIST_GROUPS);
1517 /* Get user supplementary groups. This is much quicker than trying to
1518 invert the groups database. We merge the groups from the gids and
1519 other_sids info3 fields as trusted domain, universal group
1520 memberships, and nested groups (win2k native mode only) are not
1521 returned by the getgroups RPC call but are present in the info3. */
1523 struct getgroups_state {
1524 struct winbindd_cli_state *state;
1525 struct winbindd_domain *domain;
1530 const DOM_SID *token_sids;
1531 size_t i, num_token_sids;
1534 size_t num_token_gids;
1537 static void getgroups_usersid_recv(void *private_data, bool success,
1538 const DOM_SID *sid, enum lsa_SidType type);
1539 static void getgroups_tokensids_recv(void *private_data, bool success,
1540 DOM_SID *token_sids, size_t num_token_sids);
1541 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1543 void winbindd_getgroups(struct winbindd_cli_state *state)
1545 struct getgroups_state *s;
1546 char *real_name = NULL;
1547 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1549 /* Ensure null termination */
1550 state->request.data.username
1551 [sizeof(state->request.data.username)-1]='\0';
1553 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1554 state->request.data.username));
1556 /* Parse domain and username */
1558 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1560 DEBUG(0, ("talloc failed\n"));
1561 request_error(state);
1567 nt_status = normalize_name_unmap(state->mem_ctx,
1568 state->request.data.username,
1571 /* Reset the real_name pointer if we didn't do anything
1572 productive in the above call */
1573 if (!NT_STATUS_IS_OK(nt_status) &&
1574 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1576 real_name = state->request.data.username;
1579 if (!parse_domain_user_talloc(state->mem_ctx, real_name,
1580 &s->domname, &s->username)) {
1581 DEBUG(5, ("Could not parse domain user: %s\n",
1584 /* error out if we do not have nested group support */
1586 if ( !lp_winbind_nested_groups() ) {
1587 request_error(state);
1591 s->domname = talloc_strdup(state->mem_ctx,
1592 get_global_sam_name());
1593 s->username = talloc_strdup(state->mem_ctx,
1594 state->request.data.username);
1597 /* Get info for the domain (either by short domain name or
1598 DNS name in the case of a UPN) */
1600 s->domain = find_domain_from_name_noinit(s->domname);
1602 char *p = strchr(s->username, '@');
1605 s->domain = find_domain_from_name_noinit(p+1);
1610 if (s->domain == NULL) {
1611 DEBUG(7, ("could not find domain entry for domain %s\n",
1613 request_error(state);
1617 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1618 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1619 "getgroups() for %s\\%s.\n", s->domname,
1621 request_error(state);
1625 /* Get rid and name type from name. The following costs 1 packet */
1627 winbindd_lookupname_async(state->mem_ctx,
1628 s->domname, s->username,
1629 getgroups_usersid_recv,
1630 WINBINDD_GETGROUPS, s);
1633 static void getgroups_usersid_recv(void *private_data, bool success,
1634 const DOM_SID *sid, enum lsa_SidType type)
1636 struct getgroups_state *s =
1637 (struct getgroups_state *)private_data;
1640 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1641 request_error(s->state);
1645 sid_copy(&s->user_sid, sid);
1647 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1648 getgroups_tokensids_recv, s);
1651 static void getgroups_tokensids_recv(void *private_data, bool success,
1652 DOM_SID *token_sids, size_t num_token_sids)
1654 struct getgroups_state *s =
1655 (struct getgroups_state *)private_data;
1657 /* We need at least the user sid and the primary group in the token,
1658 * otherwise it's an error */
1660 if ((!success) || (num_token_sids < 2)) {
1661 request_error(s->state);
1665 s->token_sids = token_sids;
1666 s->num_token_sids = num_token_sids;
1669 s->token_gids = NULL;
1670 s->num_token_gids = 0;
1672 getgroups_sid2gid_recv(s, False, 0);
1675 static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1677 struct getgroups_state *s =
1678 (struct getgroups_state *)private_data;
1681 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1683 &s->num_token_gids)) {
1688 if (s->i < s->num_token_sids) {
1689 const DOM_SID *sid = &s->token_sids[s->i];
1692 if (sid_equal(sid, &s->user_sid)) {
1693 getgroups_sid2gid_recv(s, False, 0);
1697 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1698 getgroups_sid2gid_recv, s);
1702 s->state->response.data.num_entries = s->num_token_gids;
1703 if (s->num_token_gids) {
1704 /* s->token_gids are talloced */
1705 s->state->response.extra_data.data =
1706 smb_xmemdup(s->token_gids,
1707 s->num_token_gids * sizeof(gid_t));
1708 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1710 request_ok(s->state);
1713 /* Get user supplementary sids. This is equivalent to the
1714 winbindd_getgroups() function but it involves a SID->SIDs mapping
1715 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1716 idmap. This call is designed to be used with applications that need
1717 to do ACL evaluation themselves. Note that the cached info3 data is
1720 this function assumes that the SID that comes in is a user SID. If
1721 you pass in another type of SID then you may get unpredictable
1725 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1728 void winbindd_getusersids(struct winbindd_cli_state *state)
1732 /* Ensure null termination */
1733 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1735 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1736 if (user_sid == NULL) {
1737 DEBUG(1, ("talloc failed\n"));
1738 request_error(state);
1742 if (!string_to_sid(user_sid, state->request.data.sid)) {
1743 DEBUG(1, ("Could not get convert sid %s from string\n",
1744 state->request.data.sid));
1745 request_error(state);
1749 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1753 static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1756 struct winbindd_cli_state *state =
1757 (struct winbindd_cli_state *)private_data;
1759 unsigned ofs, ret_size = 0;
1763 request_error(state);
1767 /* work out the response size */
1768 for (i = 0; i < num_sids; i++) {
1770 sid_to_fstring(s, &sids[i]);
1771 ret_size += strlen(s) + 1;
1774 /* build the reply */
1775 ret = (char *)SMB_MALLOC(ret_size);
1777 DEBUG(0, ("malloc failed\n"));
1778 request_error(state);
1782 for (i = 0; i < num_sids; i++) {
1784 sid_to_fstring(s, &sids[i]);
1785 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1786 ofs += strlen(ret+ofs) + 1;
1789 /* Send data back to client */
1790 state->response.data.num_entries = num_sids;
1791 state->response.extra_data.data = ret;
1792 state->response.length += ret_size;
1796 void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1799 struct winbindd_domain *domain;
1801 /* Ensure null termination */
1802 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1804 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1805 DEBUG(1, ("Could not get convert sid %s from string\n",
1806 state->request.data.sid));
1807 request_error(state);
1811 /* Get info for the domain */
1812 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1813 DEBUG(0,("could not find domain entry for sid %s\n",
1814 sid_string_dbg(&user_sid)));
1815 request_error(state);
1819 sendto_domain(state, domain);
1822 enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1823 struct winbindd_cli_state *state)
1833 /* Ensure null termination */
1834 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1836 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1837 DEBUG(1, ("Could not get convert sid %s from string\n",
1838 state->request.data.sid));
1839 return WINBINDD_ERROR;
1842 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1843 &user_sid, &num_groups,
1845 if (!NT_STATUS_IS_OK(status))
1846 return WINBINDD_ERROR;
1848 if (num_groups == 0) {
1849 state->response.data.num_entries = 0;
1850 state->response.extra_data.data = NULL;
1854 if (!print_sidlist(state->mem_ctx,
1856 &sidstring, &len)) {
1857 DEBUG(0, ("talloc failed\n"));
1858 return WINBINDD_ERROR;
1861 state->response.extra_data.data = SMB_STRDUP(sidstring);
1862 if (!state->response.extra_data.data) {
1863 return WINBINDD_ERROR;
1865 state->response.length += len+1;
1866 state->response.data.num_entries = num_groups;