2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 /***************************************************************
27 Empty static struct for negative caching.
28 ****************************************************************/
30 /* Fill a grent structure from various other information */
32 static BOOL
fill_grent(struct winbindd_gr
*gr
, const char *dom_name
,
33 const char *gr_name
, gid_t unix_gid
)
35 fstring full_group_name
;
37 fill_domain_username(full_group_name
, dom_name
, gr_name
);
39 gr
->gr_gid
= unix_gid
;
41 /* Group name and password */
43 safe_strcpy(gr
->gr_name
, full_group_name
, sizeof(gr
->gr_name
) - 1);
44 safe_strcpy(gr
->gr_passwd
, "x", sizeof(gr
->gr_passwd
) - 1);
49 /* Fill in the group membership field of a NT group given by group_rid */
51 static BOOL
fill_grent_mem(struct winbindd_domain
*domain
,
53 enum SID_NAME_USE group_name_type
,
54 int *num_gr_mem
, char **gr_mem
, int *gr_mem_len
)
56 uint32
*rid_mem
= NULL
, num_names
= 0;
57 uint32
*name_types
= NULL
;
58 int buf_len
, buf_ndx
, i
;
59 char **names
= NULL
, *buf
;
64 if (!(mem_ctx
= talloc_init_named("fill_grent_mem(%s)", domain
->name
)))
67 /* Initialise group membership information */
69 DEBUG(10, ("group %s rid 0x%x\n", domain
? domain
->name
: "NULL",
74 if (group_name_type
!= SID_NAME_DOM_GRP
) {
75 DEBUG(1, ("rid %d in domain %s isn't a " "domain group\n",
76 group_rid
, domain
->name
));
80 /* Lookup group members */
81 status
= domain
->methods
->lookup_groupmem(domain
, mem_ctx
, group_rid
, &num_names
,
82 &rid_mem
, &names
, &name_types
);
83 if (!NT_STATUS_IS_OK(status
)) {
84 DEBUG(1, ("could not lookup membership for group rid %d in domain %s\n",
85 group_rid
, domain
->name
));
90 DEBUG(10, ("looked up %d names\n", num_names
));
92 if (DEBUGLEVEL
>= 10) {
93 for (i
= 0; i
< num_names
; i
++)
94 DEBUG(10, ("\t%20s %x %d\n", names
[i
], rid_mem
[i
],
98 /* Add members to list */
101 buf_len
= buf_ndx
= 0;
105 for (i
= 0; i
< num_names
; i
++) {
112 DEBUG(10, ("processing name %s\n", the_name
));
114 /* FIXME: need to cope with groups within groups. These
115 occur in Universal groups on a Windows 2000 native mode
118 if (name_types
[i
] != SID_NAME_USER
) {
119 DEBUG(3, ("name %s isn't a domain user\n", the_name
));
123 /* Don't bother with machine accounts */
125 if (the_name
[strlen(the_name
) - 1] == '$') {
126 DEBUG(10, ("%s is machine account\n", the_name
));
130 /* Append domain name */
132 fill_domain_username(name
, domain
->name
, the_name
);
136 /* Add to list or calculate buffer length */
139 buf_len
+= len
+ 1; /* List is comma separated */
141 DEBUG(10, ("buf_len + %d = %d\n", len
+ 1, buf_len
));
143 DEBUG(10, ("appending %s at ndx %d\n", name
, len
));
144 safe_strcpy(&buf
[buf_ndx
], name
, len
);
151 /* Allocate buffer */
153 if (!buf
&& buf_len
!= 0) {
154 if (!(buf
= malloc(buf_len
))) {
155 DEBUG(1, ("out of memory\n"));
159 memset(buf
, 0, buf_len
);
163 if (buf
&& buf_ndx
> 0) {
164 buf
[buf_ndx
- 1] = '\0';
168 *gr_mem_len
= buf_len
;
170 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem
,
171 buf_len
, *num_gr_mem
? buf
: "NULL"));
176 talloc_destroy(mem_ctx
);
178 DEBUG(10, ("fill_grent_mem returning %d\n", result
));
183 /* Return a group structure from a group name */
185 enum winbindd_result
winbindd_getgrnam(struct winbindd_cli_state
*state
)
188 struct winbindd_domain
*domain
;
189 enum SID_NAME_USE name_type
;
191 fstring name_domain
, name_group
;
196 DEBUG(3, ("[%5d]: getgrnam %s\n", state
->pid
,
197 state
->request
.data
.groupname
));
199 /* Parse domain and groupname */
201 memset(name_group
, 0, sizeof(fstring
));
203 tmp
= state
->request
.data
.groupname
;
204 if (!parse_domain_user(tmp
, name_domain
, name_group
))
205 return WINBINDD_ERROR
;
207 /* Get info for the domain */
209 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
210 DEBUG(0, ("could not get domain sid for domain %s\n",
212 return WINBINDD_ERROR
;
215 /* Get rid and name type from name */
217 if (!winbindd_lookup_sid_by_name(domain
, name_group
, &group_sid
,
219 DEBUG(1, ("group %s in domain %s does not exist\n",
220 name_group
, name_domain
));
221 return WINBINDD_ERROR
;
224 if ((name_type
!= SID_NAME_ALIAS
) && (name_type
!= SID_NAME_DOM_GRP
)) {
225 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
226 name_group
, name_type
));
227 return WINBINDD_ERROR
;
230 /* Fill in group structure */
231 sid_peek_rid(&group_sid
, &group_rid
);
233 if (!winbindd_idmap_get_gid_from_sid(&group_sid
, &gid
)) {
234 DEBUG(1, ("error converting unix gid to sid\n"));
235 return WINBINDD_ERROR
;
238 if (!fill_grent(&state
->response
.data
.gr
, name_domain
,
240 !fill_grent_mem(domain
, group_rid
, name_type
,
241 &state
->response
.data
.gr
.num_gr_mem
,
242 &gr_mem
, &gr_mem_len
)) {
243 return WINBINDD_ERROR
;
246 /* Group membership lives at start of extra data */
248 state
->response
.data
.gr
.gr_mem_ofs
= 0;
250 state
->response
.length
+= gr_mem_len
;
251 state
->response
.extra_data
= gr_mem
;
256 /* Return a group structure from a gid number */
258 enum winbindd_result
winbindd_getgrgid(struct winbindd_cli_state
*state
)
260 struct winbindd_domain
*domain
;
262 enum SID_NAME_USE name_type
;
269 DEBUG(3, ("[%5d]: getgrgid %d\n", state
->pid
,
270 state
->request
.data
.gid
));
272 /* Bug out if the gid isn't in the winbind range */
274 if ((state
->request
.data
.gid
< server_state
.gid_low
) ||
275 (state
->request
.data
.gid
> server_state
.gid_high
))
276 return WINBINDD_ERROR
;
278 /* Get rid from gid */
280 if (!winbindd_idmap_get_rid_from_gid(state
->request
.data
.gid
,
281 &group_rid
, &domain
)) {
282 DEBUG(1, ("could not convert gid %d to rid\n",
283 state
->request
.data
.gid
));
284 return WINBINDD_ERROR
;
287 /* Get sid from gid */
289 sid_copy(&group_sid
, &domain
->sid
);
290 sid_append_rid(&group_sid
, group_rid
);
292 if (!winbindd_lookup_name_by_sid(&group_sid
, dom_name
, group_name
, &name_type
)) {
293 DEBUG(1, ("could not lookup sid\n"));
294 return WINBINDD_ERROR
;
297 if (!((name_type
== SID_NAME_ALIAS
) ||
298 (name_type
== SID_NAME_DOM_GRP
))) {
299 DEBUG(1, ("name '%s' is not a local or domain group: %d\n",
300 group_name
, name_type
));
301 return WINBINDD_ERROR
;
304 /* Fill in group structure */
306 if (!fill_grent(&state
->response
.data
.gr
, dom_name
, group_name
,
307 state
->request
.data
.gid
) ||
308 !fill_grent_mem(domain
, group_rid
, name_type
,
309 &state
->response
.data
.gr
.num_gr_mem
,
310 &gr_mem
, &gr_mem_len
))
311 return WINBINDD_ERROR
;
313 /* Group membership lives at start of extra data */
315 state
->response
.data
.gr
.gr_mem_ofs
= 0;
317 state
->response
.length
+= gr_mem_len
;
318 state
->response
.extra_data
= gr_mem
;
324 * set/get/endgrent functions
327 /* "Rewind" file pointer for group database enumeration */
329 enum winbindd_result
winbindd_setgrent(struct winbindd_cli_state
*state
)
331 struct winbindd_domain
*domain
;
333 DEBUG(3, ("[%5d]: setgrent\n", state
->pid
));
335 /* Check user has enabled this */
337 if (!lp_winbind_enum_groups())
338 return WINBINDD_ERROR
;
340 /* Free old static data if it exists */
342 if (state
->getgrent_state
!= NULL
) {
343 free_getent_state(state
->getgrent_state
);
344 state
->getgrent_state
= NULL
;
347 /* Create sam pipes for each domain we know about */
349 for (domain
= domain_list(); domain
!= NULL
; domain
= domain
->next
) {
350 struct getent_state
*domain_state
;
352 /* Skip domains other than WINBINDD_DOMAIN environment
355 if ((strcmp(state
->request
.domain
, "") != 0) &&
356 !check_domain_env(state
->request
.domain
, domain
->name
))
359 /* Create a state record for this domain */
361 if ((domain_state
= (struct getent_state
*)
362 malloc(sizeof(struct getent_state
))) == NULL
)
363 return WINBINDD_ERROR
;
365 ZERO_STRUCTP(domain_state
);
367 fstrcpy(domain_state
->domain_name
, domain
->name
);
369 /* Add to list of open domains */
371 DLIST_ADD(state
->getgrent_state
, domain_state
);
377 /* Close file pointer to ntdom group database */
379 enum winbindd_result
winbindd_endgrent(struct winbindd_cli_state
*state
)
381 DEBUG(3, ("[%5d]: endgrent\n", state
->pid
));
383 free_getent_state(state
->getgrent_state
);
384 state
->getgrent_state
= NULL
;
389 /* Get the list of domain groups and domain aliases for a domain. We fill in
390 the sam_entries and num_sam_entries fields with domain group information.
391 The dispinfo_ndx field is incremented to the index of the next group to
392 fetch. Return True if some groups were returned, False otherwise. */
394 #define MAX_FETCH_SAM_ENTRIES 100
396 static BOOL
get_sam_group_entries(struct getent_state
*ent
, NTSTATUS
*status
)
399 struct acct_info
*name_list
= NULL
;
402 struct acct_info
*sam_grp_entries
= NULL
;
403 struct winbindd_domain
*domain
;
406 if (!(mem_ctx
= talloc_init_named("get_sam_group_entries(%s)",
410 /* Free any existing group info */
412 SAFE_FREE(ent
->sam_entries
);
413 ent
->num_sam_entries
= 0;
414 ent
->got_sam_entries
= True
;
416 /* Enumerate domain groups */
420 if (!(domain
= find_domain_from_name(ent
->domain_name
))) {
421 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent
->domain_name
));
425 nt_status
= domain
->methods
->enum_dom_groups(
426 domain
, mem_ctx
, &num_entries
, &sam_grp_entries
);
428 if (status
&& !NT_STATUS_IS_OK(nt_status
))
431 /* Copy entries into return buffer */
434 name_list
= malloc(sizeof(struct acct_info
) * num_entries
);
435 memcpy(name_list
, sam_grp_entries
,
436 num_entries
* sizeof(struct acct_info
));
439 ent
->num_sam_entries
= num_entries
;
441 /* Fill in remaining fields */
443 ent
->sam_entries
= name_list
;
444 ent
->sam_entry_index
= 0;
446 /* Return false if we got an error or no sam entries, true otherwise */
448 if (!NT_STATUS_IS_OK(nt_status
))
451 result
= (ent
->num_sam_entries
> 0);
454 talloc_destroy(mem_ctx
);
459 /* Fetch next group entry from ntdom database */
461 #define MAX_GETGRENT_GROUPS 500
463 enum winbindd_result
winbindd_getgrent(struct winbindd_cli_state
*state
)
465 struct getent_state
*ent
;
466 struct winbindd_gr
*group_list
= NULL
;
467 int num_groups
, group_list_ndx
= 0, i
, gr_mem_list_len
= 0;
468 char *new_extra_data
, *gr_mem_list
= NULL
;
470 DEBUG(3, ("[%5d]: getgrent\n", state
->pid
));
472 /* Check user has enabled this */
474 if (!lp_winbind_enum_groups())
475 return WINBINDD_ERROR
;
477 num_groups
= MIN(MAX_GETGRENT_GROUPS
, state
->request
.data
.num_entries
);
479 if ((state
->response
.extra_data
=
480 malloc(num_groups
* sizeof(struct winbindd_gr
))) == NULL
)
481 return WINBINDD_ERROR
;
483 state
->response
.data
.num_entries
= 0;
485 group_list
= (struct winbindd_gr
*)state
->response
.extra_data
;
487 if (!(ent
= state
->getgrent_state
))
488 return WINBINDD_ERROR
;
490 /* Start sending back groups */
492 for (i
= 0; i
< num_groups
; i
++) {
493 struct acct_info
*name_list
= NULL
;
494 fstring domain_group_name
;
498 char *gr_mem
, *new_gr_mem_list
;
500 /* Do we need to fetch another chunk of groups? */
504 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
505 ent
->sam_entry_index
, ent
->num_sam_entries
));
507 if (ent
->num_sam_entries
== ent
->sam_entry_index
) {
508 struct getent_state
*next_ent
;
510 /* is this the beginning ( == 0 ) or the end ? */
512 if ( ent
->sam_entry_index
> 0 ) {
513 DEBUG(10, ("end of getgrent: freeing state info for domain %s\n", ent
->domain_name
));
514 SAFE_FREE(ent
->sam_entries
);
515 next_ent
= ent
->next
;
516 DLIST_REMOVE(state
->getgrent_state
, ent
);
521 /* find the next domain's group entries */
523 while(ent
&& !get_sam_group_entries(ent
, NULL
)) {
524 DEBUG(10, ("freeing state info for domain %s\n", ent
->domain_name
));
526 /* Free state information for this domain */
528 SAFE_FREE(ent
->sam_entries
);
530 next_ent
= ent
->next
;
531 DLIST_REMOVE(state
->getgrent_state
, ent
);
537 /* No more domains */
543 name_list
= ent
->sam_entries
;
545 /* Lookup group info */
547 if (!winbindd_idmap_get_gid_from_rid(
549 name_list
[ent
->sam_entry_index
].rid
,
552 DEBUG(1, ("could not look up gid for group %s\n",
553 name_list
[ent
->sam_entry_index
].acct_name
));
555 ent
->sam_entry_index
++;
559 DEBUG(10, ("got gid %d for group %x\n", group_gid
,
560 name_list
[ent
->sam_entry_index
].rid
));
562 /* Fill in group entry */
564 fill_domain_username(domain_group_name
, ent
->domain_name
,
565 name_list
[ent
->sam_entry_index
].acct_name
);
567 result
= fill_grent(&group_list
[group_list_ndx
],
569 name_list
[ent
->sam_entry_index
].acct_name
,
572 /* Fill in group membership entry */
575 struct winbindd_domain
*domain
;
578 find_domain_from_name(ent
->domain_name
))) {
579 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent
->domain_name
));
584 /* Get group membership */
586 result
= fill_grent_mem(
588 name_list
[ent
->sam_entry_index
].rid
,
590 &group_list
[group_list_ndx
].num_gr_mem
,
591 &gr_mem
, &gr_mem_len
);
595 /* Append to group membership list */
596 new_gr_mem_list
= Realloc(
598 gr_mem_list_len
+ gr_mem_len
);
600 if (!new_gr_mem_list
&& (group_list
[group_list_ndx
].num_gr_mem
!= 0)) {
601 DEBUG(0, ("out of memory\n"));
602 SAFE_FREE(gr_mem_list
);
607 DEBUG(10, ("list_len = %d, mem_len = %d\n",
608 gr_mem_list_len
, gr_mem_len
));
610 gr_mem_list
= new_gr_mem_list
;
612 memcpy(&gr_mem_list
[gr_mem_list_len
], gr_mem
,
617 group_list
[group_list_ndx
].gr_mem_ofs
=
620 gr_mem_list_len
+= gr_mem_len
;
623 ent
->sam_entry_index
++;
625 /* Add group to return list */
629 DEBUG(10, ("adding group num_entries = %d\n",
630 state
->response
.data
.num_entries
));
633 state
->response
.data
.num_entries
++;
635 state
->response
.length
+=
636 sizeof(struct winbindd_gr
);
639 DEBUG(0, ("could not lookup domain group %s\n",
644 /* Copy the list of group memberships to the end of the extra data */
646 if (group_list_ndx
== 0)
649 new_extra_data
= Realloc(
650 state
->response
.extra_data
,
651 group_list_ndx
* sizeof(struct winbindd_gr
) + gr_mem_list_len
);
653 if (!new_extra_data
) {
654 DEBUG(0, ("out of memory\n"));
656 SAFE_FREE(state
->response
.extra_data
);
657 SAFE_FREE(gr_mem_list
);
659 return WINBINDD_ERROR
;
662 state
->response
.extra_data
= new_extra_data
;
664 memcpy(&((char *)state
->response
.extra_data
)
665 [group_list_ndx
* sizeof(struct winbindd_gr
)],
666 gr_mem_list
, gr_mem_list_len
);
668 SAFE_FREE(gr_mem_list
);
670 state
->response
.length
+= gr_mem_list_len
;
672 DEBUG(10, ("returning %d groups, length = %d\n",
673 group_list_ndx
, gr_mem_list_len
));
679 return (group_list_ndx
> 0) ? WINBINDD_OK
: WINBINDD_ERROR
;
682 /* List domain groups without mapping to unix ids */
684 enum winbindd_result
winbindd_list_groups(struct winbindd_cli_state
*state
)
686 uint32 total_entries
= 0;
687 struct winbindd_domain
*domain
;
688 char *extra_data
= NULL
;
690 int extra_data_len
= 0, i
;
692 DEBUG(3, ("[%5d]: list groups\n", state
->pid
));
694 /* Enumerate over trusted domains */
696 for (domain
= domain_list(); domain
; domain
= domain
->next
) {
697 struct getent_state groups
;
702 /* Skip domains other than WINBINDD_DOMAIN environment
705 if ((strcmp(state
->request
.domain
, "") != 0) &&
706 !check_domain_env(state
->request
.domain
, domain
->name
))
709 /* Get list of sam groups */
712 fstrcpy(groups
.domain_name
, domain
->name
);
714 if (!get_sam_group_entries(&groups
, &status
)) {
715 if (!NT_STATUS_IS_OK(status
))
716 state
->response
.nt_status
= NT_STATUS_V(status
);
717 if (!NT_STATUS_EQUAL(status
, NT_STATUS_MORE_PROCESSING_REQUIRED
))
721 if (groups
.num_sam_entries
== 0) {
722 /* this domain is empty or in an error state */
726 /* keep track the of the total number of groups seen so
727 far over all domains */
729 total_entries
+= groups
.num_sam_entries
;
731 /* Allocate some memory for extra data. Note that we limit
732 account names to sizeof(fstring) = 128 characters. */
734 ted
= Realloc(extra_data
, sizeof(fstring
) * total_entries
);
737 DEBUG(0,("failed to enlarge buffer!\n"));
738 SAFE_FREE(extra_data
);
739 return WINBINDD_ERROR
;
743 /* Pack group list into extra data fields */
744 for (i
= 0; i
< groups
.num_sam_entries
; i
++) {
745 char *group_name
= ((struct acct_info
*)
746 groups
.sam_entries
)[i
].acct_name
;
749 fill_domain_username(name
, domain
->name
, group_name
);
750 /* Append to extra data */
751 memcpy(&extra_data
[extra_data_len
], name
,
753 extra_data_len
+= strlen(name
);
754 extra_data
[extra_data_len
++] = ',';
757 free(groups
.sam_entries
);
760 /* Assign extra_data fields in response structure */
762 extra_data
[extra_data_len
- 1] = '\0';
763 state
->response
.extra_data
= extra_data
;
764 state
->response
.length
+= extra_data_len
;
767 /* No domains may have responded but that's still OK so don't
773 /* Get user supplementary groups. This is much quicker than trying to
774 invert the groups database. */
776 enum winbindd_result
winbindd_getgroups(struct winbindd_cli_state
*state
)
778 fstring name_domain
, name_user
;
780 enum SID_NAME_USE name_type
;
781 uint32 user_rid
, num_groups
, num_gids
;
784 struct winbindd_domain
*domain
;
785 enum winbindd_result result
= WINBINDD_ERROR
;
790 DEBUG(3, ("[%5d]: getgroups %s\n", state
->pid
,
791 state
->request
.data
.username
));
793 if (!(mem_ctx
= talloc_init_named("winbindd_getgroups(%s)",
794 state
->request
.data
.username
)))
795 return WINBINDD_ERROR
;
797 /* Parse domain and username */
799 if (!parse_domain_user(state
->request
.data
.username
, name_domain
,
803 /* Get info for the domain */
805 if ((domain
= find_domain_from_name(name_domain
)) == NULL
) {
806 DEBUG(0, ("could not find domain entry for domain %s\n",
811 /* Get rid and name type from name. The following costs 1 packet */
813 if (!winbindd_lookup_sid_by_name(domain
, name_user
, &user_sid
,
815 DEBUG(1, ("user '%s' does not exist\n", name_user
));
819 if (name_type
!= SID_NAME_USER
) {
820 DEBUG(1, ("name '%s' is not a user name: %d\n",
821 name_user
, name_type
));
825 sid_split_rid(&user_sid
, &user_rid
);
827 status
= domain
->methods
->lookup_usergroups(domain
, mem_ctx
, user_rid
, &num_groups
, &user_gids
);
828 if (!NT_STATUS_IS_OK(status
)) goto done
;
830 /* Copy data back to client */
833 gid_list
= malloc(sizeof(gid_t
) * num_groups
);
835 if (state
->response
.extra_data
)
838 for (i
= 0; i
< num_groups
; i
++) {
839 if (!winbindd_idmap_get_gid_from_rid(domain
->name
,
841 &gid_list
[num_gids
])) {
843 DEBUG(1, ("unable to convert group rid %d to gid\n",
851 state
->response
.data
.num_entries
= num_gids
;
852 state
->response
.extra_data
= gid_list
;
853 state
->response
.length
+= num_gids
* sizeof(gid_t
);
855 result
= WINBINDD_OK
;
859 talloc_destroy(mem_ctx
);