2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 extern BOOL opt_nocache
;
31 #define DBGC_CLASS DBGC_WINBIND
33 /*********************************************************************
34 *********************************************************************/
36 static int gr_mem_buffer( char **buffer
, char **members
, int num_members
)
42 if ( num_members
== 0 ) {
47 for ( i
=0; i
<num_members
; i
++ )
48 len
+= strlen(members
[i
])+1;
50 *buffer
= (char*)smb_xmalloc(len
);
51 for ( i
=0; i
<num_members
; i
++ ) {
52 snprintf( &(*buffer
)[idx
], len
-idx
, "%s,", members
[i
]);
53 idx
+= strlen(members
[i
])+1;
55 /* terminate with NULL */
56 (*buffer
)[len
-1] = '\0';
61 /***************************************************************
62 Empty static struct for negative caching.
63 ****************************************************************/
65 /* Fill a grent structure from various other information */
67 static BOOL
fill_grent(struct winbindd_gr
*gr
, const char *dom_name
,
68 const char *gr_name
, gid_t unix_gid
)
70 fstring full_group_name
;
72 fill_domain_username(full_group_name
, dom_name
, gr_name
);
74 gr
->gr_gid
= unix_gid
;
76 /* Group name and password */
78 safe_strcpy(gr
->gr_name
, full_group_name
, sizeof(gr
->gr_name
) - 1);
79 safe_strcpy(gr
->gr_passwd
, "x", sizeof(gr
->gr_passwd
) - 1);
84 /* Fill in the group membership field of a NT group given by group_sid */
86 static BOOL
fill_grent_mem(struct winbindd_domain
*domain
,
88 enum SID_NAME_USE group_name_type
,
89 int *num_gr_mem
, char **gr_mem
, int *gr_mem_len
)
91 DOM_SID
**sid_mem
= NULL
;
93 uint32
*name_types
= NULL
;
94 unsigned int buf_len
, buf_ndx
, i
;
95 char **names
= NULL
, *buf
;
101 if (!(mem_ctx
= talloc_init("fill_grent_mem(%s)", domain
->name
)))
104 /* Initialise group membership information */
106 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string
, group_sid
)));
110 /* HACK ALERT!! This whole routine does not cope with group members
111 * from more than one domain, ie aliases. Thus we have to work it out
112 * ourselves in a special routine. */
114 if (domain
->internal
)
115 return fill_passdb_alias_grmem(domain
, group_sid
,
119 if ( !((group_name_type
==SID_NAME_DOM_GRP
) ||
120 ((group_name_type
==SID_NAME_ALIAS
) && domain
->primary
)) )
122 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
123 sid_to_string(sid_string
, group_sid
), domain
->name
,
128 /* Lookup group members */
129 status
= domain
->methods
->lookup_groupmem(domain
, mem_ctx
, group_sid
, &num_names
,
130 &sid_mem
, &names
, &name_types
);
131 if (!NT_STATUS_IS_OK(status
)) {
132 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
133 sid_to_string(sid_string
, group_sid
), domain
->name
, nt_errstr(status
)));
138 DEBUG(10, ("looked up %d names\n", num_names
));
140 if (DEBUGLEVEL
>= 10) {
141 for (i
= 0; i
< num_names
; i
++)
142 DEBUG(10, ("\t%20s %s %d\n", names
[i
], sid_to_string(sid_string
, sid_mem
[i
]),
146 /* Add members to list */
149 buf_len
= buf_ndx
= 0;
153 for (i
= 0; i
< num_names
; i
++) {
160 DEBUG(10, ("processing name %s\n", the_name
));
162 /* FIXME: need to cope with groups within groups. These
163 occur in Universal groups on a Windows 2000 native mode
166 /* make sure to allow machine accounts */
168 if (name_types
[i
] != SID_NAME_USER
&& name_types
[i
] != SID_NAME_COMPUTER
) {
169 DEBUG(3, ("name %s isn't a domain user\n", the_name
));
173 /* Append domain name */
175 fill_domain_username(name
, domain
->name
, the_name
);
179 /* Add to list or calculate buffer length */
182 buf_len
+= len
+ 1; /* List is comma separated */
184 DEBUG(10, ("buf_len + %d = %d\n", len
+ 1, buf_len
));
186 DEBUG(10, ("appending %s at ndx %d\n", name
, len
));
187 safe_strcpy(&buf
[buf_ndx
], name
, len
);
194 /* Allocate buffer */
196 if (!buf
&& buf_len
!= 0) {
197 if (!(buf
= malloc(buf_len
))) {
198 DEBUG(1, ("out of memory\n"));
202 memset(buf
, 0, buf_len
);
206 if (buf
&& buf_ndx
> 0) {
207 buf
[buf_ndx
- 1] = '\0';
211 *gr_mem_len
= buf_len
;
213 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem
,
214 buf_len
, *num_gr_mem
? buf
: "NULL"));
219 talloc_destroy(mem_ctx
);
221 DEBUG(10, ("fill_grent_mem returning %d\n", result
));
226 /* Return a group structure from a group name */
228 enum winbindd_result
winbindd_getgrnam(struct winbindd_cli_state
*state
)
232 struct winbindd_domain
*domain
;
233 enum SID_NAME_USE name_type
;
234 fstring name_domain
, name_group
;
239 /* Ensure null termination */
240 state
->request
.data
.groupname
[sizeof(state
->request
.data
.groupname
)-1]='\0';
242 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state
->pid
,
243 state
->request
.data
.groupname
));
245 /* Parse domain and groupname */
247 memset(name_group
, 0, sizeof(fstring
));
249 tmp
= state
->request
.data
.groupname
;
251 parse_domain_user(tmp
, name_domain
, name_group
);
253 /* if no domain or our local domain, then do a local tdb search */
255 if ( (!*name_domain
|| strequal(name_domain
, get_global_sam_name())) &&
256 ((grp
= wb_getgrnam(name_group
)) != NULL
) ) {
260 memcpy( &state
->response
.data
.gr
, grp
, sizeof(WINBINDD_GR
) );
262 gr_mem_len
= gr_mem_buffer( &buffer
, grp
->gr_mem
, grp
->num_gr_mem
);
264 state
->response
.data
.gr
.gr_mem_ofs
= 0;
265 state
->response
.length
+= gr_mem_len
;
266 state
->response
.extra_data
= buffer
; /* give the memory away */
271 /* if no domain or our local domain and no local tdb group, default to
272 * our local domain for aliases */
274 if ( !*name_domain
|| strequal(name_domain
, get_global_sam_name()) ) {
275 fstrcpy(name_domain
, get_global_sam_name());
278 /* Get info for the domain */
280 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
281 DEBUG(3, ("could not get domain sid for domain %s\n",
283 return WINBINDD_ERROR
;
285 /* should we deal with users for our domain? */
287 if ( lp_winbind_trusted_domains_only() && domain
->primary
) {
288 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
289 name_domain
, name_group
));
290 return WINBINDD_ERROR
;
293 /* Get rid and name type from name */
295 if (!winbindd_lookup_sid_by_name(domain
, domain
->name
, name_group
, &group_sid
,
297 DEBUG(1, ("group %s in domain %s does not exist\n",
298 name_group
, name_domain
));
299 return WINBINDD_ERROR
;
302 if ( !((name_type
==SID_NAME_DOM_GRP
) ||
303 ((name_type
==SID_NAME_ALIAS
) && domain
->primary
) ||
304 ((name_type
==SID_NAME_ALIAS
) && domain
->internal
)) )
306 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
307 name_group
, name_type
));
308 return WINBINDD_ERROR
;
311 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid
, &gid
, 0))) {
312 DEBUG(1, ("error converting unix gid to sid\n"));
313 return WINBINDD_ERROR
;
316 if (!fill_grent(&state
->response
.data
.gr
, name_domain
,
318 !fill_grent_mem(domain
, &group_sid
, name_type
,
319 &state
->response
.data
.gr
.num_gr_mem
,
320 &gr_mem
, &gr_mem_len
)) {
321 return WINBINDD_ERROR
;
324 /* Group membership lives at start of extra data */
326 state
->response
.data
.gr
.gr_mem_ofs
= 0;
328 state
->response
.length
+= gr_mem_len
;
329 state
->response
.extra_data
= gr_mem
;
334 /* Return a group structure from a gid number */
336 enum winbindd_result
winbindd_getgrgid(struct winbindd_cli_state
*state
)
338 struct winbindd_domain
*domain
;
341 enum SID_NAME_USE name_type
;
347 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state
->pid
,
348 (unsigned long)state
->request
.data
.gid
));
350 /* Bug out if the gid isn't in the winbind range */
352 if ((state
->request
.data
.gid
< server_state
.gid_low
) ||
353 (state
->request
.data
.gid
> server_state
.gid_high
))
354 return WINBINDD_ERROR
;
356 /* alway try local tdb lookup first */
357 if ( ( grp
=wb_getgrgid(state
->request
.data
.gid
)) != NULL
) {
360 memcpy( &state
->response
.data
.gr
, grp
, sizeof(WINBINDD_GR
) );
362 gr_mem_len
= gr_mem_buffer( &buffer
, grp
->gr_mem
, grp
->num_gr_mem
);
364 state
->response
.data
.gr
.gr_mem_ofs
= 0;
365 state
->response
.length
+= gr_mem_len
;
366 state
->response
.extra_data
= buffer
; /* give away the memory */
371 /* Get rid from gid */
372 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid
, state
->request
.data
.gid
))) {
373 DEBUG(1, ("could not convert gid %lu to rid\n",
374 (unsigned long)state
->request
.data
.gid
));
375 return WINBINDD_ERROR
;
378 /* Get name from sid */
380 if (!winbindd_lookup_name_by_sid(&group_sid
, dom_name
, group_name
, &name_type
)) {
381 DEBUG(1, ("could not lookup sid\n"));
382 return WINBINDD_ERROR
;
385 /* Fill in group structure */
387 domain
= find_domain_from_sid(&group_sid
);
390 DEBUG(1,("Can't find domain from sid\n"));
391 return WINBINDD_ERROR
;
394 if ( !((name_type
==SID_NAME_DOM_GRP
) ||
395 ((name_type
==SID_NAME_ALIAS
) && domain
->primary
) ||
396 ((name_type
==SID_NAME_ALIAS
) && domain
->internal
)) )
398 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
399 group_name
, name_type
));
400 return WINBINDD_ERROR
;
403 if (!fill_grent(&state
->response
.data
.gr
, dom_name
, group_name
,
404 state
->request
.data
.gid
) ||
405 !fill_grent_mem(domain
, &group_sid
, name_type
,
406 &state
->response
.data
.gr
.num_gr_mem
,
407 &gr_mem
, &gr_mem_len
))
408 return WINBINDD_ERROR
;
410 /* Group membership lives at start of extra data */
412 state
->response
.data
.gr
.gr_mem_ofs
= 0;
414 state
->response
.length
+= gr_mem_len
;
415 state
->response
.extra_data
= gr_mem
;
421 * set/get/endgrent functions
424 /* "Rewind" file pointer for group database enumeration */
426 enum winbindd_result
winbindd_setgrent(struct winbindd_cli_state
*state
)
428 struct winbindd_domain
*domain
;
430 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state
->pid
));
432 /* Check user has enabled this */
434 if (!lp_winbind_enum_groups())
435 return WINBINDD_ERROR
;
437 /* Free old static data if it exists */
439 if (state
->getgrent_state
!= NULL
) {
440 free_getent_state(state
->getgrent_state
);
441 state
->getgrent_state
= NULL
;
444 /* Create sam pipes for each domain we know about */
446 for (domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
447 struct getent_state
*domain_state
;
449 /* Create a state record for this domain */
451 /* don't add our domaina if we are a PDC or if we
452 are a member of a Samba domain */
454 if ( lp_winbind_trusted_domains_only() && domain
->primary
)
460 if ((domain_state
= (struct getent_state
*)
461 malloc(sizeof(struct getent_state
))) == NULL
) {
462 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
463 return WINBINDD_ERROR
;
466 ZERO_STRUCTP(domain_state
);
468 fstrcpy(domain_state
->domain_name
, domain
->name
);
470 /* Add to list of open domains */
472 DLIST_ADD(state
->getgrent_state
, domain_state
);
475 state
->getgrent_initialized
= True
;
480 /* Close file pointer to ntdom group database */
482 enum winbindd_result
winbindd_endgrent(struct winbindd_cli_state
*state
)
484 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state
->pid
));
486 free_getent_state(state
->getgrent_state
);
487 state
->getgrent_initialized
= False
;
488 state
->getgrent_state
= NULL
;
493 /* Get the list of domain groups and domain aliases for a domain. We fill in
494 the sam_entries and num_sam_entries fields with domain group information.
495 The dispinfo_ndx field is incremented to the index of the next group to
496 fetch. Return True if some groups were returned, False otherwise. */
498 static BOOL
get_sam_group_entries(struct getent_state
*ent
)
502 struct acct_info
*name_list
= NULL
, *tmp_name_list
= NULL
;
505 struct acct_info
*sam_grp_entries
= NULL
;
506 struct winbindd_domain
*domain
;
508 if (ent
->got_sam_entries
)
511 if (!(mem_ctx
= talloc_init("get_sam_group_entries(%s)",
512 ent
->domain_name
))) {
513 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
517 /* Free any existing group info */
519 SAFE_FREE(ent
->sam_entries
);
520 ent
->num_sam_entries
= 0;
521 ent
->got_sam_entries
= True
;
523 /* Enumerate domain groups */
527 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
528 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent
->domain_name
));
532 /* always get the domain global groups */
534 status
= domain
->methods
->enum_dom_groups(domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
536 if (!NT_STATUS_IS_OK(status
)) {
537 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status
)));
542 /* Copy entries into return buffer */
545 if ( !(name_list
= malloc(sizeof(struct acct_info
) * num_entries
)) ) {
546 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
551 memcpy( name_list
, sam_grp_entries
, num_entries
* sizeof(struct acct_info
) );
554 ent
->num_sam_entries
= num_entries
;
556 /* get the domain local groups if we are a member of a native win2k domain
557 and are not using LDAP to get the groups */
559 if ( ( lp_security() != SEC_ADS
&& domain
->native_mode
560 && domain
->primary
) || domain
->internal
)
562 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
564 status
= domain
->methods
->enum_local_groups(domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
566 if ( !NT_STATUS_IS_OK(status
) ) {
567 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
571 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries
));
573 /* Copy entries into return buffer */
576 if ( !(tmp_name_list
= Realloc( name_list
, sizeof(struct acct_info
) * (ent
->num_sam_entries
+num_entries
))) )
578 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
581 SAFE_FREE( name_list
);
585 name_list
= tmp_name_list
;
587 memcpy( &name_list
[ent
->num_sam_entries
], sam_grp_entries
,
588 num_entries
* sizeof(struct acct_info
) );
591 ent
->num_sam_entries
+= num_entries
;
595 /* Fill in remaining fields */
597 ent
->sam_entries
= name_list
;
598 ent
->sam_entry_index
= 0;
600 result
= (ent
->num_sam_entries
> 0);
603 talloc_destroy(mem_ctx
);
608 /* Fetch next group entry from ntdom database */
610 #define MAX_GETGRENT_GROUPS 500
612 enum winbindd_result
winbindd_getgrent(struct winbindd_cli_state
*state
)
614 struct getent_state
*ent
;
615 struct winbindd_gr
*group_list
= NULL
;
616 int num_groups
, group_list_ndx
= 0, i
, gr_mem_list_len
= 0;
617 char *new_extra_data
, *gr_mem_list
= NULL
;
619 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state
->pid
));
621 /* Check user has enabled this */
623 if (!lp_winbind_enum_groups())
624 return WINBINDD_ERROR
;
626 num_groups
= MIN(MAX_GETGRENT_GROUPS
, state
->request
.data
.num_entries
);
628 if ((state
->response
.extra_data
=
629 malloc(num_groups
* sizeof(struct winbindd_gr
))) == NULL
)
630 return WINBINDD_ERROR
;
632 memset(state
->response
.extra_data
, '\0',
633 num_groups
* sizeof(struct winbindd_gr
) );
635 state
->response
.data
.num_entries
= 0;
637 group_list
= (struct winbindd_gr
*)state
->response
.extra_data
;
639 if (!state
->getgrent_initialized
)
640 winbindd_setgrent(state
);
642 if (!(ent
= state
->getgrent_state
))
643 return WINBINDD_ERROR
;
645 /* Start sending back groups */
647 for (i
= 0; i
< num_groups
; i
++) {
648 struct acct_info
*name_list
= NULL
;
649 fstring domain_group_name
;
653 char *gr_mem
, *new_gr_mem_list
;
655 struct winbindd_domain
*domain
;
657 /* Do we need to fetch another chunk of groups? */
661 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
662 ent
->sam_entry_index
, ent
->num_sam_entries
));
664 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
666 while(ent
&& !get_sam_group_entries(ent
)) {
667 struct getent_state
*next_ent
;
669 DEBUG(10, ("freeing state info for domain %s\n", ent
->domain_name
));
671 /* Free state information for this domain */
673 SAFE_FREE(ent
->sam_entries
);
675 next_ent
= ent
->next
;
676 DLIST_REMOVE(state
->getgrent_state
, ent
);
682 /* No more domains */
688 name_list
= ent
->sam_entries
;
691 find_domain_from_name(ent
->domain_name
))) {
692 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent
->domain_name
));
697 /* Lookup group info */
699 sid_copy(&group_sid
, &domain
->sid
);
700 sid_append_rid(&group_sid
, name_list
[ent
->sam_entry_index
].rid
);
702 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid
, &group_gid
, 0))) {
704 DEBUG(1, ("could not look up gid for group %s\n",
705 name_list
[ent
->sam_entry_index
].acct_name
));
707 ent
->sam_entry_index
++;
711 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid
,
712 (unsigned long)name_list
[ent
->sam_entry_index
].rid
));
714 /* Fill in group entry */
716 fill_domain_username(domain_group_name
, ent
->domain_name
,
717 name_list
[ent
->sam_entry_index
].acct_name
);
719 result
= fill_grent(&group_list
[group_list_ndx
],
721 name_list
[ent
->sam_entry_index
].acct_name
,
724 /* Fill in group membership entry */
728 group_list
[group_list_ndx
].num_gr_mem
= 0;
732 /* Get group membership */
733 if (state
->request
.cmd
== WINBINDD_GETGRLST
) {
736 sid_copy(&member_sid
, &domain
->sid
);
737 sid_append_rid(&member_sid
, name_list
[ent
->sam_entry_index
].rid
);
738 result
= fill_grent_mem(
742 &group_list
[group_list_ndx
].num_gr_mem
,
743 &gr_mem
, &gr_mem_len
);
748 /* Append to group membership list */
749 new_gr_mem_list
= Realloc(
751 gr_mem_list_len
+ gr_mem_len
);
753 if (!new_gr_mem_list
&& (group_list
[group_list_ndx
].num_gr_mem
!= 0)) {
754 DEBUG(0, ("out of memory\n"));
755 SAFE_FREE(gr_mem_list
);
760 DEBUG(10, ("list_len = %d, mem_len = %d\n",
761 gr_mem_list_len
, gr_mem_len
));
763 gr_mem_list
= new_gr_mem_list
;
765 memcpy(&gr_mem_list
[gr_mem_list_len
], gr_mem
,
770 group_list
[group_list_ndx
].gr_mem_ofs
=
773 gr_mem_list_len
+= gr_mem_len
;
776 ent
->sam_entry_index
++;
778 /* Add group to return list */
782 DEBUG(10, ("adding group num_entries = %d\n",
783 state
->response
.data
.num_entries
));
786 state
->response
.data
.num_entries
++;
788 state
->response
.length
+=
789 sizeof(struct winbindd_gr
);
792 DEBUG(0, ("could not lookup domain group %s\n",
797 /* Copy the list of group memberships to the end of the extra data */
799 if (group_list_ndx
== 0)
802 new_extra_data
= Realloc(
803 state
->response
.extra_data
,
804 group_list_ndx
* sizeof(struct winbindd_gr
) + gr_mem_list_len
);
806 if (!new_extra_data
) {
807 DEBUG(0, ("out of memory\n"));
809 SAFE_FREE(state
->response
.extra_data
);
810 SAFE_FREE(gr_mem_list
);
812 return WINBINDD_ERROR
;
815 state
->response
.extra_data
= new_extra_data
;
817 memcpy(&((char *)state
->response
.extra_data
)
818 [group_list_ndx
* sizeof(struct winbindd_gr
)],
819 gr_mem_list
, gr_mem_list_len
);
821 SAFE_FREE(gr_mem_list
);
823 state
->response
.length
+= gr_mem_list_len
;
825 DEBUG(10, ("returning %d groups, length = %d\n",
826 group_list_ndx
, gr_mem_list_len
));
832 return (group_list_ndx
> 0) ? WINBINDD_OK
: WINBINDD_ERROR
;
835 /* List domain groups without mapping to unix ids */
837 enum winbindd_result
winbindd_list_groups(struct winbindd_cli_state
*state
)
839 uint32 total_entries
= 0;
840 struct winbindd_domain
*domain
;
841 const char *which_domain
;
842 char *extra_data
= NULL
;
844 unsigned int extra_data_len
= 0, i
;
846 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state
->pid
));
848 /* Ensure null termination */
849 state
->request
.domain_name
[sizeof(state
->request
.domain_name
)-1]='\0';
850 which_domain
= state
->request
.domain_name
;
852 /* Enumerate over trusted domains */
854 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
855 struct getent_state groups
;
857 /* if we have a domain name restricting the request and this
858 one in the list doesn't match, then just bypass the remainder
861 if ( *which_domain
&& !strequal(which_domain
, domain
->name
) )
866 /* Get list of sam groups */
868 fstrcpy(groups
.domain_name
, domain
->name
);
870 get_sam_group_entries(&groups
);
872 if (groups
.num_sam_entries
== 0) {
873 /* this domain is empty or in an error state */
877 /* keep track the of the total number of groups seen so
878 far over all domains */
879 total_entries
+= groups
.num_sam_entries
;
881 /* Allocate some memory for extra data. Note that we limit
882 account names to sizeof(fstring) = 128 characters. */
883 ted
= Realloc(extra_data
, sizeof(fstring
) * total_entries
);
886 DEBUG(0,("failed to enlarge buffer!\n"));
887 SAFE_FREE(extra_data
);
888 return WINBINDD_ERROR
;
892 /* Pack group list into extra data fields */
893 for (i
= 0; i
< groups
.num_sam_entries
; i
++) {
894 char *group_name
= ((struct acct_info
*)
895 groups
.sam_entries
)[i
].acct_name
;
898 fill_domain_username(name
, domain
->name
, group_name
);
899 /* Append to extra data */
900 memcpy(&extra_data
[extra_data_len
], name
,
902 extra_data_len
+= strlen(name
);
903 extra_data
[extra_data_len
++] = ',';
906 SAFE_FREE(groups
.sam_entries
);
909 /* Assign extra_data fields in response structure */
911 extra_data
[extra_data_len
- 1] = '\0';
912 state
->response
.extra_data
= extra_data
;
913 state
->response
.length
+= extra_data_len
;
916 /* No domains may have responded but that's still OK so don't
922 static void add_gid_to_array_unique(gid_t gid
, gid_t
**gids
, int *num
)
926 for (i
=0; i
<*num
; i
++) {
927 if ((*gids
)[i
] == gid
)
931 *gids
= Realloc(*gids
, (*num
+1) * sizeof(gid_t
));
940 static void add_local_gids_from_sid(DOM_SID
*sid
, gid_t
**gids
, int *num
)
946 DEBUG(10, ("Adding local gids from SID: %s\n",
947 sid_string_static(sid
)));
949 /* Don't expand aliases if not explicitly activated -- for now
952 if (!lp_winbind_nested_groups())
955 /* Add nested group memberships */
957 if (!pdb_enum_alias_memberships(sid
, &aliases
, &num_aliases
))
960 for (j
=0; j
<num_aliases
; j
++) {
961 enum SID_NAME_USE type
;
963 if (!local_sid_to_gid(&gid
, &aliases
[j
], &type
)) {
964 DEBUG(1, ("Got an alias membership with no alias\n"));
968 if ((type
!= SID_NAME_ALIAS
) && (type
!= SID_NAME_WKN_GRP
)) {
969 DEBUG(1, ("Got an alias membership in a non-alias\n"));
973 add_gid_to_array_unique(gid
, gids
, num
);
978 static void add_gids_from_user_sid(DOM_SID
*sid
, gid_t
**gids
, int *num
)
980 DEBUG(10, ("Adding gids from user SID: %s\n",
981 sid_string_static(sid
)));
983 add_local_gids_from_sid(sid
, gids
, num
);
986 static void add_gids_from_group_sid(DOM_SID
*sid
, gid_t
**gids
, int *num
)
990 DEBUG(10, ("Adding gids from group SID: %s\n",
991 sid_string_static(sid
)));
993 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid
, &gid
, 0)))
994 add_gid_to_array_unique(gid
, gids
, num
);
996 add_local_gids_from_sid(sid
, gids
, num
);
999 /* Get user supplementary groups. This is much quicker than trying to
1000 invert the groups database. We merge the groups from the gids and
1001 other_sids info3 fields as trusted domain, universal group
1002 memberships, and nested groups (win2k native mode only) are not
1003 returned by the getgroups RPC call but are present in the info3. */
1005 enum winbindd_result
winbindd_getgroups(struct winbindd_cli_state
*state
)
1007 fstring name_domain
, name_user
;
1008 DOM_SID user_sid
, group_sid
;
1009 enum SID_NAME_USE name_type
;
1010 uint32 num_groups
= 0;
1011 uint32 num_gids
= 0;
1013 DOM_SID
**user_grpsids
;
1014 struct winbindd_domain
*domain
;
1015 enum winbindd_result result
= WINBINDD_ERROR
;
1016 gid_t
*gid_list
= NULL
;
1018 TALLOC_CTX
*mem_ctx
;
1019 NET_USER_INFO_3
*info3
= NULL
;
1021 /* Ensure null termination */
1022 state
->request
.data
.username
[sizeof(state
->request
.data
.username
)-1]='\0';
1024 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state
->pid
,
1025 state
->request
.data
.username
));
1027 if (!(mem_ctx
= talloc_init("winbindd_getgroups(%s)",
1028 state
->request
.data
.username
)))
1029 return WINBINDD_ERROR
;
1031 /* Parse domain and username */
1033 parse_domain_user(state
->request
.data
.username
,
1034 name_domain
, name_user
);
1036 /* Get info for the domain */
1038 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
1039 DEBUG(7, ("could not find domain entry for domain %s\n",
1044 if ( domain
->primary
&& lp_winbind_trusted_domains_only()) {
1045 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
1046 name_domain
, name_user
));
1047 return WINBINDD_ERROR
;
1050 /* Get rid and name type from name. The following costs 1 packet */
1052 if (!winbindd_lookup_sid_by_name(domain
, domain
->name
, name_user
, &user_sid
,
1054 DEBUG(1, ("user '%s' does not exist\n", name_user
));
1058 if (name_type
!= SID_NAME_USER
&& name_type
!= SID_NAME_COMPUTER
) {
1059 DEBUG(1, ("name '%s' is not a user name: %d\n",
1060 name_user
, name_type
));
1064 add_gids_from_user_sid(&user_sid
, &gid_list
, &num_gids
);
1066 /* Treat the info3 cache as authoritative as the
1067 lookup_usergroups() function may return cached data. */
1069 if ( !opt_nocache
&& (info3
= netsamlogon_cache_get(mem_ctx
, &user_sid
))) {
1071 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
1072 info3
->num_groups2
, info3
->num_other_sids
));
1074 num_groups
= info3
->num_other_sids
+ info3
->num_groups2
;
1076 /* Go through each other sid and convert it to a gid */
1078 for (i
= 0; i
< info3
->num_other_sids
; i
++) {
1081 enum SID_NAME_USE sid_type
;
1083 /* Is this sid known to us? It can either be
1084 a trusted domain sid or a foreign sid. */
1086 if (!winbindd_lookup_name_by_sid( &info3
->other_sids
[i
].sid
,
1087 dom_name
, name
, &sid_type
))
1089 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
1090 sid_string_static(&info3
->other_sids
[i
].sid
)));
1094 /* Check it is a domain group or an alias (domain local group)
1095 in a win2k native mode domain. */
1097 if ( !((sid_type
==SID_NAME_DOM_GRP
) ||
1098 ((sid_type
==SID_NAME_ALIAS
) && domain
->primary
)) )
1100 DEBUG(10, ("winbindd_getgroups: sid type %d "
1101 "for %s is not a domain group\n",
1104 &info3
->other_sids
[i
].sid
)));
1108 add_gids_from_group_sid(&info3
->other_sids
[i
].sid
,
1109 &gid_list
, &num_gids
);
1111 if (gid_list
== NULL
)
1115 for (i
= 0; i
< info3
->num_groups2
; i
++) {
1117 /* create the group SID */
1119 sid_copy( &group_sid
, &domain
->sid
);
1120 sid_append_rid( &group_sid
, info3
->gids
[i
].g_rid
);
1122 add_gids_from_group_sid(&group_sid
, &gid_list
,
1125 if (gid_list
== NULL
)
1132 status
= domain
->methods
->lookup_usergroups(domain
, mem_ctx
,
1133 &user_sid
, &num_groups
,
1135 if (!NT_STATUS_IS_OK(status
))
1138 if (state
->response
.extra_data
)
1141 for (i
= 0; i
< num_groups
; i
++) {
1142 add_gids_from_group_sid(user_grpsids
[i
],
1143 &gid_list
, &num_gids
);
1145 if (gid_list
== NULL
)
1150 remove_duplicate_gids( &num_gids
, gid_list
);
1152 /* Send data back to client */
1154 state
->response
.data
.num_entries
= num_gids
;
1155 state
->response
.extra_data
= gid_list
;
1156 state
->response
.length
+= num_gids
* sizeof(gid_t
);
1158 result
= WINBINDD_OK
;
1162 talloc_destroy(mem_ctx
);
1167 static void add_sid_to_array_unique(TALLOC_CTX
*mem_ctx
, const DOM_SID
*sid
,
1168 DOM_SID
***sids
, int *num_sids
)
1172 for (i
=0; i
<(*num_sids
); i
++) {
1173 if (sid_compare(sid
, (*sids
)[i
]) == 0)
1177 *sids
= talloc_realloc(mem_ctx
, *sids
, sizeof(**sids
) * (*num_sids
+1));
1182 (*sids
)[*num_sids
] = talloc(mem_ctx
, sizeof(DOM_SID
));
1183 sid_copy((*sids
)[*num_sids
], sid
);
1188 static void add_local_sids_from_sid(TALLOC_CTX
*mem_ctx
, const DOM_SID
*sid
,
1189 DOM_SID
***user_grpsids
,
1192 DOM_SID
*aliases
= NULL
;
1193 int i
, num_aliases
= 0;
1195 if (!pdb_enum_alias_memberships(sid
, &aliases
, &num_aliases
))
1198 if (num_aliases
== 0)
1201 for (i
=0; i
<num_aliases
; i
++)
1202 add_sid_to_array_unique(mem_ctx
, &aliases
[i
], user_grpsids
,
1210 /* Get user supplementary sids. This is equivalent to the
1211 winbindd_getgroups() function but it involves a SID->SIDs mapping
1212 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1213 idmap. This call is designed to be used with applications that need
1214 to do ACL evaluation themselves. Note that the cached info3 data is
1217 this function assumes that the SID that comes in is a user SID. If
1218 you pass in another type of SID then you may get unpredictable
1221 enum winbindd_result
winbindd_getusersids(struct winbindd_cli_state
*state
)
1225 DOM_SID
**user_grpsids
;
1226 struct winbindd_domain
*domain
;
1227 enum winbindd_result result
= WINBINDD_ERROR
;
1229 TALLOC_CTX
*mem_ctx
;
1232 unsigned ofs
, ret_size
= 0;
1234 /* Ensure null termination */
1235 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1237 if (!string_to_sid(&user_sid
, state
->request
.data
.sid
)) {
1238 DEBUG(1, ("Could not get convert sid %s from string\n", state
->request
.data
.sid
));
1239 return WINBINDD_ERROR
;
1242 if (!(mem_ctx
= talloc_init("winbindd_getusersids(%s)",
1243 state
->request
.data
.username
))) {
1244 return WINBINDD_ERROR
;
1247 /* Get info for the domain */
1248 if ((domain
= find_domain_from_sid(&user_sid
)) == NULL
) {
1249 DEBUG(0,("could not find domain entry for sid %s\n",
1250 sid_string_static(&user_sid
)));
1254 status
= domain
->methods
->lookup_usergroups(domain
, mem_ctx
,
1255 &user_sid
, &num_groups
,
1257 if (!NT_STATUS_IS_OK(status
))
1260 if (num_groups
== 0) {
1264 if (lp_winbind_nested_groups()) {
1266 /* num_groups is changed during the loop, that's why we have
1267 to count down here.*/
1269 for (k
=num_groups
-1; k
>=0; k
--) {
1270 add_local_sids_from_sid(mem_ctx
, user_grpsids
[k
],
1271 &user_grpsids
, &num_groups
);
1274 add_local_sids_from_sid(mem_ctx
, &user_sid
, &user_grpsids
,
1278 /* work out the response size */
1279 for (i
= 0; i
< num_groups
; i
++) {
1280 const char *s
= sid_string_static(user_grpsids
[i
]);
1281 ret_size
+= strlen(s
) + 1;
1284 /* build the reply */
1285 ret
= malloc(ret_size
);
1286 if (!ret
) goto done
;
1288 for (i
= 0; i
< num_groups
; i
++) {
1289 const char *s
= sid_string_static(user_grpsids
[i
]);
1290 safe_strcpy(ret
+ ofs
, s
, ret_size
- ofs
- 1);
1291 ofs
+= strlen(ret
+ofs
) + 1;
1295 /* Send data back to client */
1296 state
->response
.data
.num_entries
= num_groups
;
1297 state
->response
.extra_data
= ret
;
1298 state
->response
.length
+= ret_size
;
1299 result
= WINBINDD_OK
;
1302 talloc_destroy(mem_ctx
);