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
)
38 fill_domain_username(name
, domain
, user
, True
);
39 safe_strcat(name
, ",", sizeof(name
)-1);
40 string_append(pp_members
, name
);
44 /**********************************************************************
45 Add member users resulting from sid. Expand if it is a domain group.
46 **********************************************************************/
48 static void add_expanded_sid(const DOM_SID
*sid
,
50 size_t *p_num_members
)
54 struct winbindd_domain
*domain
;
57 char *domain_name
= NULL
;
59 enum lsa_SidType type
;
68 TALLOC_CTX
*mem_ctx
= talloc_init("add_expanded_sid");
70 if (mem_ctx
== NULL
) {
71 DEBUG(1, ("talloc_init failed\n"));
75 sid_copy(&dom_sid
, sid
);
76 sid_split_rid(&dom_sid
, &rid
);
78 domain
= find_lookup_domain_from_sid(sid
);
81 DEBUG(3, ("Could not find domain for sid %s\n",
82 sid_string_dbg(sid
)));
86 result
= domain
->methods
->sid_to_name(domain
, mem_ctx
, sid
,
87 &domain_name
, &name
, &type
);
89 if (!NT_STATUS_IS_OK(result
)) {
90 DEBUG(3, ("sid_to_name failed for sid %s\n",
91 sid_string_dbg(sid
)));
95 DEBUG(10, ("Found name %s, type %d\n", name
, type
));
97 if (type
== SID_NAME_USER
) {
98 add_member(domain_name
, name
, pp_members
, p_num_members
);
102 if (type
!= SID_NAME_DOM_GRP
) {
103 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
108 /* Expand the domain group, this must be done via the target domain */
110 domain
= find_domain_from_sid(sid
);
112 if (domain
== NULL
) {
113 DEBUG(3, ("Could not find domain from SID %s\n",
114 sid_string_dbg(sid
)));
118 result
= domain
->methods
->lookup_groupmem(domain
, mem_ctx
,
123 if (!NT_STATUS_IS_OK(result
)) {
124 DEBUG(10, ("Could not lookup group members for %s: %s\n",
125 name
, nt_errstr(result
)));
129 for (i
=0; i
<num_names
; i
++) {
130 DEBUG(10, ("Adding group member SID %s\n",
131 sid_string_dbg(&sid_mem
[i
])));
133 if (types
[i
] != SID_NAME_USER
) {
134 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
135 "Ignoring.\n", names
[i
], name
));
139 add_member(domain
->name
, names
[i
], pp_members
, p_num_members
);
143 talloc_destroy(mem_ctx
);
147 static bool fill_passdb_alias_grmem(struct winbindd_domain
*domain
,
148 DOM_SID
*group_sid
, size_t *num_gr_mem
,
149 char **gr_mem
, size_t *gr_mem_len
)
152 size_t i
, num_members
;
158 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid
, &members
,
162 for (i
=0; i
<num_members
; i
++) {
163 add_expanded_sid(&members
[i
], gr_mem
, num_gr_mem
);
166 TALLOC_FREE(members
);
168 if (*gr_mem
!= NULL
) {
171 /* We have at least one member, strip off the last "," */
172 len
= strlen(*gr_mem
);
173 (*gr_mem
)[len
-1] = '\0';
180 /* Fill a grent structure from various other information */
182 static bool fill_grent(struct winbindd_gr
*gr
, const char *dom_name
,
183 const char *gr_name
, gid_t unix_gid
)
185 fstring full_group_name
;
187 fill_domain_username( full_group_name
, dom_name
, gr_name
, True
);
189 gr
->gr_gid
= unix_gid
;
191 /* Group name and password */
193 safe_strcpy(gr
->gr_name
, full_group_name
, sizeof(gr
->gr_name
) - 1);
194 safe_strcpy(gr
->gr_passwd
, "x", sizeof(gr
->gr_passwd
) - 1);
199 /***********************************************************************
200 If "enum users" is set to false, and the group being looked
201 up is the Domain Users SID: S-1-5-domain-513, then for the
202 list of members check if the querying user is in that group,
203 and if so only return that user as the gr_mem array.
204 We can change this to a different parameter than "enum users"
205 if neccessaey, or parameterize the group list we do this for.
206 ***********************************************************************/
208 static bool fill_grent_mem_domusers( TALLOC_CTX
*mem_ctx
,
209 struct winbindd_domain
*domain
,
210 struct winbindd_cli_state
*state
,
212 enum lsa_SidType group_name_type
,
213 size_t *num_gr_mem
, char **gr_mem
,
216 DOM_SID querying_user_sid
;
217 DOM_SID
*pquerying_user_sid
= NULL
;
218 uint32 num_groups
= 0;
219 DOM_SID
*user_sids
= NULL
;
220 bool u_in_group
= False
;
223 unsigned int buf_len
= 0;
226 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
230 uid_t ret_uid
= (uid_t
)-1;
231 if (sys_getpeereid(state
->sock
, &ret_uid
)==0) {
232 /* We know who's asking - look up their SID if
233 it's one we've mapped before. */
234 status
= idmap_uid_to_sid(domain
->name
,
235 &querying_user_sid
, ret_uid
);
236 if (NT_STATUS_IS_OK(status
)) {
237 pquerying_user_sid
= &querying_user_sid
;
238 DEBUG(10,("fill_grent_mem_domain_users: "
239 "querying uid %u -> %s\n",
240 (unsigned int)ret_uid
,
241 sid_string_dbg(pquerying_user_sid
)));
246 /* Only look up if it was a winbindd user in this domain. */
247 if (pquerying_user_sid
&&
248 (sid_compare_domain(pquerying_user_sid
, &domain
->sid
) == 0)) {
250 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
251 sid_string_dbg(pquerying_user_sid
) ));
253 status
= domain
->methods
->lookup_usergroups(domain
,
258 if (!NT_STATUS_IS_OK(status
)) {
259 DEBUG(1, ("fill_grent_mem_domain_users: "
260 "lookup_usergroups failed "
261 "for sid %s in domain %s (error: %s)\n",
262 sid_string_dbg(pquerying_user_sid
),
268 for (i
= 0; i
< num_groups
; i
++) {
269 if (sid_equal(group_sid
, &user_sids
[i
])) {
270 /* User is in Domain Users, add their name
271 as the only group member. */
280 char *domainname
= NULL
;
281 char *username
= NULL
;
283 enum lsa_SidType type
;
285 DEBUG(10,("fill_grent_mem_domain_users: "
286 "sid %s in 'Domain Users' in domain %s\n",
287 sid_string_dbg(pquerying_user_sid
),
290 status
= domain
->methods
->sid_to_name(domain
, mem_ctx
,
295 if (!NT_STATUS_IS_OK(status
)) {
296 DEBUG(1, ("could not lookup username for user "
297 "sid %s in domain %s (error: %s)\n",
298 sid_string_dbg(pquerying_user_sid
),
303 fill_domain_username(name
, domain
->name
, username
, True
);
306 if (!(buf
= (char *)SMB_MALLOC(buf_len
))) {
307 DEBUG(1, ("out of memory\n"));
310 memcpy(buf
, name
, buf_len
);
312 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
313 "'Domain Users' in domain %s\n",
314 name
, domain
->name
));
316 /* user is the only member */
321 *gr_mem_len
= buf_len
;
323 DEBUG(10, ("fill_grent_mem_domain_users: "
324 "num_mem = %u, len = %u, mem = %s\n",
325 (unsigned int)*num_gr_mem
,
326 (unsigned int)buf_len
, *num_gr_mem
? buf
: "NULL"));
331 /***********************************************************************
332 Add names to a list. Assumes a canonical version of the string
334 ***********************************************************************/
336 static int namecmp( const void *a
, const void *b
)
338 return StrCaseCmp( * (char * const *) a
, * (char * const *) b
);
341 static NTSTATUS
add_names_to_list( TALLOC_CTX
*ctx
,
342 char ***list
, uint32
*n_list
,
343 char **names
, uint32 n_names
)
345 char **new_list
= NULL
;
346 uint32 n_new_list
= 0;
349 if ( !names
|| (n_names
== 0) )
352 /* Alloc the maximum size we'll need */
354 if ( *list
== NULL
) {
355 if ((new_list
= TALLOC_ARRAY(ctx
, char *, n_names
)) == NULL
) {
356 return NT_STATUS_NO_MEMORY
;
358 n_new_list
= n_names
;
360 new_list
= TALLOC_REALLOC_ARRAY( ctx
, *list
, char *,
361 (*n_list
) + n_names
);
363 return NT_STATUS_NO_MEMORY
;
364 n_new_list
= (*n_list
) + n_names
;
369 for ( i
=*n_list
, j
=0; i
<n_new_list
; i
++, j
++ ) {
370 new_list
[i
] = talloc_strdup( new_list
, names
[j
] );
373 /* search for duplicates for sorting and looking for matching
376 qsort( new_list
, n_new_list
, sizeof(char*), QSORT_CAST namecmp
);
378 for ( i
=1; i
<n_new_list
; i
++ ) {
379 if ( strcmp( new_list
[i
-1], new_list
[i
] ) == 0 ) {
380 memmove( &new_list
[i
-1], &new_list
[i
],
381 sizeof(char*)*(n_new_list
-i
) );
387 *n_list
= n_new_list
;
392 /***********************************************************************
393 ***********************************************************************/
395 static NTSTATUS
expand_groups( TALLOC_CTX
*ctx
,
396 struct winbindd_domain
*d
,
397 DOM_SID
*glist
, uint32 n_glist
,
398 DOM_SID
**new_glist
, uint32
*n_new_glist
,
399 char ***members
, uint32
*n_members
)
402 NTSTATUS status
= NT_STATUS_OK
;
403 uint32 num_names
= 0;
404 uint32
*name_types
= NULL
;
406 DOM_SID
*sid_mem
= NULL
;
407 TALLOC_CTX
*tmp_ctx
= NULL
;
408 DOM_SID
*new_groups
= NULL
;
409 size_t new_groups_size
= 0;
416 for ( i
=0; i
<n_glist
; i
++ ) {
417 tmp_ctx
= talloc_new( ctx
);
419 /* Lookup the group membership */
421 status
= d
->methods
->lookup_groupmem(d
, tmp_ctx
,
422 &glist
[i
], &num_names
,
425 if ( !NT_STATUS_IS_OK(status
) )
428 /* Separate users and groups into two lists */
430 for ( j
=0; j
<num_names
; j
++ ) {
433 if ( name_types
[j
] == SID_NAME_USER
||
434 name_types
[j
] == SID_NAME_COMPUTER
)
436 status
= add_names_to_list( ctx
, members
,
439 if ( !NT_STATUS_IS_OK(status
) )
446 if ( name_types
[j
] == SID_NAME_DOM_GRP
||
447 name_types
[j
] == SID_NAME_ALIAS
)
449 status
= add_sid_to_array_unique(ctx
,
453 if (!NT_STATUS_IS_OK(status
)) {
461 TALLOC_FREE( tmp_ctx
);
464 *new_glist
= new_groups
;
465 *n_new_glist
= (uint32
)new_groups_size
;
468 TALLOC_FREE( tmp_ctx
);
473 /***********************************************************************
474 Fill in the group membership field of a NT group given by group_sid
475 ***********************************************************************/
477 static bool fill_grent_mem(struct winbindd_domain
*domain
,
478 struct winbindd_cli_state
*state
,
480 enum lsa_SidType group_name_type
,
481 size_t *num_gr_mem
, char **gr_mem
,
484 uint32 num_names
= 0;
485 unsigned int buf_len
= 0, buf_ndx
= 0, i
;
486 char **names
= NULL
, *buf
= NULL
;
490 DOM_SID
*glist
= NULL
;
491 DOM_SID
*new_glist
= NULL
;
492 uint32 n_glist
, n_new_glist
;
493 int max_depth
= lp_winbind_expand_groups();
495 if (!(mem_ctx
= talloc_init("fill_grent_mem(%s)", domain
->name
)))
498 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid
)));
500 /* Initialize with no members */
504 /* HACK ALERT!! This whole routine does not cope with group members
505 * from more than one domain, ie aliases. Thus we have to work it out
506 * ourselves in a special routine. */
508 if (domain
->internal
) {
509 result
= fill_passdb_alias_grmem(domain
, group_sid
,
515 /* Verify name type */
517 if ( !((group_name_type
==SID_NAME_DOM_GRP
) ||
518 ((group_name_type
==SID_NAME_ALIAS
) && domain
->primary
)) )
520 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
521 sid_string_dbg(group_sid
),
522 domain
->name
, group_name_type
));
526 /* OPTIMIZATION / HACK. See comment in
527 fill_grent_mem_domusers() */
529 sid_peek_rid( group_sid
, &group_rid
);
530 if (!lp_winbind_enum_users() && group_rid
== DOMAIN_GROUP_RID_USERS
) {
531 result
= fill_grent_mem_domusers( mem_ctx
, domain
, state
,
532 group_sid
, group_name_type
,
538 /* Real work goes here. Create a list of group names to
539 expand startign with the initial one. Pass that to
540 expand_groups() which returns a list of more group names
541 to expand. Do this up to the max search depth. */
543 if ( (glist
= TALLOC_ARRAY(mem_ctx
, DOM_SID
, 1 )) == NULL
) {
545 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
548 sid_copy( &glist
[0], group_sid
);
551 for ( i
=0; i
<max_depth
&& glist
; i
++ ) {
552 uint32 n_members
= 0;
553 char **members
= NULL
;
556 nt_status
= expand_groups( mem_ctx
, domain
,
558 &new_glist
, &n_new_glist
,
559 &members
, &n_members
);
560 if ( !NT_STATUS_IS_OK(nt_status
) ) {
565 /* Add new group members to list */
567 nt_status
= add_names_to_list( mem_ctx
, &names
, &num_names
,
568 members
, n_members
);
569 if ( !NT_STATUS_IS_OK(nt_status
) ) {
574 TALLOC_FREE( members
);
576 /* If we have no more groups to expand, break out
579 if (new_glist
== NULL
)
585 n_glist
= n_new_glist
;
587 TALLOC_FREE( glist
);
589 DEBUG(10, ("looked up %d names\n", num_names
));
592 /* Add members to list */
594 for (i
= 0; i
< num_names
; i
++) {
597 DEBUG(10, ("processing name %s\n", names
[i
]));
599 len
= strlen(names
[i
]);
601 /* Add to list or calculate buffer length */
604 buf_len
+= len
+ 1; /* List is comma separated */
606 DEBUG(10, ("buf_len + %d = %d\n", len
+ 1, buf_len
));
608 DEBUG(10, ("appending %s at ndx %d\n",
610 safe_strcpy(&buf
[buf_ndx
], names
[i
], len
);
617 /* Allocate buffer */
619 if (!buf
&& buf_len
!= 0) {
620 if (!(buf
= (char *)SMB_MALLOC(buf_len
))) {
621 DEBUG(1, ("out of memory\n"));
625 memset(buf
, 0, buf_len
);
631 if (buf
&& buf_ndx
> 0) {
632 buf
[buf_ndx
- 1] = '\0';
636 *gr_mem_len
= buf_len
;
638 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
639 (unsigned int)*num_gr_mem
,
640 (unsigned int)buf_len
, *num_gr_mem
? buf
: "NULL"));
645 talloc_destroy(mem_ctx
);
647 DEBUG(10, ("fill_grent_mem returning %d\n", result
));
652 static void winbindd_getgrsid(struct winbindd_cli_state
*state
, DOM_SID group_sid
);
654 static void getgrnam_recv( void *private_data
, bool success
, const DOM_SID
*sid
,
655 enum lsa_SidType type
)
657 struct winbindd_cli_state
*state
= (struct winbindd_cli_state
*)private_data
;
660 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
661 request_error(state
);
665 if ( (type
!= SID_NAME_DOM_GRP
) && (type
!= SID_NAME_ALIAS
) ) {
666 DEBUG(5,("getgrnam_recv: not a group!\n"));
667 request_error(state
);
671 winbindd_getgrsid( state
, *sid
);
675 /* Return a group structure from a group name */
677 void winbindd_getgrnam(struct winbindd_cli_state
*state
)
679 struct winbindd_domain
*domain
;
680 fstring name_domain
, name_group
;
683 /* Ensure null termination */
684 state
->request
.data
.groupname
[sizeof(state
->request
.data
.groupname
)-1]='\0';
686 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state
->pid
,
687 state
->request
.data
.groupname
));
689 /* Parse domain and groupname */
691 memset(name_group
, 0, sizeof(fstring
));
693 tmp
= state
->request
.data
.groupname
;
695 name_domain
[0] = '\0';
696 name_group
[0] = '\0';
698 parse_domain_user(tmp
, name_domain
, name_group
);
700 /* if no domain or our local domain and no local tdb group, default to
701 * our local domain for aliases */
703 if ( !*name_domain
|| strequal(name_domain
, get_global_sam_name()) ) {
704 fstrcpy(name_domain
, get_global_sam_name());
707 /* Get info for the domain */
709 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
710 DEBUG(3, ("could not get domain sid for domain %s\n",
712 request_error(state
);
715 /* should we deal with users for our domain? */
717 if ( lp_winbind_trusted_domains_only() && domain
->primary
) {
718 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
719 "getgrnam() for %s\\%s.\n", name_domain
, name_group
));
720 request_error(state
);
724 /* Get rid and name type from name */
726 ws_name_replace( name_group
, WB_REPLACE_CHAR
);
728 winbindd_lookupname_async( state
->mem_ctx
, domain
->name
, name_group
,
729 getgrnam_recv
, WINBINDD_GETGRNAM
, state
);
732 struct getgrsid_state
{
733 struct winbindd_cli_state
*state
;
734 struct winbindd_domain
*domain
;
736 enum lsa_SidType group_type
;
741 static void getgrsid_sid2gid_recv(void *private_data
, bool success
, gid_t gid
)
743 struct getgrsid_state
*s
=
744 (struct getgrsid_state
*)private_data
;
745 struct winbindd_domain
*domain
;
749 fstring dom_name
, group_name
;
752 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
753 request_error(s
->state
);
759 if ( !parse_domain_user( s
->group_name
, dom_name
, group_name
) ) {
760 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
761 request_error(s
->state
);
766 /* Fill in group structure */
768 if ( (domain
= find_domain_from_name_noinit(dom_name
)) == NULL
) {
769 DEBUG(1,("Can't find domain from name (%s)\n", dom_name
));
770 request_error(s
->state
);
774 if (!fill_grent(&s
->state
->response
.data
.gr
, dom_name
, group_name
, gid
) ||
775 !fill_grent_mem(domain
, s
->state
, &s
->group_sid
, s
->group_type
,
776 &num_gr_mem
, &gr_mem
, &gr_mem_len
))
778 request_error(s
->state
);
782 s
->state
->response
.data
.gr
.num_gr_mem
= (uint32
)num_gr_mem
;
784 /* Group membership lives at start of extra data */
786 s
->state
->response
.data
.gr
.gr_mem_ofs
= 0;
788 s
->state
->response
.length
+= gr_mem_len
;
789 s
->state
->response
.extra_data
.data
= gr_mem
;
791 request_ok(s
->state
);
794 static void getgrsid_lookupsid_recv( void *private_data
, bool success
,
795 const char *dom_name
, const char *name
,
796 enum lsa_SidType name_type
)
798 struct getgrsid_state
*s
= (struct getgrsid_state
*)private_data
;
801 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
802 request_error(s
->state
);
806 /* either it's a domain group, a domain local group, or a
807 local group in an internal domain */
809 if ( !( (name_type
==SID_NAME_DOM_GRP
) ||
810 ((name_type
==SID_NAME_ALIAS
) &&
811 (s
->domain
->primary
|| s
->domain
->internal
)) ) )
813 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
814 dom_name
, name
, name_type
));
815 request_error(s
->state
);
819 if ( (s
->group_name
= talloc_asprintf( s
->state
->mem_ctx
,
822 *lp_winbind_separator(),
825 DEBUG(1, ("getgrsid_lookupsid_recv: talloc_asprintf() Failed!\n"));
826 request_error(s
->state
);
830 s
->group_type
= name_type
;
832 winbindd_sid2gid_async(s
->state
->mem_ctx
, &s
->group_sid
,
833 getgrsid_sid2gid_recv
, s
);
836 static void winbindd_getgrsid( struct winbindd_cli_state
*state
, const DOM_SID group_sid
)
838 struct getgrsid_state
*s
;
840 if ( (s
= TALLOC_ZERO_P(state
->mem_ctx
, struct getgrsid_state
)) == NULL
) {
841 DEBUG(0, ("talloc failed\n"));
842 request_error(state
);
848 if ( (s
->domain
= find_domain_from_sid_noinit(&group_sid
)) == NULL
) {
849 DEBUG(3, ("Could not find domain for sid %s\n",
850 sid_string_dbg(&group_sid
)));
851 request_error(state
);
855 sid_copy(&s
->group_sid
, &group_sid
);
857 winbindd_lookupsid_async( s
->state
->mem_ctx
, &group_sid
,
858 getgrsid_lookupsid_recv
, s
);
862 static void getgrgid_recv(void *private_data
, bool success
, const char *sid
)
864 struct winbindd_cli_state
*state
= talloc_get_type_abort(private_data
, struct winbindd_cli_state
);
865 enum lsa_SidType name_type
;
869 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
870 (unsigned long)(state
->request
.data
.gid
), sid
));
872 string_to_sid(&group_sid
, sid
);
873 winbindd_getgrsid(state
, group_sid
);
877 /* Ok, this might be "ours", i.e. an alias */
878 if (pdb_gid_to_sid(state
->request
.data
.gid
, &group_sid
) &&
879 lookup_sid(state
->mem_ctx
, &group_sid
, NULL
, NULL
, &name_type
) &&
880 (name_type
== SID_NAME_ALIAS
)) {
881 /* Hey, got an alias */
882 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
883 (unsigned long)(state
->request
.data
.gid
), sid
));
884 winbindd_getgrsid(state
, group_sid
);
888 DEBUG(1, ("could not convert gid %lu to sid\n",
889 (unsigned long)state
->request
.data
.gid
));
890 request_error(state
);
893 /* Return a group structure from a gid number */
894 void winbindd_getgrgid(struct winbindd_cli_state
*state
)
896 gid_t gid
= state
->request
.data
.gid
;
898 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
899 (unsigned long)state
->pid
,
900 (unsigned long)gid
));
902 /* always use the async interface */
903 winbindd_gid2sid_async(state
->mem_ctx
, gid
, getgrgid_recv
, state
);
907 * set/get/endgrent functions
910 /* "Rewind" file pointer for group database enumeration */
912 static bool winbindd_setgrent_internal(struct winbindd_cli_state
*state
)
914 struct winbindd_domain
*domain
;
916 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state
->pid
));
918 /* Check user has enabled this */
920 if (!lp_winbind_enum_groups()) {
924 /* Free old static data if it exists */
926 if (state
->getgrent_state
!= NULL
) {
927 free_getent_state(state
->getgrent_state
);
928 state
->getgrent_state
= NULL
;
931 /* Create sam pipes for each domain we know about */
933 for (domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
934 struct getent_state
*domain_state
;
936 /* Create a state record for this domain */
938 /* don't add our domaina if we are a PDC or if we
939 are a member of a Samba domain */
941 if ( lp_winbind_trusted_domains_only() && domain
->primary
)
946 domain_state
= SMB_MALLOC_P(struct getent_state
);
948 DEBUG(1, ("winbindd_setgrent: "
949 "malloc failed for domain_state!\n"));
953 ZERO_STRUCTP(domain_state
);
955 fstrcpy(domain_state
->domain_name
, domain
->name
);
957 /* Add to list of open domains */
959 DLIST_ADD(state
->getgrent_state
, domain_state
);
962 state
->getgrent_initialized
= True
;
966 void winbindd_setgrent(struct winbindd_cli_state
*state
)
968 if (winbindd_setgrent_internal(state
)) {
971 request_error(state
);
975 /* Close file pointer to ntdom group database */
977 void winbindd_endgrent(struct winbindd_cli_state
*state
)
979 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state
->pid
));
981 free_getent_state(state
->getgrent_state
);
982 state
->getgrent_initialized
= False
;
983 state
->getgrent_state
= NULL
;
987 /* Get the list of domain groups and domain aliases for a domain. We fill in
988 the sam_entries and num_sam_entries fields with domain group information.
989 Return True if some groups were returned, False otherwise. */
991 bool get_sam_group_entries(struct getent_state
*ent
)
995 struct acct_info
*name_list
= NULL
;
998 struct acct_info
*sam_grp_entries
= NULL
;
999 struct winbindd_domain
*domain
;
1001 if (ent
->got_sam_entries
)
1004 if (!(mem_ctx
= talloc_init("get_sam_group_entries(%s)",
1005 ent
->domain_name
))) {
1006 DEBUG(1, ("get_sam_group_entries: "
1007 "could not create talloc context!\n"));
1011 /* Free any existing group info */
1013 SAFE_FREE(ent
->sam_entries
);
1014 ent
->num_sam_entries
= 0;
1015 ent
->got_sam_entries
= True
;
1017 /* Enumerate domain groups */
1021 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
1022 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1027 /* always get the domain global groups */
1029 status
= domain
->methods
->enum_dom_groups(domain
, mem_ctx
, &num_entries
,
1032 if (!NT_STATUS_IS_OK(status
)) {
1033 DEBUG(3, ("get_sam_group_entries: "
1034 "could not enumerate domain groups! Error: %s\n",
1035 nt_errstr(status
)));
1040 /* Copy entries into return buffer */
1043 name_list
= SMB_MALLOC_ARRAY(struct acct_info
, num_entries
);
1045 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1046 "memory for %d domain groups!\n",
1051 memcpy(name_list
, sam_grp_entries
,
1052 num_entries
* sizeof(struct acct_info
));
1055 ent
->num_sam_entries
= num_entries
;
1057 /* get the domain local groups if we are a member of a native win2k
1058 * domain and are not using LDAP to get the groups */
1060 if ( ( lp_security() != SEC_ADS
&& domain
->native_mode
1061 && domain
->primary
) || domain
->internal
)
1063 DEBUG(4,("get_sam_group_entries: %s domain; "
1064 "enumerating local groups as well\n",
1065 domain
->native_mode
? "Native Mode 2k":
1066 "BUILTIN or local"));
1068 status
= domain
->methods
->enum_local_groups(domain
, mem_ctx
,
1072 if ( !NT_STATUS_IS_OK(status
) ) {
1073 DEBUG(3,("get_sam_group_entries: "
1074 "Failed to enumerate "
1075 "domain local groups with error %s!\n",
1076 nt_errstr(status
)));
1080 DEBUG(4,("get_sam_group_entries: "
1081 "Returned %d local groups\n",
1084 /* Copy entries into return buffer */
1086 if ( num_entries
) {
1087 name_list
= SMB_REALLOC_ARRAY(name_list
,
1089 ent
->num_sam_entries
+
1092 DEBUG(0,("get_sam_group_entries: "
1093 "Failed to realloc more memory "
1094 "for %d local groups!\n",
1100 memcpy(&name_list
[ent
->num_sam_entries
],
1102 num_entries
* sizeof(struct acct_info
));
1105 ent
->num_sam_entries
+= num_entries
;
1109 /* Fill in remaining fields */
1111 ent
->sam_entries
= name_list
;
1112 ent
->sam_entry_index
= 0;
1114 result
= (ent
->num_sam_entries
> 0);
1117 talloc_destroy(mem_ctx
);
1122 /* Fetch next group entry from ntdom database */
1124 #define MAX_GETGRENT_GROUPS 500
1126 void winbindd_getgrent(struct winbindd_cli_state
*state
)
1128 struct getent_state
*ent
;
1129 struct winbindd_gr
*group_list
= NULL
;
1130 int num_groups
, group_list_ndx
, gr_mem_list_len
= 0;
1131 char *gr_mem_list
= NULL
;
1133 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state
->pid
));
1135 /* Check user has enabled this */
1137 if (!lp_winbind_enum_groups()) {
1138 request_error(state
);
1142 num_groups
= MIN(MAX_GETGRENT_GROUPS
, state
->request
.data
.num_entries
);
1144 if (num_groups
== 0) {
1145 request_error(state
);
1149 group_list
= SMB_MALLOC_ARRAY(struct winbindd_gr
, num_groups
);
1151 request_error(state
);
1154 /* will be freed by process_request() */
1155 state
->response
.extra_data
.data
= group_list
;
1157 memset(state
->response
.extra_data
.data
, '\0',
1158 num_groups
* sizeof(struct winbindd_gr
) );
1160 state
->response
.data
.num_entries
= 0;
1162 if (!state
->getgrent_initialized
)
1163 winbindd_setgrent_internal(state
);
1165 if (!(ent
= state
->getgrent_state
)) {
1166 request_error(state
);
1170 /* Start sending back groups */
1172 for (group_list_ndx
= 0; group_list_ndx
< num_groups
; ) {
1173 struct acct_info
*name_list
= NULL
;
1174 fstring domain_group_name
;
1180 struct winbindd_domain
*domain
;
1182 /* Do we need to fetch another chunk of groups? */
1186 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1187 ent
->sam_entry_index
, ent
->num_sam_entries
));
1189 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
1191 while(ent
&& !get_sam_group_entries(ent
)) {
1192 struct getent_state
*next_ent
;
1194 DEBUG(10, ("freeing state info for domain %s\n",
1197 /* Free state information for this domain */
1199 SAFE_FREE(ent
->sam_entries
);
1201 next_ent
= ent
->next
;
1202 DLIST_REMOVE(state
->getgrent_state
, ent
);
1208 /* No more domains */
1214 name_list
= (struct acct_info
*)ent
->sam_entries
;
1216 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
1217 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1223 /* Lookup group info */
1225 sid_copy(&group_sid
, &domain
->sid
);
1226 sid_append_rid(&group_sid
, name_list
[ent
->sam_entry_index
].rid
);
1228 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain
->name
, &group_sid
,
1231 enum lsa_SidType type
;
1233 DEBUG(10, ("SID %s not in idmap\n",
1234 sid_string_dbg(&group_sid
)));
1236 if (!pdb_sid_to_id(&group_sid
, &id
, &type
)) {
1237 DEBUG(1,("could not look up gid for group %s\n",
1238 name_list
[ent
->sam_entry_index
].acct_name
));
1239 ent
->sam_entry_index
++;
1243 if ((type
!= SID_NAME_DOM_GRP
) &&
1244 (type
!= SID_NAME_ALIAS
) &&
1245 (type
!= SID_NAME_WKN_GRP
)) {
1246 DEBUG(1, ("Group %s is a %s, not a group\n",
1247 sid_type_lookup(type
),
1248 name_list
[ent
->sam_entry_index
].acct_name
));
1249 ent
->sam_entry_index
++;
1255 DEBUG(10, ("got gid %lu for group %lu\n",
1256 (unsigned long)group_gid
,
1257 (unsigned long)name_list
[ent
->sam_entry_index
].rid
));
1259 /* Fill in group entry */
1261 fill_domain_username(domain_group_name
, ent
->domain_name
,
1262 name_list
[ent
->sam_entry_index
].acct_name
, True
);
1264 result
= fill_grent(&group_list
[group_list_ndx
],
1266 name_list
[ent
->sam_entry_index
].acct_name
,
1269 /* Fill in group membership entry */
1272 size_t num_gr_mem
= 0;
1274 group_list
[group_list_ndx
].num_gr_mem
= 0;
1278 /* Get group membership */
1279 if (state
->request
.cmd
== WINBINDD_GETGRLST
) {
1282 sid_copy(&member_sid
, &domain
->sid
);
1283 sid_append_rid(&member_sid
, name_list
[ent
->sam_entry_index
].rid
);
1284 result
= fill_grent_mem(
1290 &gr_mem
, &gr_mem_len
);
1292 group_list
[group_list_ndx
].num_gr_mem
= (uint32
)num_gr_mem
;
1297 /* Append to group membership list */
1298 gr_mem_list
= (char *)SMB_REALLOC(
1299 gr_mem_list
, gr_mem_list_len
+ gr_mem_len
);
1302 (group_list
[group_list_ndx
].num_gr_mem
!= 0)) {
1303 DEBUG(0, ("out of memory\n"));
1304 gr_mem_list_len
= 0;
1308 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1309 gr_mem_list_len
, (unsigned int)gr_mem_len
));
1311 memcpy(&gr_mem_list
[gr_mem_list_len
], gr_mem
,
1316 group_list
[group_list_ndx
].gr_mem_ofs
=
1319 gr_mem_list_len
+= gr_mem_len
;
1322 ent
->sam_entry_index
++;
1324 /* Add group to return list */
1328 DEBUG(10, ("adding group num_entries = %d\n",
1329 state
->response
.data
.num_entries
));
1332 state
->response
.data
.num_entries
++;
1334 state
->response
.length
+=
1335 sizeof(struct winbindd_gr
);
1338 DEBUG(0, ("could not lookup domain group %s\n",
1339 domain_group_name
));
1343 /* Copy the list of group memberships to the end of the extra data */
1345 if (group_list_ndx
== 0)
1348 state
->response
.extra_data
.data
= SMB_REALLOC(
1349 state
->response
.extra_data
.data
,
1350 group_list_ndx
* sizeof(struct winbindd_gr
) + gr_mem_list_len
);
1352 if (!state
->response
.extra_data
.data
) {
1353 DEBUG(0, ("out of memory\n"));
1355 SAFE_FREE(gr_mem_list
);
1356 request_error(state
);
1360 memcpy(&((char *)state
->response
.extra_data
.data
)
1361 [group_list_ndx
* sizeof(struct winbindd_gr
)],
1362 gr_mem_list
, gr_mem_list_len
);
1364 state
->response
.length
+= gr_mem_list_len
;
1366 DEBUG(10, ("returning %d groups, length = %d\n",
1367 group_list_ndx
, gr_mem_list_len
));
1369 /* Out of domains */
1373 SAFE_FREE(gr_mem_list
);
1375 if (group_list_ndx
> 0)
1378 request_error(state
);
1381 /* List domain groups without mapping to unix ids */
1382 void winbindd_list_groups(struct winbindd_cli_state
*state
)
1384 winbindd_list_ent(state
, LIST_GROUPS
);
1387 /* Get user supplementary groups. This is much quicker than trying to
1388 invert the groups database. We merge the groups from the gids and
1389 other_sids info3 fields as trusted domain, universal group
1390 memberships, and nested groups (win2k native mode only) are not
1391 returned by the getgroups RPC call but are present in the info3. */
1393 struct getgroups_state
{
1394 struct winbindd_cli_state
*state
;
1395 struct winbindd_domain
*domain
;
1400 const DOM_SID
*token_sids
;
1401 size_t i
, num_token_sids
;
1404 size_t num_token_gids
;
1407 static void getgroups_usersid_recv(void *private_data
, bool success
,
1408 const DOM_SID
*sid
, enum lsa_SidType type
);
1409 static void getgroups_tokensids_recv(void *private_data
, bool success
,
1410 DOM_SID
*token_sids
, size_t num_token_sids
);
1411 static void getgroups_sid2gid_recv(void *private_data
, bool success
, gid_t gid
);
1413 void winbindd_getgroups(struct winbindd_cli_state
*state
)
1415 struct getgroups_state
*s
;
1417 /* Ensure null termination */
1418 state
->request
.data
.username
1419 [sizeof(state
->request
.data
.username
)-1]='\0';
1421 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state
->pid
,
1422 state
->request
.data
.username
));
1424 /* Parse domain and username */
1426 s
= TALLOC_P(state
->mem_ctx
, struct getgroups_state
);
1428 DEBUG(0, ("talloc failed\n"));
1429 request_error(state
);
1435 ws_name_return( state
->request
.data
.username
, WB_REPLACE_CHAR
);
1437 if (!parse_domain_user_talloc(state
->mem_ctx
,
1438 state
->request
.data
.username
,
1439 &s
->domname
, &s
->username
)) {
1440 DEBUG(5, ("Could not parse domain user: %s\n",
1441 state
->request
.data
.username
));
1443 /* error out if we do not have nested group support */
1445 if ( !lp_winbind_nested_groups() ) {
1446 request_error(state
);
1450 s
->domname
= talloc_strdup(state
->mem_ctx
,
1451 get_global_sam_name());
1452 s
->username
= talloc_strdup(state
->mem_ctx
,
1453 state
->request
.data
.username
);
1456 /* Get info for the domain (either by short domain name or
1457 DNS name in the case of a UPN) */
1459 s
->domain
= find_domain_from_name_noinit(s
->domname
);
1461 char *p
= strchr(s
->username
, '@');
1464 s
->domain
= find_domain_from_name_noinit(p
+1);
1469 if (s
->domain
== NULL
) {
1470 DEBUG(7, ("could not find domain entry for domain %s\n",
1472 request_error(state
);
1476 if ( s
->domain
->primary
&& lp_winbind_trusted_domains_only()) {
1477 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1478 "getgroups() for %s\\%s.\n", s
->domname
,
1480 request_error(state
);
1484 /* Get rid and name type from name. The following costs 1 packet */
1486 winbindd_lookupname_async(state
->mem_ctx
,
1487 s
->domname
, s
->username
,
1488 getgroups_usersid_recv
,
1489 WINBINDD_GETGROUPS
, s
);
1492 static void getgroups_usersid_recv(void *private_data
, bool success
,
1493 const DOM_SID
*sid
, enum lsa_SidType type
)
1495 struct getgroups_state
*s
=
1496 (struct getgroups_state
*)private_data
;
1499 ((type
!= SID_NAME_USER
) && (type
!= SID_NAME_COMPUTER
))) {
1500 request_error(s
->state
);
1504 sid_copy(&s
->user_sid
, sid
);
1506 winbindd_gettoken_async(s
->state
->mem_ctx
, &s
->user_sid
,
1507 getgroups_tokensids_recv
, s
);
1510 static void getgroups_tokensids_recv(void *private_data
, bool success
,
1511 DOM_SID
*token_sids
, size_t num_token_sids
)
1513 struct getgroups_state
*s
=
1514 (struct getgroups_state
*)private_data
;
1516 /* We need at least the user sid and the primary group in the token,
1517 * otherwise it's an error */
1519 if ((!success
) || (num_token_sids
< 2)) {
1520 request_error(s
->state
);
1524 s
->token_sids
= token_sids
;
1525 s
->num_token_sids
= num_token_sids
;
1528 s
->token_gids
= NULL
;
1529 s
->num_token_gids
= 0;
1531 getgroups_sid2gid_recv(s
, False
, 0);
1534 static void getgroups_sid2gid_recv(void *private_data
, bool success
, gid_t gid
)
1536 struct getgroups_state
*s
=
1537 (struct getgroups_state
*)private_data
;
1540 if (!add_gid_to_array_unique(s
->state
->mem_ctx
, gid
,
1542 &s
->num_token_gids
)) {
1547 if (s
->i
< s
->num_token_sids
) {
1548 const DOM_SID
*sid
= &s
->token_sids
[s
->i
];
1551 if (sid_equal(sid
, &s
->user_sid
)) {
1552 getgroups_sid2gid_recv(s
, False
, 0);
1556 winbindd_sid2gid_async(s
->state
->mem_ctx
, sid
,
1557 getgroups_sid2gid_recv
, s
);
1561 s
->state
->response
.data
.num_entries
= s
->num_token_gids
;
1562 if (s
->num_token_gids
) {
1563 /* s->token_gids are talloced */
1564 s
->state
->response
.extra_data
.data
=
1565 smb_xmemdup(s
->token_gids
,
1566 s
->num_token_gids
* sizeof(gid_t
));
1567 s
->state
->response
.length
+= s
->num_token_gids
* sizeof(gid_t
);
1569 request_ok(s
->state
);
1572 /* Get user supplementary sids. This is equivalent to the
1573 winbindd_getgroups() function but it involves a SID->SIDs mapping
1574 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1575 idmap. This call is designed to be used with applications that need
1576 to do ACL evaluation themselves. Note that the cached info3 data is
1579 this function assumes that the SID that comes in is a user SID. If
1580 you pass in another type of SID then you may get unpredictable
1584 static void getusersids_recv(void *private_data
, bool success
, DOM_SID
*sids
,
1587 void winbindd_getusersids(struct winbindd_cli_state
*state
)
1591 /* Ensure null termination */
1592 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1594 user_sid
= TALLOC_P(state
->mem_ctx
, DOM_SID
);
1595 if (user_sid
== NULL
) {
1596 DEBUG(1, ("talloc failed\n"));
1597 request_error(state
);
1601 if (!string_to_sid(user_sid
, state
->request
.data
.sid
)) {
1602 DEBUG(1, ("Could not get convert sid %s from string\n",
1603 state
->request
.data
.sid
));
1604 request_error(state
);
1608 winbindd_gettoken_async(state
->mem_ctx
, user_sid
, getusersids_recv
,
1612 static void getusersids_recv(void *private_data
, bool success
, DOM_SID
*sids
,
1615 struct winbindd_cli_state
*state
=
1616 (struct winbindd_cli_state
*)private_data
;
1618 unsigned ofs
, ret_size
= 0;
1622 request_error(state
);
1626 /* work out the response size */
1627 for (i
= 0; i
< num_sids
; i
++) {
1629 sid_to_fstring(s
, &sids
[i
]);
1630 ret_size
+= strlen(s
) + 1;
1633 /* build the reply */
1634 ret
= (char *)SMB_MALLOC(ret_size
);
1636 DEBUG(0, ("malloc failed\n"));
1637 request_error(state
);
1641 for (i
= 0; i
< num_sids
; i
++) {
1643 sid_to_fstring(s
, &sids
[i
]);
1644 safe_strcpy(ret
+ ofs
, s
, ret_size
- ofs
- 1);
1645 ofs
+= strlen(ret
+ofs
) + 1;
1648 /* Send data back to client */
1649 state
->response
.data
.num_entries
= num_sids
;
1650 state
->response
.extra_data
.data
= ret
;
1651 state
->response
.length
+= ret_size
;
1655 void winbindd_getuserdomgroups(struct winbindd_cli_state
*state
)
1658 struct winbindd_domain
*domain
;
1660 /* Ensure null termination */
1661 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1663 if (!string_to_sid(&user_sid
, state
->request
.data
.sid
)) {
1664 DEBUG(1, ("Could not get convert sid %s from string\n",
1665 state
->request
.data
.sid
));
1666 request_error(state
);
1670 /* Get info for the domain */
1671 if ((domain
= find_domain_from_sid_noinit(&user_sid
)) == NULL
) {
1672 DEBUG(0,("could not find domain entry for sid %s\n",
1673 sid_string_dbg(&user_sid
)));
1674 request_error(state
);
1678 sendto_domain(state
, domain
);
1681 enum winbindd_result
winbindd_dual_getuserdomgroups(struct winbindd_domain
*domain
,
1682 struct winbindd_cli_state
*state
)
1692 /* Ensure null termination */
1693 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1695 if (!string_to_sid(&user_sid
, state
->request
.data
.sid
)) {
1696 DEBUG(1, ("Could not get convert sid %s from string\n",
1697 state
->request
.data
.sid
));
1698 return WINBINDD_ERROR
;
1701 status
= domain
->methods
->lookup_usergroups(domain
, state
->mem_ctx
,
1702 &user_sid
, &num_groups
,
1704 if (!NT_STATUS_IS_OK(status
))
1705 return WINBINDD_ERROR
;
1707 if (num_groups
== 0) {
1708 state
->response
.data
.num_entries
= 0;
1709 state
->response
.extra_data
.data
= NULL
;
1713 if (!print_sidlist(state
->mem_ctx
,
1715 &sidstring
, &len
)) {
1716 DEBUG(0, ("talloc failed\n"));
1717 return WINBINDD_ERROR
;
1720 state
->response
.extra_data
.data
= SMB_STRDUP(sidstring
);
1721 if (!state
->response
.extra_data
.data
) {
1722 return WINBINDD_ERROR
;
1724 state
->response
.length
+= len
+1;
1725 state
->response
.data
.num_entries
= num_groups
;