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.
29 #define DBGC_CLASS DBGC_WINBIND
31 /*********************************************************************
32 *********************************************************************/
34 static int gr_mem_buffer( char **buffer
, char **members
, int num_members
)
40 if ( num_members
== 0 ) {
45 for ( i
=0; i
<num_members
; i
++ )
46 len
+= strlen(members
[i
])+1;
48 *buffer
= (char*)smb_xmalloc(len
);
49 for ( i
=0; i
<num_members
; i
++ ) {
50 snprintf( &(*buffer
)[idx
], len
-idx
, "%s,", members
[i
]);
51 idx
+= strlen(members
[i
])+1;
53 /* terminate with NULL */
54 (*buffer
)[len
-1] = '\0';
59 /***************************************************************
60 Empty static struct for negative caching.
61 ****************************************************************/
63 /* Fill a grent structure from various other information */
65 static BOOL
fill_grent(struct winbindd_gr
*gr
, const char *dom_name
,
66 const char *gr_name
, gid_t unix_gid
)
68 fstring full_group_name
;
70 fill_domain_username(full_group_name
, dom_name
, gr_name
);
72 gr
->gr_gid
= unix_gid
;
74 /* Group name and password */
76 safe_strcpy(gr
->gr_name
, full_group_name
, sizeof(gr
->gr_name
) - 1);
77 safe_strcpy(gr
->gr_passwd
, "x", sizeof(gr
->gr_passwd
) - 1);
82 /* Fill in the group membership field of a NT group given by group_sid */
84 static BOOL
fill_grent_mem(struct winbindd_domain
*domain
,
86 enum SID_NAME_USE group_name_type
,
87 int *num_gr_mem
, char **gr_mem
, int *gr_mem_len
)
89 DOM_SID
**sid_mem
= NULL
;
91 uint32
*name_types
= NULL
;
92 unsigned int buf_len
, buf_ndx
, i
;
93 char **names
= NULL
, *buf
;
99 if (!(mem_ctx
= talloc_init("fill_grent_mem(%s)", domain
->name
)))
102 /* Initialise group membership information */
104 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string
, group_sid
)));
108 if ( !((group_name_type
==SID_NAME_DOM_GRP
) ||
109 ((group_name_type
==SID_NAME_ALIAS
) && domain
->primary
)) )
111 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
112 sid_to_string(sid_string
, group_sid
), domain
->name
,
117 /* Lookup group members */
118 status
= domain
->methods
->lookup_groupmem(domain
, mem_ctx
, group_sid
, &num_names
,
119 &sid_mem
, &names
, &name_types
);
120 if (!NT_STATUS_IS_OK(status
)) {
121 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n",
122 sid_to_string(sid_string
, group_sid
), domain
->name
, nt_errstr(status
)));
127 DEBUG(10, ("looked up %d names\n", num_names
));
129 if (DEBUGLEVEL
>= 10) {
130 for (i
= 0; i
< num_names
; i
++)
131 DEBUG(10, ("\t%20s %s %d\n", names
[i
], sid_to_string(sid_string
, sid_mem
[i
]),
135 /* Add members to list */
138 buf_len
= buf_ndx
= 0;
142 for (i
= 0; i
< num_names
; i
++) {
149 DEBUG(10, ("processing name %s\n", the_name
));
151 /* FIXME: need to cope with groups within groups. These
152 occur in Universal groups on a Windows 2000 native mode
155 /* make sure to allow machine accounts */
157 if (name_types
[i
] != SID_NAME_USER
&& name_types
[i
] != SID_NAME_COMPUTER
) {
158 DEBUG(3, ("name %s isn't a domain user\n", the_name
));
162 /* Append domain name */
164 fill_domain_username(name
, domain
->name
, the_name
);
168 /* Add to list or calculate buffer length */
171 buf_len
+= len
+ 1; /* List is comma separated */
173 DEBUG(10, ("buf_len + %d = %d\n", len
+ 1, buf_len
));
175 DEBUG(10, ("appending %s at ndx %d\n", name
, len
));
176 safe_strcpy(&buf
[buf_ndx
], name
, len
);
183 /* Allocate buffer */
185 if (!buf
&& buf_len
!= 0) {
186 if (!(buf
= malloc(buf_len
))) {
187 DEBUG(1, ("out of memory\n"));
191 memset(buf
, 0, buf_len
);
195 if (buf
&& buf_ndx
> 0) {
196 buf
[buf_ndx
- 1] = '\0';
200 *gr_mem_len
= buf_len
;
202 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem
,
203 buf_len
, *num_gr_mem
? buf
: "NULL"));
208 talloc_destroy(mem_ctx
);
210 DEBUG(10, ("fill_grent_mem returning %d\n", result
));
215 /* Return a group structure from a group name */
217 enum winbindd_result
winbindd_getgrnam(struct winbindd_cli_state
*state
)
221 struct winbindd_domain
*domain
;
222 enum SID_NAME_USE name_type
;
223 fstring name_domain
, name_group
;
228 /* Ensure null termination */
229 state
->request
.data
.groupname
[sizeof(state
->request
.data
.groupname
)-1]='\0';
231 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state
->pid
,
232 state
->request
.data
.groupname
));
234 /* Parse domain and groupname */
236 memset(name_group
, 0, sizeof(fstring
));
238 tmp
= state
->request
.data
.groupname
;
240 parse_domain_user(tmp
, name_domain
, name_group
);
242 /* if no domain or our local domain, then do a local tdb search */
244 if ( !*name_domain
|| strequal(name_domain
, get_global_sam_name()) ) {
247 if ( !(grp
=wb_getgrnam(name_group
)) ) {
248 DEBUG(5,("winbindd_getgrnam: lookup for %s\\%s failed\n",
249 name_domain
, name_group
));
250 return WINBINDD_ERROR
;
252 memcpy( &state
->response
.data
.gr
, grp
, sizeof(WINBINDD_GR
) );
254 gr_mem_len
= gr_mem_buffer( &buffer
, grp
->gr_mem
, grp
->num_gr_mem
);
256 state
->response
.data
.gr
.gr_mem_ofs
= 0;
257 state
->response
.length
+= gr_mem_len
;
258 state
->response
.extra_data
= buffer
; /* give the memory away */
263 /* Get info for the domain */
265 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
266 DEBUG(3, ("could not get domain sid for domain %s\n",
268 return WINBINDD_ERROR
;
270 /* should we deal with users for our domain? */
272 if ( lp_winbind_trusted_domains_only() && domain
->primary
) {
273 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n",
274 name_domain
, name_group
));
275 return WINBINDD_ERROR
;
278 /* Get rid and name type from name */
280 if (!winbindd_lookup_sid_by_name(domain
, name_group
, &group_sid
,
282 DEBUG(1, ("group %s in domain %s does not exist\n",
283 name_group
, name_domain
));
284 return WINBINDD_ERROR
;
287 if ( !((name_type
==SID_NAME_DOM_GRP
) ||
288 ((name_type
==SID_NAME_ALIAS
) && domain
->primary
)) )
290 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
291 name_group
, name_type
));
292 return WINBINDD_ERROR
;
295 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid
, &gid
, 0))) {
296 DEBUG(1, ("error converting unix gid to sid\n"));
297 return WINBINDD_ERROR
;
300 if (!fill_grent(&state
->response
.data
.gr
, name_domain
,
302 !fill_grent_mem(domain
, &group_sid
, name_type
,
303 &state
->response
.data
.gr
.num_gr_mem
,
304 &gr_mem
, &gr_mem_len
)) {
305 return WINBINDD_ERROR
;
308 /* Group membership lives at start of extra data */
310 state
->response
.data
.gr
.gr_mem_ofs
= 0;
312 state
->response
.length
+= gr_mem_len
;
313 state
->response
.extra_data
= gr_mem
;
318 /* Return a group structure from a gid number */
320 enum winbindd_result
winbindd_getgrgid(struct winbindd_cli_state
*state
)
322 struct winbindd_domain
*domain
;
325 enum SID_NAME_USE name_type
;
331 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state
->pid
,
332 (unsigned long)state
->request
.data
.gid
));
334 /* Bug out if the gid isn't in the winbind range */
336 if ((state
->request
.data
.gid
< server_state
.gid_low
) ||
337 (state
->request
.data
.gid
> server_state
.gid_high
))
338 return WINBINDD_ERROR
;
340 /* alway try local tdb lookup first */
341 if ( ( grp
=wb_getgrgid(state
->request
.data
.gid
)) != NULL
) {
344 memcpy( &state
->response
.data
.gr
, grp
, sizeof(WINBINDD_GR
) );
346 gr_mem_len
= gr_mem_buffer( &buffer
, grp
->gr_mem
, grp
->num_gr_mem
);
348 state
->response
.data
.gr
.gr_mem_ofs
= 0;
349 state
->response
.length
+= gr_mem_len
;
350 state
->response
.extra_data
= buffer
; /* give away the memory */
355 /* Get rid from gid */
356 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid
, state
->request
.data
.gid
))) {
357 DEBUG(1, ("could not convert gid %lu to rid\n",
358 (unsigned long)state
->request
.data
.gid
));
359 return WINBINDD_ERROR
;
362 /* Get name from sid */
364 if (!winbindd_lookup_name_by_sid(&group_sid
, dom_name
, group_name
, &name_type
)) {
365 DEBUG(1, ("could not lookup sid\n"));
366 return WINBINDD_ERROR
;
369 /* Fill in group structure */
371 domain
= find_domain_from_sid(&group_sid
);
374 DEBUG(1,("Can't find domain from sid\n"));
375 return WINBINDD_ERROR
;
378 if ( !((name_type
==SID_NAME_DOM_GRP
) ||
379 ((name_type
==SID_NAME_ALIAS
) && domain
->primary
) ))
381 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
382 group_name
, name_type
));
383 return WINBINDD_ERROR
;
386 if (!fill_grent(&state
->response
.data
.gr
, dom_name
, group_name
,
387 state
->request
.data
.gid
) ||
388 !fill_grent_mem(domain
, &group_sid
, name_type
,
389 &state
->response
.data
.gr
.num_gr_mem
,
390 &gr_mem
, &gr_mem_len
))
391 return WINBINDD_ERROR
;
393 /* Group membership lives at start of extra data */
395 state
->response
.data
.gr
.gr_mem_ofs
= 0;
397 state
->response
.length
+= gr_mem_len
;
398 state
->response
.extra_data
= gr_mem
;
404 * set/get/endgrent functions
407 /* "Rewind" file pointer for group database enumeration */
409 enum winbindd_result
winbindd_setgrent(struct winbindd_cli_state
*state
)
411 struct winbindd_domain
*domain
;
413 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state
->pid
));
415 /* Check user has enabled this */
417 if (!lp_winbind_enum_groups())
418 return WINBINDD_ERROR
;
420 /* Free old static data if it exists */
422 if (state
->getgrent_state
!= NULL
) {
423 free_getent_state(state
->getgrent_state
);
424 state
->getgrent_state
= NULL
;
427 /* Create sam pipes for each domain we know about */
429 for (domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
430 struct getent_state
*domain_state
;
433 /* don't add our domaina if we are a PDC or if we
434 are a member of a Samba domain */
436 if ( (IS_DC
|| lp_winbind_trusted_domains_only())
442 /* Create a state record for this domain */
444 if ((domain_state
= (struct getent_state
*)
445 malloc(sizeof(struct getent_state
))) == NULL
) {
446 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
447 return WINBINDD_ERROR
;
450 ZERO_STRUCTP(domain_state
);
452 fstrcpy(domain_state
->domain_name
, domain
->name
);
454 /* Add to list of open domains */
456 DLIST_ADD(state
->getgrent_state
, domain_state
);
462 /* Close file pointer to ntdom group database */
464 enum winbindd_result
winbindd_endgrent(struct winbindd_cli_state
*state
)
466 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state
->pid
));
468 free_getent_state(state
->getgrent_state
);
469 state
->getgrent_state
= NULL
;
474 /* Get the list of domain groups and domain aliases for a domain. We fill in
475 the sam_entries and num_sam_entries fields with domain group information.
476 The dispinfo_ndx field is incremented to the index of the next group to
477 fetch. Return True if some groups were returned, False otherwise. */
479 #define MAX_FETCH_SAM_ENTRIES 100
481 static BOOL
get_sam_group_entries(struct getent_state
*ent
)
485 struct acct_info
*name_list
= NULL
, *tmp_name_list
= NULL
;
488 struct acct_info
*sam_grp_entries
= NULL
;
489 struct winbindd_domain
*domain
;
491 if (ent
->got_sam_entries
)
494 if (!(mem_ctx
= talloc_init("get_sam_group_entries(%s)",
495 ent
->domain_name
))) {
496 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
500 /* Free any existing group info */
502 SAFE_FREE(ent
->sam_entries
);
503 ent
->num_sam_entries
= 0;
504 ent
->got_sam_entries
= True
;
506 /* Enumerate domain groups */
510 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
511 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent
->domain_name
));
515 /* always get the domain global groups */
517 status
= domain
->methods
->enum_dom_groups(domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
519 if (!NT_STATUS_IS_OK(status
)) {
520 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status
)));
525 /* Copy entries into return buffer */
528 if ( !(name_list
= malloc(sizeof(struct acct_info
) * num_entries
)) ) {
529 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
534 memcpy( name_list
, sam_grp_entries
, num_entries
* sizeof(struct acct_info
) );
537 ent
->num_sam_entries
= num_entries
;
539 /* get the domain local groups if we are a member of a native win2k domain
540 and are not using LDAP to get the groups */
542 if ( lp_security() != SEC_ADS
&& domain
->native_mode
545 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
547 status
= domain
->methods
->enum_local_groups(domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
549 if ( !NT_STATUS_IS_OK(status
) ) {
550 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
554 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries
));
556 /* Copy entries into return buffer */
559 if ( !(tmp_name_list
= Realloc( name_list
, sizeof(struct acct_info
) * (ent
->num_sam_entries
+num_entries
))) )
561 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
564 SAFE_FREE( name_list
);
568 name_list
= tmp_name_list
;
570 memcpy( &name_list
[ent
->num_sam_entries
], sam_grp_entries
,
571 num_entries
* sizeof(struct acct_info
) );
574 ent
->num_sam_entries
+= num_entries
;
578 /* Fill in remaining fields */
580 ent
->sam_entries
= name_list
;
581 ent
->sam_entry_index
= 0;
583 result
= (ent
->num_sam_entries
> 0);
586 talloc_destroy(mem_ctx
);
591 /* Fetch next group entry from ntdom database */
593 #define MAX_GETGRENT_GROUPS 500
595 enum winbindd_result
winbindd_getgrent(struct winbindd_cli_state
*state
)
597 struct getent_state
*ent
;
598 struct winbindd_gr
*group_list
= NULL
;
599 int num_groups
, group_list_ndx
= 0, i
, gr_mem_list_len
= 0;
600 char *new_extra_data
, *gr_mem_list
= NULL
;
602 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state
->pid
));
604 /* Check user has enabled this */
606 if (!lp_winbind_enum_groups())
607 return WINBINDD_ERROR
;
609 num_groups
= MIN(MAX_GETGRENT_GROUPS
, state
->request
.data
.num_entries
);
611 if ((state
->response
.extra_data
=
612 malloc(num_groups
* sizeof(struct winbindd_gr
))) == NULL
)
613 return WINBINDD_ERROR
;
615 state
->response
.data
.num_entries
= 0;
617 group_list
= (struct winbindd_gr
*)state
->response
.extra_data
;
619 if (!(ent
= state
->getgrent_state
))
620 return WINBINDD_ERROR
;
622 /* Start sending back groups */
624 for (i
= 0; i
< num_groups
; i
++) {
625 struct acct_info
*name_list
= NULL
;
626 fstring domain_group_name
;
630 char *gr_mem
, *new_gr_mem_list
;
632 struct winbindd_domain
*domain
;
634 /* Do we need to fetch another chunk of groups? */
638 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
639 ent
->sam_entry_index
, ent
->num_sam_entries
));
641 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
643 while(ent
&& !get_sam_group_entries(ent
)) {
644 struct getent_state
*next_ent
;
646 DEBUG(10, ("freeing state info for domain %s\n", ent
->domain_name
));
648 /* Free state information for this domain */
650 SAFE_FREE(ent
->sam_entries
);
652 next_ent
= ent
->next
;
653 DLIST_REMOVE(state
->getgrent_state
, ent
);
659 /* No more domains */
665 name_list
= ent
->sam_entries
;
668 find_domain_from_name(ent
->domain_name
))) {
669 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent
->domain_name
));
674 /* Lookup group info */
676 sid_copy(&group_sid
, &domain
->sid
);
677 sid_append_rid(&group_sid
, name_list
[ent
->sam_entry_index
].rid
);
679 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid
, &group_gid
, 0))) {
681 DEBUG(1, ("could not look up gid for group %s\n",
682 name_list
[ent
->sam_entry_index
].acct_name
));
684 ent
->sam_entry_index
++;
688 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid
,
689 (unsigned long)name_list
[ent
->sam_entry_index
].rid
));
691 /* Fill in group entry */
693 fill_domain_username(domain_group_name
, ent
->domain_name
,
694 name_list
[ent
->sam_entry_index
].acct_name
);
696 result
= fill_grent(&group_list
[group_list_ndx
],
698 name_list
[ent
->sam_entry_index
].acct_name
,
701 /* Fill in group membership entry */
705 group_list
[group_list_ndx
].num_gr_mem
= 0;
709 /* Get group membership */
710 if (state
->request
.cmd
== WINBINDD_GETGRLST
) {
713 sid_copy(&member_sid
, &domain
->sid
);
714 sid_append_rid(&member_sid
, name_list
[ent
->sam_entry_index
].rid
);
715 result
= fill_grent_mem(
719 &group_list
[group_list_ndx
].num_gr_mem
,
720 &gr_mem
, &gr_mem_len
);
725 /* Append to group membership list */
726 new_gr_mem_list
= Realloc(
728 gr_mem_list_len
+ gr_mem_len
);
730 if (!new_gr_mem_list
&& (group_list
[group_list_ndx
].num_gr_mem
!= 0)) {
731 DEBUG(0, ("out of memory\n"));
732 SAFE_FREE(gr_mem_list
);
737 DEBUG(10, ("list_len = %d, mem_len = %d\n",
738 gr_mem_list_len
, gr_mem_len
));
740 gr_mem_list
= new_gr_mem_list
;
742 memcpy(&gr_mem_list
[gr_mem_list_len
], gr_mem
,
747 group_list
[group_list_ndx
].gr_mem_ofs
=
750 gr_mem_list_len
+= gr_mem_len
;
753 ent
->sam_entry_index
++;
755 /* Add group to return list */
759 DEBUG(10, ("adding group num_entries = %d\n",
760 state
->response
.data
.num_entries
));
763 state
->response
.data
.num_entries
++;
765 state
->response
.length
+=
766 sizeof(struct winbindd_gr
);
769 DEBUG(0, ("could not lookup domain group %s\n",
774 /* Copy the list of group memberships to the end of the extra data */
776 if (group_list_ndx
== 0)
779 new_extra_data
= Realloc(
780 state
->response
.extra_data
,
781 group_list_ndx
* sizeof(struct winbindd_gr
) + gr_mem_list_len
);
783 if (!new_extra_data
) {
784 DEBUG(0, ("out of memory\n"));
786 SAFE_FREE(state
->response
.extra_data
);
787 SAFE_FREE(gr_mem_list
);
789 return WINBINDD_ERROR
;
792 state
->response
.extra_data
= new_extra_data
;
794 memcpy(&((char *)state
->response
.extra_data
)
795 [group_list_ndx
* sizeof(struct winbindd_gr
)],
796 gr_mem_list
, gr_mem_list_len
);
798 SAFE_FREE(gr_mem_list
);
800 state
->response
.length
+= gr_mem_list_len
;
802 DEBUG(10, ("returning %d groups, length = %d\n",
803 group_list_ndx
, gr_mem_list_len
));
809 return (group_list_ndx
> 0) ? WINBINDD_OK
: WINBINDD_ERROR
;
812 /* List domain groups without mapping to unix ids */
814 enum winbindd_result
winbindd_list_groups(struct winbindd_cli_state
*state
)
816 uint32 total_entries
= 0;
817 struct winbindd_domain
*domain
;
818 const char *which_domain
;
819 char *extra_data
= NULL
;
821 unsigned int extra_data_len
= 0, i
;
823 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state
->pid
));
825 /* Ensure null termination */
826 state
->request
.domain_name
[sizeof(state
->request
.domain_name
)-1]='\0';
827 which_domain
= state
->request
.domain_name
;
829 /* Enumerate over trusted domains */
831 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
832 struct getent_state groups
;
834 /* if we have a domain name restricting the request and this
835 one in the list doesn't match, then just bypass the remainder
838 if ( *which_domain
&& !strequal(which_domain
, domain
->name
) )
843 /* Get list of sam groups */
845 fstrcpy(groups
.domain_name
, domain
->name
);
847 get_sam_group_entries(&groups
);
849 if (groups
.num_sam_entries
== 0) {
850 /* this domain is empty or in an error state */
854 /* keep track the of the total number of groups seen so
855 far over all domains */
856 total_entries
+= groups
.num_sam_entries
;
858 /* Allocate some memory for extra data. Note that we limit
859 account names to sizeof(fstring) = 128 characters. */
860 ted
= Realloc(extra_data
, sizeof(fstring
) * total_entries
);
863 DEBUG(0,("failed to enlarge buffer!\n"));
864 SAFE_FREE(extra_data
);
865 return WINBINDD_ERROR
;
869 /* Pack group list into extra data fields */
870 for (i
= 0; i
< groups
.num_sam_entries
; i
++) {
871 char *group_name
= ((struct acct_info
*)
872 groups
.sam_entries
)[i
].acct_name
;
875 fill_domain_username(name
, domain
->name
, group_name
);
876 /* Append to extra data */
877 memcpy(&extra_data
[extra_data_len
], name
,
879 extra_data_len
+= strlen(name
);
880 extra_data
[extra_data_len
++] = ',';
883 SAFE_FREE(groups
.sam_entries
);
886 /* Assign extra_data fields in response structure */
888 extra_data
[extra_data_len
- 1] = '\0';
889 state
->response
.extra_data
= extra_data
;
890 state
->response
.length
+= extra_data_len
;
893 /* No domains may have responded but that's still OK so don't
899 /* Get user supplementary groups. This is much quicker than trying to
900 invert the groups database. We merge the groups from the gids and
901 other_sids info3 fields as trusted domain, universal group
902 memberships, and nested groups (win2k native mode only) are not
903 returned by the getgroups RPC call but are present in the info3. */
905 enum winbindd_result
winbindd_getgroups(struct winbindd_cli_state
*state
)
907 fstring name_domain
, name_user
;
908 DOM_SID user_sid
, group_sid
;
909 enum SID_NAME_USE name_type
;
910 uint32 num_groups
= 0;
913 DOM_SID
**user_grpsids
;
914 struct winbindd_domain
*domain
;
915 enum winbindd_result result
= WINBINDD_ERROR
;
919 NET_USER_INFO_3
*info3
= NULL
;
921 /* Ensure null termination */
922 state
->request
.data
.username
[sizeof(state
->request
.data
.username
)-1]='\0';
924 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state
->pid
,
925 state
->request
.data
.username
));
927 if (!(mem_ctx
= talloc_init("winbindd_getgroups(%s)",
928 state
->request
.data
.username
)))
929 return WINBINDD_ERROR
;
931 /* Parse domain and username */
933 parse_domain_user(state
->request
.data
.username
,
934 name_domain
, name_user
);
936 /* Get info for the domain */
938 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
939 DEBUG(7, ("could not find domain entry for domain %s\n",
944 if ( domain
->primary
&& lp_winbind_trusted_domains_only()) {
945 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n",
946 name_domain
, name_user
));
947 return WINBINDD_ERROR
;
950 /* Get rid and name type from name. The following costs 1 packet */
952 if (!winbindd_lookup_sid_by_name(domain
, name_user
, &user_sid
,
954 DEBUG(1, ("user '%s' does not exist\n", name_user
));
958 if (name_type
!= SID_NAME_USER
&& name_type
!= SID_NAME_COMPUTER
) {
959 DEBUG(1, ("name '%s' is not a user name: %d\n",
960 name_user
, name_type
));
964 /* Treat the info3 cache as authoritative as the
965 lookup_usergroups() function may return cached data. */
967 if ((info3
= netsamlogon_cache_get(mem_ctx
, &user_sid
))) {
969 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n",
970 info3
->num_groups2
, info3
->num_other_sids
));
972 num_groups
= info3
->num_other_sids
+ info3
->num_groups2
;
973 gid_list
= calloc(sizeof(gid_t
), num_groups
);
975 /* Go through each other sid and convert it to a gid */
977 for (i
= 0; i
< info3
->num_other_sids
; i
++) {
980 enum SID_NAME_USE sid_type
;
982 /* Is this sid known to us? It can either be
983 a trusted domain sid or a foreign sid. */
985 if (!winbindd_lookup_name_by_sid( &info3
->other_sids
[i
].sid
,
986 dom_name
, name
, &sid_type
))
988 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n",
989 sid_string_static(&info3
->other_sids
[i
].sid
)));
993 /* Check it is a domain group or an alias (domain local group)
994 in a win2k native mode domain. */
996 if ( !((sid_type
==SID_NAME_DOM_GRP
) ||
997 ((sid_type
==SID_NAME_ALIAS
) && domain
->primary
)) )
999 DEBUG(10, ("winbindd_getgroups: sid type %d "
1000 "for %s is not a domain group\n",
1003 &info3
->other_sids
[i
].sid
)));
1009 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&info3
->other_sids
[i
].sid
, &gid_list
[num_gids
], 0)) )
1011 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1012 sid_string_static(&info3
->other_sids
[i
].sid
)));
1016 /* We've jumped through a lot of hoops to get here */
1018 DEBUG(10, ("winbindd_getgroups: mapped other sid %s to "
1019 "gid %lu\n", sid_string_static(
1020 &info3
->other_sids
[i
].sid
),
1021 (unsigned long)gid_list
[num_gids
]));
1026 for (i
= 0; i
< info3
->num_groups2
; i
++) {
1028 /* create the group SID */
1030 sid_copy( &group_sid
, &domain
->sid
);
1031 sid_append_rid( &group_sid
, info3
->gids
[i
].g_rid
);
1033 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid
, &gid_list
[num_gids
], 0)) ) {
1034 DEBUG(10, ("winbindd_getgroups: could not map sid %s to gid\n",
1035 sid_string_static(&group_sid
)));
1044 status
= domain
->methods
->lookup_usergroups(domain
, mem_ctx
,
1045 &user_sid
, &num_groups
,
1047 if (!NT_STATUS_IS_OK(status
))
1050 gid_list
= malloc(sizeof(gid_t
) * num_groups
);
1052 if (state
->response
.extra_data
)
1055 for (i
= 0; i
< num_groups
; i
++) {
1056 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(user_grpsids
[i
], &gid_list
[num_gids
], 0))) {
1057 DEBUG(1, ("unable to convert group sid %s to gid\n",
1058 sid_string_static(user_grpsids
[i
])));
1065 /* Send data back to client */
1067 state
->response
.data
.num_entries
= num_gids
;
1068 state
->response
.extra_data
= gid_list
;
1069 state
->response
.length
+= num_gids
* sizeof(gid_t
);
1071 result
= WINBINDD_OK
;
1075 talloc_destroy(mem_ctx
);
1081 /* Get user supplementary sids. This is equivalent to the
1082 winbindd_getgroups() function but it involves a SID->SIDs mapping
1083 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1084 idmap. This call is designed to be used with applications that need
1085 to do ACL evaluation themselves. Note that the cached info3 data is
1088 this function assumes that the SID that comes in is a user SID. If
1089 you pass in another type of SID then you may get unpredictable
1092 enum winbindd_result
winbindd_getusersids(struct winbindd_cli_state
*state
)
1096 DOM_SID
**user_grpsids
;
1097 struct winbindd_domain
*domain
;
1098 enum winbindd_result result
= WINBINDD_ERROR
;
1100 TALLOC_CTX
*mem_ctx
;
1103 unsigned ofs
, ret_size
= 0;
1105 /* Ensure null termination */
1106 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1108 if (!string_to_sid(&user_sid
, state
->request
.data
.sid
)) {
1109 DEBUG(1, ("Could not get convert sid %s from string\n", state
->request
.data
.sid
));
1110 return WINBINDD_ERROR
;
1113 if (!(mem_ctx
= talloc_init("winbindd_getusersids(%s)",
1114 state
->request
.data
.username
))) {
1115 return WINBINDD_ERROR
;
1118 /* Get info for the domain */
1119 if ((domain
= find_domain_from_sid(&user_sid
)) == NULL
) {
1120 DEBUG(0,("could not find domain entry for sid %s\n",
1121 sid_string_static(&user_sid
)));
1125 status
= domain
->methods
->lookup_usergroups(domain
, mem_ctx
,
1126 &user_sid
, &num_groups
,
1128 if (!NT_STATUS_IS_OK(status
))
1131 if (num_groups
== 0) {
1135 /* work out the response size */
1136 for (i
= 0; i
< num_groups
; i
++) {
1137 const char *s
= sid_string_static(user_grpsids
[i
]);
1138 ret_size
+= strlen(s
) + 1;
1141 /* build the reply */
1142 ret
= malloc(ret_size
);
1143 if (!ret
) goto done
;
1145 for (i
= 0; i
< num_groups
; i
++) {
1146 const char *s
= sid_string_static(user_grpsids
[i
]);
1147 safe_strcpy(ret
+ ofs
, s
, ret_size
- ofs
- 1);
1148 ofs
+= strlen(ret
+ofs
) + 1;
1152 /* Send data back to client */
1153 state
->response
.data
.num_entries
= num_groups
;
1154 state
->response
.extra_data
= ret
;
1155 state
->response
.length
+= ret_size
;
1156 result
= WINBINDD_OK
;
1159 talloc_destroy(mem_ctx
);