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 /* Add our locally defined groups */
429 state
->local_group_names
= NULL
;
430 state
->num_local_group_names
= 0;
431 state
->local_group_ndx
= 0;
433 wb_list_group_names(&state
->local_group_names
,
434 &state
->num_local_group_names
);
436 /* Create sam pipes for each domain we know about */
438 for (domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
439 struct getent_state
*domain_state
;
442 /* don't add our domaina if we are a PDC or if we
443 are a member of a Samba domain */
445 if ( (IS_DC
|| lp_winbind_trusted_domains_only())
451 /* Create a state record for this domain */
453 if ((domain_state
= (struct getent_state
*)
454 malloc(sizeof(struct getent_state
))) == NULL
) {
455 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n"));
456 return WINBINDD_ERROR
;
459 ZERO_STRUCTP(domain_state
);
461 fstrcpy(domain_state
->domain_name
, domain
->name
);
463 /* Add to list of open domains */
465 DLIST_ADD(state
->getgrent_state
, domain_state
);
471 /* Close file pointer to ntdom group database */
473 enum winbindd_result
winbindd_endgrent(struct winbindd_cli_state
*state
)
475 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state
->pid
));
477 free_getent_state(state
->getgrent_state
);
478 state
->getgrent_state
= NULL
;
483 /* Fetch group entries from local faked database */
485 static BOOL
return_local_winbind_groups(struct winbindd_cli_state
*state
)
490 int gr_mem_list_len
= 0;
491 struct winbindd_gr
*group_list
;
492 struct winbindd_gr
*gr
;
494 if (state
->local_group_names
== NULL
)
497 name
= state
->local_group_names
[state
->local_group_ndx
];
498 grp
= wb_getgrnam(name
);
501 DEBUG(3, ("Group %s vanished\n", name
));
503 /* Stop that stuff.. */
504 state
->local_group_ndx
= state
->num_local_group_names
;
509 gr_mem_list_len
= gr_mem_buffer( &buffer
, grp
->gr_mem
, grp
->num_gr_mem
);
511 state
->response
.extra_data
= malloc(sizeof(struct winbindd_gr
) +
513 state
->response
.length
+= sizeof(struct winbindd_gr
) + gr_mem_list_len
;
515 group_list
= (struct winbindd_gr
*)state
->response
.extra_data
;
517 if (group_list
== NULL
) {
518 DEBUG(0, ("Could not malloc group_list\n"));
526 gr
->gr_gid
= grp
->gr_gid
;
527 safe_strcpy(gr
->gr_name
, name
, sizeof(gr
->gr_name
) - 1);
528 safe_strcpy(gr
->gr_passwd
, "x", sizeof(gr
->gr_passwd
) - 1);
529 gr
->num_gr_mem
= grp
->num_gr_mem
;
532 memcpy(&((char *)state
->response
.extra_data
)
533 [sizeof(struct winbindd_gr
)],
534 buffer
, gr_mem_list_len
);
537 SAFE_FREE(grp
->gr_mem
);
539 state
->response
.data
.num_entries
= 1;
541 state
->local_group_ndx
+= 1;
543 if (state
->local_group_ndx
>= state
->num_local_group_names
) {
546 for (i
=0; i
<state
->num_local_group_names
; i
++) {
547 free(state
->local_group_names
[i
]);
549 free(state
->local_group_names
);
550 state
->local_group_names
= NULL
;
557 /* Get the list of domain groups and domain aliases for a domain. We fill in
558 the sam_entries and num_sam_entries fields with domain group information.
559 The dispinfo_ndx field is incremented to the index of the next group to
560 fetch. Return True if some groups were returned, False otherwise. */
562 #define MAX_FETCH_SAM_ENTRIES 100
564 static BOOL
get_sam_group_entries(struct getent_state
*ent
)
568 struct acct_info
*name_list
= NULL
, *tmp_name_list
= NULL
;
571 struct acct_info
*sam_grp_entries
= NULL
;
572 struct winbindd_domain
*domain
;
574 if (ent
->got_sam_entries
)
577 if (!(mem_ctx
= talloc_init("get_sam_group_entries(%s)",
578 ent
->domain_name
))) {
579 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n"));
583 /* Free any existing group info */
585 SAFE_FREE(ent
->sam_entries
);
586 ent
->num_sam_entries
= 0;
587 ent
->got_sam_entries
= True
;
589 /* Enumerate domain groups */
593 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
594 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent
->domain_name
));
598 /* always get the domain global groups */
600 status
= domain
->methods
->enum_dom_groups(domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
602 if (!NT_STATUS_IS_OK(status
)) {
603 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status
)));
608 /* Copy entries into return buffer */
611 if ( !(name_list
= malloc(sizeof(struct acct_info
) * num_entries
)) ) {
612 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n",
617 memcpy( name_list
, sam_grp_entries
, num_entries
* sizeof(struct acct_info
) );
620 ent
->num_sam_entries
= num_entries
;
622 /* get the domain local groups if we are a member of a native win2k domain
623 and are not using LDAP to get the groups */
625 if ( lp_security() != SEC_ADS
&& domain
->native_mode
628 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n"));
630 status
= domain
->methods
->enum_local_groups(domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
632 if ( !NT_STATUS_IS_OK(status
) ) {
633 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n"));
637 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries
));
639 /* Copy entries into return buffer */
642 if ( !(tmp_name_list
= Realloc( name_list
, sizeof(struct acct_info
) * (ent
->num_sam_entries
+num_entries
))) )
644 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n",
647 SAFE_FREE( name_list
);
651 name_list
= tmp_name_list
;
653 memcpy( &name_list
[ent
->num_sam_entries
], sam_grp_entries
,
654 num_entries
* sizeof(struct acct_info
) );
657 ent
->num_sam_entries
+= num_entries
;
661 /* Fill in remaining fields */
663 ent
->sam_entries
= name_list
;
664 ent
->sam_entry_index
= 0;
666 result
= (ent
->num_sam_entries
> 0);
669 talloc_destroy(mem_ctx
);
674 /* Fetch next group entry from ntdom database */
676 #define MAX_GETGRENT_GROUPS 500
678 enum winbindd_result
winbindd_getgrent(struct winbindd_cli_state
*state
)
680 struct getent_state
*ent
;
681 struct winbindd_gr
*group_list
= NULL
;
682 int num_groups
, group_list_ndx
= 0, i
, gr_mem_list_len
= 0;
683 char *new_extra_data
, *gr_mem_list
= NULL
;
685 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state
->pid
));
687 /* Check user has enabled this */
689 if (!lp_winbind_enum_groups())
690 return WINBINDD_ERROR
;
692 if (return_local_winbind_groups(state
))
695 num_groups
= MIN(MAX_GETGRENT_GROUPS
, state
->request
.data
.num_entries
);
697 if ((state
->response
.extra_data
=
698 malloc(num_groups
* sizeof(struct winbindd_gr
))) == NULL
)
699 return WINBINDD_ERROR
;
701 state
->response
.data
.num_entries
= 0;
703 group_list
= (struct winbindd_gr
*)state
->response
.extra_data
;
705 if (!(ent
= state
->getgrent_state
))
706 return WINBINDD_ERROR
;
708 /* Start sending back groups */
710 for (i
= 0; i
< num_groups
; i
++) {
711 struct acct_info
*name_list
= NULL
;
712 fstring domain_group_name
;
716 char *gr_mem
, *new_gr_mem_list
;
718 struct winbindd_domain
*domain
;
720 /* Do we need to fetch another chunk of groups? */
724 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
725 ent
->sam_entry_index
, ent
->num_sam_entries
));
727 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
729 while(ent
&& !get_sam_group_entries(ent
)) {
730 struct getent_state
*next_ent
;
732 DEBUG(10, ("freeing state info for domain %s\n", ent
->domain_name
));
734 /* Free state information for this domain */
736 SAFE_FREE(ent
->sam_entries
);
738 next_ent
= ent
->next
;
739 DLIST_REMOVE(state
->getgrent_state
, ent
);
745 /* No more domains */
751 name_list
= ent
->sam_entries
;
754 find_domain_from_name(ent
->domain_name
))) {
755 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent
->domain_name
));
760 /* Lookup group info */
762 sid_copy(&group_sid
, &domain
->sid
);
763 sid_append_rid(&group_sid
, name_list
[ent
->sam_entry_index
].rid
);
765 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid
, &group_gid
, 0))) {
767 DEBUG(1, ("could not look up gid for group %s\n",
768 name_list
[ent
->sam_entry_index
].acct_name
));
770 ent
->sam_entry_index
++;
774 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid
,
775 (unsigned long)name_list
[ent
->sam_entry_index
].rid
));
777 /* Fill in group entry */
779 fill_domain_username(domain_group_name
, ent
->domain_name
,
780 name_list
[ent
->sam_entry_index
].acct_name
);
782 result
= fill_grent(&group_list
[group_list_ndx
],
784 name_list
[ent
->sam_entry_index
].acct_name
,
787 /* Fill in group membership entry */
791 group_list
[group_list_ndx
].num_gr_mem
= 0;
795 /* Get group membership */
796 if (state
->request
.cmd
== WINBINDD_GETGRLST
) {
799 sid_copy(&member_sid
, &domain
->sid
);
800 sid_append_rid(&member_sid
, name_list
[ent
->sam_entry_index
].rid
);
801 result
= fill_grent_mem(
805 &group_list
[group_list_ndx
].num_gr_mem
,
806 &gr_mem
, &gr_mem_len
);
811 /* Append to group membership list */
812 new_gr_mem_list
= Realloc(
814 gr_mem_list_len
+ gr_mem_len
);
816 if (!new_gr_mem_list
&& (group_list
[group_list_ndx
].num_gr_mem
!= 0)) {
817 DEBUG(0, ("out of memory\n"));
818 SAFE_FREE(gr_mem_list
);
823 DEBUG(10, ("list_len = %d, mem_len = %d\n",
824 gr_mem_list_len
, gr_mem_len
));
826 gr_mem_list
= new_gr_mem_list
;
828 memcpy(&gr_mem_list
[gr_mem_list_len
], gr_mem
,
833 group_list
[group_list_ndx
].gr_mem_ofs
=
836 gr_mem_list_len
+= gr_mem_len
;
839 ent
->sam_entry_index
++;
841 /* Add group to return list */
845 DEBUG(10, ("adding group num_entries = %d\n",
846 state
->response
.data
.num_entries
));
849 state
->response
.data
.num_entries
++;
851 state
->response
.length
+=
852 sizeof(struct winbindd_gr
);
855 DEBUG(0, ("could not lookup domain group %s\n",
860 /* Copy the list of group memberships to the end of the extra data */
862 if (group_list_ndx
== 0)
865 new_extra_data
= Realloc(
866 state
->response
.extra_data
,
867 group_list_ndx
* sizeof(struct winbindd_gr
) + gr_mem_list_len
);
869 if (!new_extra_data
) {
870 DEBUG(0, ("out of memory\n"));
872 SAFE_FREE(state
->response
.extra_data
);
873 SAFE_FREE(gr_mem_list
);
875 return WINBINDD_ERROR
;
878 state
->response
.extra_data
= new_extra_data
;
880 memcpy(&((char *)state
->response
.extra_data
)
881 [group_list_ndx
* sizeof(struct winbindd_gr
)],
882 gr_mem_list
, gr_mem_list_len
);
884 SAFE_FREE(gr_mem_list
);
886 state
->response
.length
+= gr_mem_list_len
;
888 DEBUG(10, ("returning %d groups, length = %d\n",
889 group_list_ndx
, gr_mem_list_len
));
895 return (group_list_ndx
> 0) ? WINBINDD_OK
: WINBINDD_ERROR
;
898 /* List domain groups without mapping to unix ids */
900 enum winbindd_result
winbindd_list_groups(struct winbindd_cli_state
*state
)
902 uint32 total_entries
= 0;
903 struct winbindd_domain
*domain
;
904 const char *which_domain
;
905 char *extra_data
= NULL
;
907 unsigned int extra_data_len
= 0, i
;
909 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state
->pid
));
911 /* Ensure null termination */
912 state
->request
.domain_name
[sizeof(state
->request
.domain_name
)-1]='\0';
913 which_domain
= state
->request
.domain_name
;
915 /* Enumerate over trusted domains */
917 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
918 struct getent_state groups
;
920 /* if we have a domain name restricting the request and this
921 one in the list doesn't match, then just bypass the remainder
924 if ( *which_domain
&& !strequal(which_domain
, domain
->name
) )
929 /* Get list of sam groups */
931 fstrcpy(groups
.domain_name
, domain
->name
);
933 get_sam_group_entries(&groups
);
935 if (groups
.num_sam_entries
== 0) {
936 /* this domain is empty or in an error state */
940 /* keep track the of the total number of groups seen so
941 far over all domains */
942 total_entries
+= groups
.num_sam_entries
;
944 /* Allocate some memory for extra data. Note that we limit
945 account names to sizeof(fstring) = 128 characters. */
946 ted
= Realloc(extra_data
, sizeof(fstring
) * total_entries
);
949 DEBUG(0,("failed to enlarge buffer!\n"));
950 SAFE_FREE(extra_data
);
951 return WINBINDD_ERROR
;
955 /* Pack group list into extra data fields */
956 for (i
= 0; i
< groups
.num_sam_entries
; i
++) {
957 char *group_name
= ((struct acct_info
*)
958 groups
.sam_entries
)[i
].acct_name
;
961 fill_domain_username(name
, domain
->name
, group_name
);
962 /* Append to extra data */
963 memcpy(&extra_data
[extra_data_len
], name
,
965 extra_data_len
+= strlen(name
);
966 extra_data
[extra_data_len
++] = ',';
969 SAFE_FREE(groups
.sam_entries
);
972 /* Assign extra_data fields in response structure */
974 extra_data
[extra_data_len
- 1] = '\0';
975 state
->response
.extra_data
= extra_data
;
976 state
->response
.length
+= extra_data_len
;
979 /* No domains may have responded but that's still OK so don't
985 static void add_gids_from_sid(DOM_SID
*sid
, gid_t
**gids
, int *num
)
989 DEBUG(10, ("Adding gids from SID: %s\n", sid_string_static(sid
)));
991 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid
, &gid
, 0)))
992 add_gid_to_array_unique(gid
, gids
, num
);
994 /* Add nested group memberships */
996 add_foreign_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
, 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_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 ((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_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_sid(&group_sid
, &gid_list
, &num_gids
);
1124 if (gid_list
== NULL
)
1131 status
= domain
->methods
->lookup_usergroups(domain
, mem_ctx
,
1132 &user_sid
, &num_groups
,
1134 if (!NT_STATUS_IS_OK(status
))
1137 gid_list
= malloc(sizeof(gid_t
) * num_groups
);
1139 if (state
->response
.extra_data
)
1142 for (i
= 0; i
< num_groups
; i
++) {
1143 add_gids_from_sid(user_grpsids
[i
],
1144 &gid_list
, &num_gids
);
1146 if (gid_list
== NULL
)
1151 /* Send data back to client */
1153 state
->response
.data
.num_entries
= num_gids
;
1154 state
->response
.extra_data
= gid_list
;
1155 state
->response
.length
+= num_gids
* sizeof(gid_t
);
1157 result
= WINBINDD_OK
;
1161 talloc_destroy(mem_ctx
);
1167 /* Get user supplementary sids. This is equivalent to the
1168 winbindd_getgroups() function but it involves a SID->SIDs mapping
1169 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1170 idmap. This call is designed to be used with applications that need
1171 to do ACL evaluation themselves. Note that the cached info3 data is
1174 this function assumes that the SID that comes in is a user SID. If
1175 you pass in another type of SID then you may get unpredictable
1178 enum winbindd_result
winbindd_getusersids(struct winbindd_cli_state
*state
)
1182 DOM_SID
**user_grpsids
;
1183 struct winbindd_domain
*domain
;
1184 enum winbindd_result result
= WINBINDD_ERROR
;
1186 TALLOC_CTX
*mem_ctx
;
1189 unsigned ofs
, ret_size
= 0;
1191 /* Ensure null termination */
1192 state
->request
.data
.sid
[sizeof(state
->request
.data
.sid
)-1]='\0';
1194 if (!string_to_sid(&user_sid
, state
->request
.data
.sid
)) {
1195 DEBUG(1, ("Could not get convert sid %s from string\n", state
->request
.data
.sid
));
1196 return WINBINDD_ERROR
;
1199 if (!(mem_ctx
= talloc_init("winbindd_getusersids(%s)",
1200 state
->request
.data
.username
))) {
1201 return WINBINDD_ERROR
;
1204 /* Get info for the domain */
1205 if ((domain
= find_domain_from_sid(&user_sid
)) == NULL
) {
1206 DEBUG(0,("could not find domain entry for sid %s\n",
1207 sid_string_static(&user_sid
)));
1211 status
= domain
->methods
->lookup_usergroups(domain
, mem_ctx
,
1212 &user_sid
, &num_groups
,
1214 if (!NT_STATUS_IS_OK(status
))
1217 if (num_groups
== 0) {
1221 /* work out the response size */
1222 for (i
= 0; i
< num_groups
; i
++) {
1223 const char *s
= sid_string_static(user_grpsids
[i
]);
1224 ret_size
+= strlen(s
) + 1;
1227 /* build the reply */
1228 ret
= malloc(ret_size
);
1229 if (!ret
) goto done
;
1231 for (i
= 0; i
< num_groups
; i
++) {
1232 const char *s
= sid_string_static(user_grpsids
[i
]);
1233 safe_strcpy(ret
+ ofs
, s
, ret_size
- ofs
- 1);
1234 ofs
+= strlen(ret
+ofs
) + 1;
1238 /* Send data back to client */
1239 state
->response
.data
.num_entries
= num_groups
;
1240 state
->response
.extra_data
= ret
;
1241 state
->response
.length
+= ret_size
;
1242 result
= WINBINDD_OK
;
1245 talloc_destroy(mem_ctx
);