source: branches/samba-3.3.x/source/winbindd/winbindd_group.c

Last change on this file was 222, checked in by Herwig Bauernfeind, 16 years ago

Update Samba 3.3 branch to 3.3.2

File size: 47.6 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3
4 Winbind daemon for ntdom nss module
5
6 Copyright (C) Tim Potter 2000
7 Copyright (C) Jeremy Allison 2001.
8 Copyright (C) Gerald (Jerry) Carter 2003.
9 Copyright (C) Volker Lendecke 2005
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 3 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25#include "includes.h"
26#include "winbindd.h"
27
28#undef DBGC_CLASS
29#define DBGC_CLASS DBGC_WINBIND
30
31static void add_member(const char *domain, const char *user,
32 char **pp_members, size_t *p_num_members)
33{
34 fstring name;
35
36 if (domain != NULL) {
37 fill_domain_username(name, domain, user, True);
38 } else {
39 fstrcpy(name, user);
40 }
41 safe_strcat(name, ",", sizeof(name)-1);
42 string_append(pp_members, name);
43 *p_num_members += 1;
44}
45
46/**********************************************************************
47 Add member users resulting from sid. Expand if it is a domain group.
48**********************************************************************/
49
50static void add_expanded_sid(const DOM_SID *sid,
51 char **pp_members,
52 size_t *p_num_members)
53{
54 DOM_SID dom_sid;
55 uint32 rid;
56 struct winbindd_domain *domain;
57 size_t i;
58
59 char *domain_name = NULL;
60 char *name = NULL;
61 enum lsa_SidType type;
62
63 uint32 num_names;
64 DOM_SID *sid_mem;
65 char **names;
66 uint32 *types;
67
68 NTSTATUS result;
69
70 TALLOC_CTX *mem_ctx = talloc_init("add_expanded_sid");
71
72 if (mem_ctx == NULL) {
73 DEBUG(1, ("talloc_init failed\n"));
74 return;
75 }
76
77 sid_copy(&dom_sid, sid);
78 sid_split_rid(&dom_sid, &rid);
79
80 domain = find_lookup_domain_from_sid(sid);
81
82 if (domain == NULL) {
83 DEBUG(3, ("Could not find domain for sid %s\n",
84 sid_string_dbg(sid)));
85 goto done;
86 }
87
88 result = domain->methods->sid_to_name(domain, mem_ctx, sid,
89 &domain_name, &name, &type);
90
91 if (!NT_STATUS_IS_OK(result)) {
92 DEBUG(3, ("sid_to_name failed for sid %s\n",
93 sid_string_dbg(sid)));
94 goto done;
95 }
96
97 DEBUG(10, ("Found name %s, type %d\n", name, type));
98
99 if (type == SID_NAME_USER) {
100 add_member(domain_name, name, pp_members, p_num_members);
101 goto done;
102 }
103
104 if (type != SID_NAME_DOM_GRP) {
105 DEBUG(10, ("Alias member %s neither user nor group, ignore\n",
106 name));
107 goto done;
108 }
109
110 /* Expand the domain group, this must be done via the target domain */
111
112 domain = find_domain_from_sid(sid);
113
114 if (domain == NULL) {
115 DEBUG(3, ("Could not find domain from SID %s\n",
116 sid_string_dbg(sid)));
117 goto done;
118 }
119
120 result = domain->methods->lookup_groupmem(domain, mem_ctx,
121 sid, &num_names,
122 &sid_mem, &names,
123 &types);
124
125 if (!NT_STATUS_IS_OK(result)) {
126 DEBUG(10, ("Could not lookup group members for %s: %s\n",
127 name, nt_errstr(result)));
128 goto done;
129 }
130
131 for (i=0; i<num_names; i++) {
132 DEBUG(10, ("Adding group member SID %s\n",
133 sid_string_dbg(&sid_mem[i])));
134
135 if (types[i] != SID_NAME_USER) {
136 DEBUG(1, ("Hmmm. Member %s of group %s is no user. "
137 "Ignoring.\n", names[i], name));
138 continue;
139 }
140
141 add_member(NULL, names[i], pp_members, p_num_members);
142 }
143
144 done:
145 talloc_destroy(mem_ctx);
146 return;
147}
148
149static bool fill_passdb_alias_grmem(struct winbindd_domain *domain,
150 DOM_SID *group_sid, size_t *num_gr_mem,
151 char **gr_mem, size_t *gr_mem_len)
152{
153 DOM_SID *members;
154 size_t i, num_members;
155
156 *num_gr_mem = 0;
157 *gr_mem = NULL;
158 *gr_mem_len = 0;
159
160 if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(group_sid, &members,
161 &num_members)))
162 return True;
163
164 for (i=0; i<num_members; i++) {
165 add_expanded_sid(&members[i], gr_mem, num_gr_mem);
166 }
167
168 TALLOC_FREE(members);
169
170 if (*gr_mem != NULL) {
171 size_t len;
172
173 /* We have at least one member, strip off the last "," */
174 len = strlen(*gr_mem);
175 (*gr_mem)[len-1] = '\0';
176 *gr_mem_len = len;
177 }
178
179 return True;
180}
181
182/* Fill a grent structure from various other information */
183
184static bool fill_grent(TALLOC_CTX *mem_ctx, struct winbindd_gr *gr,
185 const char *dom_name,
186 char *gr_name, gid_t unix_gid)
187{
188 fstring full_group_name;
189 char *mapped_name = NULL;
190 struct winbindd_domain *domain = find_domain_from_name_noinit(dom_name);
191 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
192
193 nt_status = normalize_name_map(mem_ctx, domain, gr_name,
194 &mapped_name);
195
196 /* Basic whitespace replacement */
197 if (NT_STATUS_IS_OK(nt_status)) {
198 fill_domain_username(full_group_name, dom_name,
199 mapped_name, true);
200 }
201 /* Mapped to an aliase */
202 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
203 fstrcpy(full_group_name, mapped_name);
204 }
205 /* no change */
206 else {
207 fill_domain_username( full_group_name, dom_name,
208 gr_name, True );
209 }
210
211 gr->gr_gid = unix_gid;
212
213 /* Group name and password */
214
215 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1);
216 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1);
217
218 return True;
219}
220
221/***********************************************************************
222 If "enum users" is set to false, and the group being looked
223 up is the Domain Users SID: S-1-5-domain-513, then for the
224 list of members check if the querying user is in that group,
225 and if so only return that user as the gr_mem array.
226 We can change this to a different parameter than "enum users"
227 if neccessaey, or parameterize the group list we do this for.
228***********************************************************************/
229
230static bool fill_grent_mem_domusers( TALLOC_CTX *mem_ctx,
231 struct winbindd_domain *domain,
232 struct winbindd_cli_state *state,
233 DOM_SID *group_sid,
234 enum lsa_SidType group_name_type,
235 size_t *num_gr_mem, char **gr_mem,
236 size_t *gr_mem_len)
237{
238 DOM_SID querying_user_sid;
239 DOM_SID *pquerying_user_sid = NULL;
240 uint32 num_groups = 0;
241 DOM_SID *user_sids = NULL;
242 bool u_in_group = False;
243 NTSTATUS status;
244 int i;
245 unsigned int buf_len = 0;
246 char *buf = NULL;
247
248 DEBUG(10,("fill_grent_mem_domain_users: domain %s\n",
249 domain->name ));
250
251 if (state) {
252 uid_t ret_uid = (uid_t)-1;
253 if (sys_getpeereid(state->sock, &ret_uid)==0) {
254 /* We know who's asking - look up their SID if
255 it's one we've mapped before. */
256 status = idmap_uid_to_sid(domain->name,
257 &querying_user_sid, ret_uid);
258 if (NT_STATUS_IS_OK(status)) {
259 pquerying_user_sid = &querying_user_sid;
260 DEBUG(10,("fill_grent_mem_domain_users: "
261 "querying uid %u -> %s\n",
262 (unsigned int)ret_uid,
263 sid_string_dbg(pquerying_user_sid)));
264 }
265 }
266 }
267
268 /* Only look up if it was a winbindd user in this domain. */
269 if (pquerying_user_sid &&
270 (sid_compare_domain(pquerying_user_sid, &domain->sid) == 0)) {
271
272 DEBUG(10,("fill_grent_mem_domain_users: querying user = %s\n",
273 sid_string_dbg(pquerying_user_sid) ));
274
275 status = domain->methods->lookup_usergroups(domain,
276 mem_ctx,
277 pquerying_user_sid,
278 &num_groups,
279 &user_sids);
280 if (!NT_STATUS_IS_OK(status)) {
281 DEBUG(1, ("fill_grent_mem_domain_users: "
282 "lookup_usergroups failed "
283 "for sid %s in domain %s (error: %s)\n",
284 sid_string_dbg(pquerying_user_sid),
285 domain->name,
286 nt_errstr(status)));
287 return False;
288 }
289
290 for (i = 0; i < num_groups; i++) {
291 if (sid_equal(group_sid, &user_sids[i])) {
292 /* User is in Domain Users, add their name
293 as the only group member. */
294 u_in_group = True;
295 break;
296 }
297 }
298 }
299
300 if (u_in_group) {
301 size_t len = 0;
302 char *domainname = NULL;
303 char *username = NULL;
304 fstring name;
305 char *mapped_name = NULL;
306 enum lsa_SidType type;
307 struct winbindd_domain *target_domain = NULL;
308 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
309
310 DEBUG(10,("fill_grent_mem_domain_users: "
311 "sid %s in 'Domain Users' in domain %s\n",
312 sid_string_dbg(pquerying_user_sid),
313 domain->name ));
314
315 status = domain->methods->sid_to_name(domain, mem_ctx,
316 pquerying_user_sid,
317 &domainname,
318 &username,
319 &type);
320 if (!NT_STATUS_IS_OK(status)) {
321 DEBUG(1, ("could not lookup username for user "
322 "sid %s in domain %s (error: %s)\n",
323 sid_string_dbg(pquerying_user_sid),
324 domain->name,
325 nt_errstr(status)));
326 return False;
327 }
328
329 target_domain = find_domain_from_name_noinit(domainname);
330 name_map_status = normalize_name_map(mem_ctx, target_domain,
331 username, &mapped_name);
332
333 /* Basic whitespace replacement */
334 if (NT_STATUS_IS_OK(name_map_status)) {
335 fill_domain_username(name, domainname, mapped_name, true);
336 }
337 /* Mapped to an alias */
338 else if (NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
339 fstrcpy(name, mapped_name);
340 }
341 /* no mapping done...use original name */
342 else {
343 fill_domain_username(name, domainname, username, true);
344 }
345
346 len = strlen(name);
347 buf_len = len + 1;
348 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
349 DEBUG(1, ("out of memory\n"));
350 return False;
351 }
352 memcpy(buf, name, buf_len);
353
354 DEBUG(10,("fill_grent_mem_domain_users: user %s in "
355 "'Domain Users' in domain %s\n",
356 name, domain->name ));
357
358 /* user is the only member */
359 *num_gr_mem = 1;
360 }
361
362 *gr_mem = buf;
363 *gr_mem_len = buf_len;
364
365 DEBUG(10, ("fill_grent_mem_domain_users: "
366 "num_mem = %u, len = %u, mem = %s\n",
367 (unsigned int)*num_gr_mem,
368 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
369
370 return True;
371}
372
373/***********************************************************************
374 Add names to a list. Assumes a canonical version of the string
375 in DOMAIN\user
376***********************************************************************/
377
378static int namecmp( const void *a, const void *b )
379{
380 return StrCaseCmp( * (char * const *) a, * (char * const *) b);
381}
382
383static void sort_unique_list(char ***list, uint32 *n_list)
384{
385 uint32_t i;
386
387 /* search for duplicates for sorting and looking for matching
388 neighbors */
389
390 qsort(*list, *n_list, sizeof(char*), QSORT_CAST namecmp);
391
392 for (i=1; i < *n_list; i++) {
393 if (strcmp((*list)[i-1], (*list)[i]) == 0) {
394 memmove(&((*list)[i-1]), &((*list)[i]),
395 sizeof(char*)*((*n_list)-i));
396 (*n_list)--;
397 }
398 }
399}
400
401static NTSTATUS add_names_to_list( TALLOC_CTX *ctx,
402 char ***list, uint32 *n_list,
403 char **names, uint32 n_names )
404{
405 char **new_list = NULL;
406 uint32 n_new_list = 0;
407 int i, j;
408
409 if ( !names || (n_names == 0) )
410 return NT_STATUS_OK;
411
412 /* Alloc the maximum size we'll need */
413
414 if ( *list == NULL ) {
415 if ((new_list = TALLOC_ARRAY(ctx, char *, n_names)) == NULL) {
416 return NT_STATUS_NO_MEMORY;
417 }
418 n_new_list = n_names;
419 } else {
420 new_list = TALLOC_REALLOC_ARRAY( ctx, *list, char *,
421 (*n_list) + n_names );
422 if ( !new_list )
423 return NT_STATUS_NO_MEMORY;
424 n_new_list = (*n_list) + n_names;
425 }
426
427 /* Add all names */
428
429 for ( i=*n_list, j=0; i<n_new_list; i++, j++ ) {
430 new_list[i] = talloc_strdup( new_list, names[j] );
431 }
432
433 *list = new_list;
434 *n_list = n_new_list;
435
436 return NT_STATUS_OK;
437}
438
439/***********************************************************************
440***********************************************************************/
441
442static NTSTATUS expand_groups( TALLOC_CTX *ctx,
443 struct winbindd_domain *d,
444 DOM_SID *glist, uint32 n_glist,
445 DOM_SID **new_glist, uint32 *n_new_glist,
446 char ***members, uint32 *n_members )
447{
448 int i, j;
449 NTSTATUS status = NT_STATUS_OK;
450 uint32 num_names = 0;
451 uint32 *name_types = NULL;
452 char **names = NULL;
453 DOM_SID *sid_mem = NULL;
454 TALLOC_CTX *tmp_ctx = NULL;
455 DOM_SID *new_groups = NULL;
456 size_t new_groups_size = 0;
457
458 *members = NULL;
459 *n_members = 0;
460 *new_glist = NULL;
461 *n_new_glist = 0;
462
463 for ( i=0; i<n_glist; i++ ) {
464 tmp_ctx = talloc_new( ctx );
465
466 /* Lookup the group membership */
467
468 status = d->methods->lookup_groupmem(d, tmp_ctx,
469 &glist[i], &num_names,
470 &sid_mem, &names,
471 &name_types);
472 if ( !NT_STATUS_IS_OK(status) )
473 goto out;
474
475 /* Separate users and groups into two lists */
476
477 for ( j=0; j<num_names; j++ ) {
478
479 /* Users */
480 if ( name_types[j] == SID_NAME_USER ||
481 name_types[j] == SID_NAME_COMPUTER )
482 {
483 status = add_names_to_list( ctx, members,
484 n_members,
485 names+j, 1 );
486 if ( !NT_STATUS_IS_OK(status) )
487 goto out;
488
489 continue;
490 }
491
492 /* Groups */
493 if ( name_types[j] == SID_NAME_DOM_GRP ||
494 name_types[j] == SID_NAME_ALIAS )
495 {
496 status = add_sid_to_array_unique(ctx,
497 &sid_mem[j],
498 &new_groups,
499 &new_groups_size);
500 if (!NT_STATUS_IS_OK(status)) {
501 goto out;
502 }
503
504 continue;
505 }
506 }
507
508 TALLOC_FREE( tmp_ctx );
509 }
510
511 *new_glist = new_groups;
512 *n_new_glist = (uint32)new_groups_size;
513
514 out:
515 TALLOC_FREE( tmp_ctx );
516
517 return status;
518}
519
520/***********************************************************************
521 Fill in the group membership field of a NT group given by group_sid
522***********************************************************************/
523
524static bool fill_grent_mem(struct winbindd_domain *domain,
525 struct winbindd_cli_state *state,
526 DOM_SID *group_sid,
527 enum lsa_SidType group_name_type,
528 size_t *num_gr_mem, char **gr_mem,
529 size_t *gr_mem_len)
530{
531 uint32 num_names = 0;
532 unsigned int buf_len = 0, buf_ndx = 0, i;
533 char **names = NULL, *buf = NULL;
534 bool result = False;
535 TALLOC_CTX *mem_ctx;
536 uint32 group_rid;
537 DOM_SID *glist = NULL;
538 DOM_SID *new_glist = NULL;
539 uint32 n_glist, n_new_glist;
540 int max_depth = lp_winbind_expand_groups();
541
542 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name)))
543 return False;
544
545 DEBUG(10, ("group SID %s\n", sid_string_dbg(group_sid)));
546
547 /* Initialize with no members */
548
549 *num_gr_mem = 0;
550
551 /* HACK ALERT!! This whole routine does not cope with group members
552 * from more than one domain, ie aliases. Thus we have to work it out
553 * ourselves in a special routine. */
554
555 if (domain->internal) {
556 result = fill_passdb_alias_grmem(domain, group_sid,
557 num_gr_mem,
558 gr_mem, gr_mem_len);
559 goto done;
560 }
561
562 /* Verify name type */
563
564 if ( !((group_name_type==SID_NAME_DOM_GRP) ||
565 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) )
566 {
567 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n",
568 sid_string_dbg(group_sid),
569 domain->name, group_name_type));
570 goto done;
571 }
572
573 /* OPTIMIZATION / HACK. See comment in
574 fill_grent_mem_domusers() */
575
576 sid_peek_rid( group_sid, &group_rid );
577 if (!lp_winbind_enum_users() && group_rid == DOMAIN_GROUP_RID_USERS) {
578 result = fill_grent_mem_domusers( mem_ctx, domain, state,
579 group_sid, group_name_type,
580 num_gr_mem, gr_mem,
581 gr_mem_len );
582 goto done;
583 }
584
585 /* Real work goes here. Create a list of group names to
586 expand starting with the initial one. Pass that to
587 expand_groups() which returns a list of more group names
588 to expand. Do this up to the max search depth. */
589
590 if ( (glist = TALLOC_ARRAY(mem_ctx, DOM_SID, 1 )) == NULL ) {
591 result = False;
592 DEBUG(0,("fill_grent_mem: talloc failure!\n"));
593 goto done;
594 }
595 sid_copy( &glist[0], group_sid );
596 n_glist = 1;
597
598 for ( i=0; i<max_depth && glist; i++ ) {
599 uint32 n_members = 0;
600 char **members = NULL;
601 NTSTATUS nt_status;
602 int j;
603
604 nt_status = expand_groups( mem_ctx, domain,
605 glist, n_glist,
606 &new_glist, &n_new_glist,
607 &members, &n_members);
608 if ( !NT_STATUS_IS_OK(nt_status) ) {
609 result = False;
610 goto done;
611 }
612
613 /* Add new group members to list. Pass through the
614 alias mapping function */
615
616 for (j=0; j<n_members; j++) {
617 fstring name_domain, name_acct;
618 fstring qualified_name;
619 char *mapped_name = NULL;
620 NTSTATUS name_map_status = NT_STATUS_UNSUCCESSFUL;
621 struct winbindd_domain *target_domain = NULL;
622
623 if (parse_domain_user(members[j], name_domain, name_acct)) {
624 target_domain = find_domain_from_name_noinit(name_domain);
625 /* NOW WHAT ? */
626 }
627 if (!target_domain) {
628 target_domain = domain;
629 }
630
631 name_map_status = normalize_name_map(members, target_domain,
632 name_acct, &mapped_name);
633
634 /* Basic whitespace replacement */
635 if (NT_STATUS_IS_OK(name_map_status)) {
636 fill_domain_username(qualified_name, name_domain,
637 mapped_name, true);
638 mapped_name = qualified_name;
639 }
640 /* no mapping at all */
641 else if (!NT_STATUS_EQUAL(name_map_status, NT_STATUS_FILE_RENAMED)) {
642 mapped_name = members[j];
643 }
644
645 nt_status = add_names_to_list( mem_ctx, &names,
646 &num_names,
647 &mapped_name, 1);
648 if ( !NT_STATUS_IS_OK(nt_status) ) {
649 result = False;
650 goto done;
651 }
652 }
653
654 TALLOC_FREE( members );
655
656 /* If we have no more groups to expand, break out
657 early */
658
659 if (new_glist == NULL)
660 break;
661
662 /* One more round */
663 TALLOC_FREE(glist);
664 glist = new_glist;
665 n_glist = n_new_glist;
666 }
667 TALLOC_FREE( glist );
668
669 sort_unique_list(&names, &num_names);
670
671 DEBUG(10, ("looked up %d names\n", num_names));
672
673 again:
674 /* Add members to list */
675
676 for (i = 0; i < num_names; i++) {
677 int len;
678
679 DEBUG(10, ("processing name %s\n", names[i]));
680
681 len = strlen(names[i]);
682
683 /* Add to list or calculate buffer length */
684
685 if (!buf) {
686 buf_len += len + 1; /* List is comma separated */
687 (*num_gr_mem)++;
688 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len));
689 } else {
690 DEBUG(10, ("appending %s at ndx %d\n",
691 names[i], buf_ndx));
692 parse_add_domuser(&buf[buf_ndx], names[i], &len);
693 buf_ndx += len;
694 buf[buf_ndx] = ',';
695 buf_ndx++;
696 }
697 }
698
699 /* Allocate buffer */
700
701 if (!buf && buf_len != 0) {
702 if (!(buf = (char *)SMB_MALLOC(buf_len))) {
703 DEBUG(1, ("out of memory\n"));
704 result = False;
705 goto done;
706 }
707 memset(buf, 0, buf_len);
708 goto again;
709 }
710
711 /* Now we're done */
712
713 if (buf && buf_ndx > 0) {
714 buf[buf_ndx - 1] = '\0';
715 }
716
717 *gr_mem = buf;
718 *gr_mem_len = buf_len;
719
720 DEBUG(10, ("num_mem = %u, len = %u, mem = %s\n",
721 (unsigned int)*num_gr_mem,
722 (unsigned int)buf_len, *num_gr_mem ? buf : "NULL"));
723 result = True;
724
725done:
726
727 talloc_destroy(mem_ctx);
728
729 DEBUG(10, ("fill_grent_mem returning %d\n", result));
730
731 return result;
732}
733
734static void winbindd_getgrsid(struct winbindd_cli_state *state, DOM_SID group_sid);
735
736static void getgrnam_recv( void *private_data, bool success, const DOM_SID *sid,
737 enum lsa_SidType type )
738{
739 struct winbindd_cli_state *state = (struct winbindd_cli_state*)private_data;
740
741 if (!success) {
742 DEBUG(5,("getgrnam_recv: lookupname failed!\n"));
743 request_error(state);
744 return;
745 }
746
747 if ( (type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) ) {
748 DEBUG(5,("getgrnam_recv: not a group!\n"));
749 request_error(state);
750 return;
751 }
752
753 winbindd_getgrsid( state, *sid );
754}
755
756
757/* Return a group structure from a group name */
758
759void winbindd_getgrnam(struct winbindd_cli_state *state)
760{
761 struct winbindd_domain *domain;
762 fstring name_domain, name_group;
763 char *tmp;
764 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
765
766 /* Ensure null termination */
767 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0';
768
769 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid,
770 state->request.data.groupname));
771
772 nt_status = normalize_name_unmap(state->mem_ctx,
773 state->request.data.groupname,
774 &tmp);
775 /* If we didn't map anything in the above call, just reset the
776 tmp pointer to the original string */
777 if (!NT_STATUS_IS_OK(nt_status) &&
778 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
779 {
780 tmp = state->request.data.groupname;
781 }
782
783 /* Parse domain and groupname */
784
785 memset(name_group, 0, sizeof(name_group));
786
787 name_domain[0] = '\0';
788 name_group[0] = '\0';
789
790 parse_domain_user(tmp, name_domain, name_group);
791
792 /* if no domain or our local domain and no local tdb group, default to
793 * our local domain for aliases */
794
795 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) {
796 fstrcpy(name_domain, get_global_sam_name());
797 }
798
799 /* Get info for the domain */
800
801 if ((domain = find_domain_from_name_noinit(name_domain)) == NULL) {
802 DEBUG(3, ("could not get domain sid for domain %s\n",
803 name_domain));
804 request_error(state);
805 return;
806 }
807 /* should we deal with users for our domain? */
808
809 if ( lp_winbind_trusted_domains_only() && domain->primary) {
810 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting "
811 "getgrnam() for %s\\%s.\n", name_domain, name_group));
812 request_error(state);
813 return;
814 }
815
816 /* Get rid and name type from name */
817
818 fstrcpy( name_group, tmp );
819
820 winbindd_lookupname_async( state->mem_ctx, domain->name, name_group,
821 getgrnam_recv, WINBINDD_GETGRNAM, state );
822}
823
824struct getgrsid_state {
825 struct winbindd_cli_state *state;
826 struct winbindd_domain *domain;
827 char *group_name;
828 enum lsa_SidType group_type;
829 uid_t gid;
830 DOM_SID group_sid;
831};
832
833static void getgrsid_sid2gid_recv(void *private_data, bool success, gid_t gid)
834{
835 struct getgrsid_state *s =
836 (struct getgrsid_state *)private_data;
837 struct winbindd_domain *domain;
838 size_t gr_mem_len;
839 size_t num_gr_mem;
840 char *gr_mem;
841 fstring dom_name, group_name;
842
843 if (!success) {
844 DEBUG(5,("getgrsid_sid2gid_recv: sid2gid failed!\n"));
845 request_error(s->state);
846 return;
847 }
848
849 s->gid = gid;
850
851 if ( !parse_domain_user( s->group_name, dom_name, group_name ) ) {
852 DEBUG(5,("getgrsid_sid2gid_recv: parse_domain_user() failed!\n"));
853 request_error(s->state);
854 return;
855 }
856
857
858 /* Fill in group structure */
859
860 if ( (domain = find_domain_from_name_noinit(dom_name)) == NULL ) {
861 DEBUG(1,("Can't find domain from name (%s)\n", dom_name));
862 request_error(s->state);
863 return;
864 }
865
866 if (!fill_grent(s->state->mem_ctx, &s->state->response.data.gr,
867 dom_name, group_name, gid) ||
868 !fill_grent_mem(domain, s->state, &s->group_sid, s->group_type,
869 &num_gr_mem, &gr_mem, &gr_mem_len))
870 {
871 request_error(s->state);
872 return;
873 }
874
875 s->state->response.data.gr.num_gr_mem = (uint32)num_gr_mem;
876
877 /* Group membership lives at start of extra data */
878
879 s->state->response.data.gr.gr_mem_ofs = 0;
880
881 s->state->response.length += gr_mem_len;
882 s->state->response.extra_data.data = gr_mem;
883
884 request_ok(s->state);
885}
886
887static void getgrsid_lookupsid_recv( void *private_data, bool success,
888 const char *dom_name, const char *name,
889 enum lsa_SidType name_type )
890{
891 struct getgrsid_state *s = (struct getgrsid_state *)private_data;
892 char *mapped_name = NULL;
893 fstring raw_name;
894 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
895
896 if (!success) {
897 DEBUG(5,("getgrsid_lookupsid_recv: lookupsid failed!\n"));
898 request_error(s->state);
899 return;
900 }
901
902 /* either it's a domain group, a domain local group, or a
903 local group in an internal domain */
904
905 if ( !( (name_type==SID_NAME_DOM_GRP) ||
906 ((name_type==SID_NAME_ALIAS) &&
907 (s->domain->primary || s->domain->internal)) ) )
908 {
909 DEBUG(1, ("name '%s\\%s' is not a local or domain group: %d\n",
910 dom_name, name, name_type));
911 request_error(s->state);
912 return;
913 }
914
915 /* normalize the name and ensure that we have the DOM\name
916 coming out of here */
917
918 fstrcpy(raw_name, name);
919
920 nt_status = normalize_name_unmap(s->state->mem_ctx, raw_name,
921 &mapped_name);
922
923 /* basic whitespace reversal */
924 if (NT_STATUS_IS_OK(nt_status)) {
925 s->group_name = talloc_asprintf(s->state->mem_ctx,
926 "%s%c%s",
927 dom_name,
928 *lp_winbind_separator(),
929 mapped_name);
930 }
931 /* mapped from alias */
932 else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED)) {
933 s->group_name = mapped_name;
934 }
935 /* no mapping at all. use original string */
936 else {
937 s->group_name = talloc_asprintf(s->state->mem_ctx,
938 "%s%c%s",
939 dom_name,
940 *lp_winbind_separator(),
941 raw_name);
942 }
943
944 if (s->group_name == NULL) {
945 DEBUG(1, ("getgrsid_lookupsid_recv: group_name is NULL!\n"));
946 request_error(s->state);
947 return;
948 }
949
950 s->group_type = name_type;
951
952 winbindd_sid2gid_async(s->state->mem_ctx, &s->group_sid,
953 getgrsid_sid2gid_recv, s);
954}
955
956static void winbindd_getgrsid( struct winbindd_cli_state *state, const DOM_SID group_sid )
957{
958 struct getgrsid_state *s;
959
960 if ( (s = TALLOC_ZERO_P(state->mem_ctx, struct getgrsid_state)) == NULL ) {
961 DEBUG(0, ("talloc failed\n"));
962 request_error(state);
963 return;
964 }
965
966 s->state = state;
967
968 if ( (s->domain = find_domain_from_sid_noinit(&group_sid)) == NULL ) {
969 DEBUG(3, ("Could not find domain for sid %s\n",
970 sid_string_dbg(&group_sid)));
971 request_error(state);
972 return;
973 }
974
975 sid_copy(&s->group_sid, &group_sid);
976
977 winbindd_lookupsid_async( s->state->mem_ctx, &group_sid,
978 getgrsid_lookupsid_recv, s );
979}
980
981
982static void getgrgid_recv(void *private_data, bool success, const char *sid)
983{
984 struct winbindd_cli_state *state = talloc_get_type_abort(private_data, struct winbindd_cli_state);
985 enum lsa_SidType name_type;
986 DOM_SID group_sid;
987
988 if (success) {
989 DEBUG(10,("getgrgid_recv: gid %lu has sid %s\n",
990 (unsigned long)(state->request.data.gid), sid));
991
992 if (!string_to_sid(&group_sid, sid)) {
993 DEBUG(1,("getgrgid_recv: Could not convert sid %s "
994 "from string\n", sid));
995 request_error(state);
996 return;
997 }
998
999 winbindd_getgrsid(state, group_sid);
1000 return;
1001 }
1002
1003 /* Ok, this might be "ours", i.e. an alias */
1004 if (pdb_gid_to_sid(state->request.data.gid, &group_sid) &&
1005 lookup_sid(state->mem_ctx, &group_sid, NULL, NULL, &name_type) &&
1006 (name_type == SID_NAME_ALIAS)) {
1007 /* Hey, got an alias */
1008 DEBUG(10,("getgrgid_recv: we have an alias with gid %lu and sid %s\n",
1009 (unsigned long)(state->request.data.gid), sid));
1010 winbindd_getgrsid(state, group_sid);
1011 return;
1012 }
1013
1014 DEBUG(1, ("could not convert gid %lu to sid\n",
1015 (unsigned long)state->request.data.gid));
1016 request_error(state);
1017}
1018
1019/* Return a group structure from a gid number */
1020void winbindd_getgrgid(struct winbindd_cli_state *state)
1021{
1022 gid_t gid = state->request.data.gid;
1023
1024 DEBUG(3, ("[%5lu]: getgrgid %lu\n",
1025 (unsigned long)state->pid,
1026 (unsigned long)gid));
1027
1028 /* always use the async interface */
1029 winbindd_gid2sid_async(state->mem_ctx, gid, getgrgid_recv, state);
1030}
1031
1032/*
1033 * set/get/endgrent functions
1034 */
1035
1036/* "Rewind" file pointer for group database enumeration */
1037
1038static bool winbindd_setgrent_internal(struct winbindd_cli_state *state)
1039{
1040 struct winbindd_domain *domain;
1041
1042 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid));
1043
1044 /* Check user has enabled this */
1045
1046 if (!lp_winbind_enum_groups()) {
1047 return False;
1048 }
1049
1050 /* Free old static data if it exists */
1051
1052 if (state->getgrent_state != NULL) {
1053 free_getent_state(state->getgrent_state);
1054 state->getgrent_state = NULL;
1055 }
1056
1057 /* Create sam pipes for each domain we know about */
1058
1059 for (domain = domain_list(); domain != NULL; domain = domain->next) {
1060 struct getent_state *domain_state;
1061
1062 /* Create a state record for this domain */
1063
1064 /* don't add our domaina if we are a PDC or if we
1065 are a member of a Samba domain */
1066
1067 if ( lp_winbind_trusted_domains_only() && domain->primary )
1068 {
1069 continue;
1070 }
1071
1072 domain_state = SMB_MALLOC_P(struct getent_state);
1073 if (!domain_state) {
1074 DEBUG(1, ("winbindd_setgrent: "
1075 "malloc failed for domain_state!\n"));
1076 return False;
1077 }
1078
1079 ZERO_STRUCTP(domain_state);
1080
1081 fstrcpy(domain_state->domain_name, domain->name);
1082
1083 /* Add to list of open domains */
1084
1085 DLIST_ADD(state->getgrent_state, domain_state);
1086 }
1087
1088 state->getgrent_initialized = True;
1089 return True;
1090}
1091
1092void winbindd_setgrent(struct winbindd_cli_state *state)
1093{
1094 if (winbindd_setgrent_internal(state)) {
1095 request_ok(state);
1096 } else {
1097 request_error(state);
1098 }
1099}
1100
1101/* Close file pointer to ntdom group database */
1102
1103void winbindd_endgrent(struct winbindd_cli_state *state)
1104{
1105 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid));
1106
1107 free_getent_state(state->getgrent_state);
1108 state->getgrent_initialized = False;
1109 state->getgrent_state = NULL;
1110 request_ok(state);
1111}
1112
1113/* Get the list of domain groups and domain aliases for a domain. We fill in
1114 the sam_entries and num_sam_entries fields with domain group information.
1115 Return True if some groups were returned, False otherwise. */
1116
1117bool get_sam_group_entries(struct getent_state *ent)
1118{
1119 NTSTATUS status;
1120 uint32 num_entries;
1121 struct acct_info *name_list = NULL;
1122 TALLOC_CTX *mem_ctx;
1123 bool result = False;
1124 struct acct_info *sam_grp_entries = NULL;
1125 struct winbindd_domain *domain;
1126
1127 if (ent->got_sam_entries)
1128 return False;
1129
1130 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)",
1131 ent->domain_name))) {
1132 DEBUG(1, ("get_sam_group_entries: "
1133 "could not create talloc context!\n"));
1134 return False;
1135 }
1136
1137 /* Free any existing group info */
1138
1139 SAFE_FREE(ent->sam_entries);
1140 ent->num_sam_entries = 0;
1141 ent->got_sam_entries = True;
1142
1143 /* Enumerate domain groups */
1144
1145 num_entries = 0;
1146
1147 if (!(domain = find_domain_from_name(ent->domain_name))) {
1148 DEBUG(3, ("no such domain %s in get_sam_group_entries\n",
1149 ent->domain_name));
1150 goto done;
1151 }
1152
1153 /* always get the domain global groups */
1154
1155 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries,
1156 &sam_grp_entries);
1157
1158 if (!NT_STATUS_IS_OK(status)) {
1159 DEBUG(3, ("get_sam_group_entries: "
1160 "could not enumerate domain groups! Error: %s\n",
1161 nt_errstr(status)));
1162 result = False;
1163 goto done;
1164 }
1165
1166 /* Copy entries into return buffer */
1167
1168 if (num_entries) {
1169 name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries);
1170 if (!name_list) {
1171 DEBUG(0,("get_sam_group_entries: Failed to malloc "
1172 "memory for %d domain groups!\n",
1173 num_entries));
1174 result = False;
1175 goto done;
1176 }
1177 memcpy(name_list, sam_grp_entries,
1178 num_entries * sizeof(struct acct_info));
1179 }
1180
1181 ent->num_sam_entries = num_entries;
1182
1183 /* get the domain local groups if we are a member of a native win2k
1184 * domain and are not using LDAP to get the groups */
1185
1186 if ( ( lp_security() != SEC_ADS && domain->native_mode
1187 && domain->primary) || domain->internal )
1188 {
1189 DEBUG(4,("get_sam_group_entries: %s domain; "
1190 "enumerating local groups as well\n",
1191 domain->native_mode ? "Native Mode 2k":
1192 "BUILTIN or local"));
1193
1194 status = domain->methods->enum_local_groups(domain, mem_ctx,
1195 &num_entries,
1196 &sam_grp_entries);
1197
1198 if ( !NT_STATUS_IS_OK(status) ) {
1199 DEBUG(3,("get_sam_group_entries: "
1200 "Failed to enumerate "
1201 "domain local groups with error %s!\n",
1202 nt_errstr(status)));
1203 num_entries = 0;
1204 }
1205 else
1206 DEBUG(4,("get_sam_group_entries: "
1207 "Returned %d local groups\n",
1208 num_entries));
1209
1210 /* Copy entries into return buffer */
1211
1212 if ( num_entries ) {
1213 name_list = SMB_REALLOC_ARRAY(name_list,
1214 struct acct_info,
1215 ent->num_sam_entries+
1216 num_entries);
1217 if (!name_list) {
1218 DEBUG(0,("get_sam_group_entries: "
1219 "Failed to realloc more memory "
1220 "for %d local groups!\n",
1221 num_entries));
1222 result = False;
1223 goto done;
1224 }
1225
1226 memcpy(&name_list[ent->num_sam_entries],
1227 sam_grp_entries,
1228 num_entries * sizeof(struct acct_info));
1229 }
1230
1231 ent->num_sam_entries += num_entries;
1232 }
1233
1234
1235 /* Fill in remaining fields */
1236
1237 ent->sam_entries = name_list;
1238 ent->sam_entry_index = 0;
1239
1240 result = (ent->num_sam_entries > 0);
1241
1242 done:
1243 talloc_destroy(mem_ctx);
1244
1245 return result;
1246}
1247
1248/* Fetch next group entry from ntdom database */
1249
1250#define MAX_GETGRENT_GROUPS 500
1251
1252void winbindd_getgrent(struct winbindd_cli_state *state)
1253{
1254 struct getent_state *ent;
1255 struct winbindd_gr *group_list = NULL;
1256 int num_groups, group_list_ndx, gr_mem_list_len = 0;
1257 char *gr_mem_list = NULL;
1258
1259 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid));
1260
1261 /* Check user has enabled this */
1262
1263 if (!lp_winbind_enum_groups()) {
1264 request_error(state);
1265 return;
1266 }
1267
1268 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries);
1269
1270 if (num_groups == 0) {
1271 request_error(state);
1272 return;
1273 }
1274
1275 group_list = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups);
1276 if (!group_list) {
1277 request_error(state);
1278 return;
1279 }
1280 /* will be freed by process_request() */
1281 state->response.extra_data.data = group_list;
1282
1283 memset(state->response.extra_data.data, '\0',
1284 num_groups * sizeof(struct winbindd_gr) );
1285
1286 state->response.data.num_entries = 0;
1287
1288 if (!state->getgrent_initialized)
1289 winbindd_setgrent_internal(state);
1290
1291 if (!(ent = state->getgrent_state)) {
1292 request_error(state);
1293 return;
1294 }
1295
1296 /* Start sending back groups */
1297
1298 for (group_list_ndx = 0; group_list_ndx < num_groups; ) {
1299 struct acct_info *name_list = NULL;
1300 fstring domain_group_name;
1301 uint32 result;
1302 gid_t group_gid;
1303 size_t gr_mem_len;
1304 char *gr_mem;
1305 DOM_SID group_sid;
1306 struct winbindd_domain *domain;
1307
1308 /* Do we need to fetch another chunk of groups? */
1309
1310 tryagain:
1311
1312 DEBUG(10, ("entry_index = %d, num_entries = %d\n",
1313 ent->sam_entry_index, ent->num_sam_entries));
1314
1315 if (ent->num_sam_entries == ent->sam_entry_index) {
1316
1317 while(ent && !get_sam_group_entries(ent)) {
1318 struct getent_state *next_ent;
1319
1320 DEBUG(10, ("freeing state info for domain %s\n",
1321 ent->domain_name));
1322
1323 /* Free state information for this domain */
1324
1325 SAFE_FREE(ent->sam_entries);
1326
1327 next_ent = ent->next;
1328 DLIST_REMOVE(state->getgrent_state, ent);
1329
1330 SAFE_FREE(ent);
1331 ent = next_ent;
1332 }
1333
1334 /* No more domains */
1335
1336 if (!ent)
1337 break;
1338 }
1339
1340 name_list = (struct acct_info *)ent->sam_entries;
1341
1342 if (!(domain = find_domain_from_name(ent->domain_name))) {
1343 DEBUG(3, ("No such domain %s in winbindd_getgrent\n",
1344 ent->domain_name));
1345 result = False;
1346 goto done;
1347 }
1348
1349 /* Lookup group info */
1350
1351 sid_copy(&group_sid, &domain->sid);
1352 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid);
1353
1354 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(domain->have_idmap_config
1355 ? domain->name : "",
1356 &group_sid, &group_gid)))
1357 {
1358 union unid_t id;
1359 enum lsa_SidType type;
1360
1361 DEBUG(10, ("SID %s not in idmap\n",
1362 sid_string_dbg(&group_sid)));
1363
1364 if (!pdb_sid_to_id(&group_sid, &id, &type)) {
1365 DEBUG(1,("could not look up gid for group %s\n",
1366 name_list[ent->sam_entry_index].acct_name));
1367 ent->sam_entry_index++;
1368 goto tryagain;
1369 }
1370
1371 if ((type != SID_NAME_DOM_GRP) &&
1372 (type != SID_NAME_ALIAS) &&
1373 (type != SID_NAME_WKN_GRP)) {
1374 DEBUG(1, ("Group %s is a %s, not a group\n",
1375 sid_type_lookup(type),
1376 name_list[ent->sam_entry_index].acct_name));
1377 ent->sam_entry_index++;
1378 goto tryagain;
1379 }
1380 group_gid = id.gid;
1381 }
1382
1383 DEBUG(10, ("got gid %lu for group %lu\n",
1384 (unsigned long)group_gid,
1385 (unsigned long)name_list[ent->sam_entry_index].rid));
1386
1387 /* Fill in group entry */
1388
1389 fill_domain_username(domain_group_name, ent->domain_name,
1390 name_list[ent->sam_entry_index].acct_name, True);
1391
1392 result = fill_grent(state->mem_ctx, &group_list[group_list_ndx],
1393 ent->domain_name,
1394 name_list[ent->sam_entry_index].acct_name,
1395 group_gid);
1396
1397 /* Fill in group membership entry */
1398
1399 if (result) {
1400 size_t num_gr_mem = 0;
1401 DOM_SID member_sid;
1402 group_list[group_list_ndx].num_gr_mem = 0;
1403 gr_mem = NULL;
1404 gr_mem_len = 0;
1405
1406 /* Get group membership */
1407 if (state->request.cmd == WINBINDD_GETGRLST) {
1408 result = True;
1409 } else {
1410 sid_copy(&member_sid, &domain->sid);
1411 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid);
1412 result = fill_grent_mem(
1413 domain,
1414 NULL,
1415 &member_sid,
1416 SID_NAME_DOM_GRP,
1417 &num_gr_mem,
1418 &gr_mem, &gr_mem_len);
1419
1420 group_list[group_list_ndx].num_gr_mem = (uint32)num_gr_mem;
1421 }
1422 }
1423
1424 if (result) {
1425 /* Append to group membership list */
1426 gr_mem_list = (char *)SMB_REALLOC(
1427 gr_mem_list, gr_mem_list_len + gr_mem_len);
1428
1429 if (!gr_mem_list &&
1430 (group_list[group_list_ndx].num_gr_mem != 0)) {
1431 DEBUG(0, ("out of memory\n"));
1432 gr_mem_list_len = 0;
1433 break;
1434 }
1435
1436 DEBUG(10, ("list_len = %d, mem_len = %u\n",
1437 gr_mem_list_len, (unsigned int)gr_mem_len));
1438
1439 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem,
1440 gr_mem_len);
1441
1442 SAFE_FREE(gr_mem);
1443
1444 group_list[group_list_ndx].gr_mem_ofs =
1445 gr_mem_list_len;
1446
1447 gr_mem_list_len += gr_mem_len;
1448 }
1449
1450 ent->sam_entry_index++;
1451
1452 /* Add group to return list */
1453
1454 if (result) {
1455
1456 DEBUG(10, ("adding group num_entries = %d\n",
1457 state->response.data.num_entries));
1458
1459 group_list_ndx++;
1460 state->response.data.num_entries++;
1461
1462 state->response.length +=
1463 sizeof(struct winbindd_gr);
1464
1465 } else {
1466 DEBUG(0, ("could not lookup domain group %s\n",
1467 domain_group_name));
1468 }
1469 }
1470
1471 /* Copy the list of group memberships to the end of the extra data */
1472
1473 if (group_list_ndx == 0)
1474 goto done;
1475
1476 state->response.extra_data.data = SMB_REALLOC(
1477 state->response.extra_data.data,
1478 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len);
1479
1480 if (!state->response.extra_data.data) {
1481 DEBUG(0, ("out of memory\n"));
1482 group_list_ndx = 0;
1483 SAFE_FREE(gr_mem_list);
1484 request_error(state);
1485 return;
1486 }
1487
1488 memcpy(&((char *)state->response.extra_data.data)
1489 [group_list_ndx * sizeof(struct winbindd_gr)],
1490 gr_mem_list, gr_mem_list_len);
1491
1492 state->response.length += gr_mem_list_len;
1493
1494 DEBUG(10, ("returning %d groups, length = %d\n",
1495 group_list_ndx, gr_mem_list_len));
1496
1497 /* Out of domains */
1498
1499 done:
1500
1501 SAFE_FREE(gr_mem_list);
1502
1503 if (group_list_ndx > 0)
1504 request_ok(state);
1505 else
1506 request_error(state);
1507}
1508
1509/* List domain groups without mapping to unix ids */
1510void winbindd_list_groups(struct winbindd_cli_state *state)
1511{
1512 winbindd_list_ent(state, LIST_GROUPS);
1513}
1514
1515/* Get user supplementary groups. This is much quicker than trying to
1516 invert the groups database. We merge the groups from the gids and
1517 other_sids info3 fields as trusted domain, universal group
1518 memberships, and nested groups (win2k native mode only) are not
1519 returned by the getgroups RPC call but are present in the info3. */
1520
1521struct getgroups_state {
1522 struct winbindd_cli_state *state;
1523 struct winbindd_domain *domain;
1524 char *domname;
1525 char *username;
1526 DOM_SID user_sid;
1527
1528 const DOM_SID *token_sids;
1529 size_t i, num_token_sids;
1530
1531 gid_t *token_gids;
1532 size_t num_token_gids;
1533};
1534
1535static void getgroups_usersid_recv(void *private_data, bool success,
1536 const DOM_SID *sid, enum lsa_SidType type);
1537static void getgroups_tokensids_recv(void *private_data, bool success,
1538 DOM_SID *token_sids, size_t num_token_sids);
1539static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid);
1540
1541void winbindd_getgroups(struct winbindd_cli_state *state)
1542{
1543 struct getgroups_state *s;
1544 char *real_name = NULL;
1545 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
1546
1547 /* Ensure null termination */
1548 state->request.data.username
1549 [sizeof(state->request.data.username)-1]='\0';
1550
1551 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid,
1552 state->request.data.username));
1553
1554 /* Parse domain and username */
1555
1556 s = TALLOC_P(state->mem_ctx, struct getgroups_state);
1557 if (s == NULL) {
1558 DEBUG(0, ("talloc failed\n"));
1559 request_error(state);
1560 return;
1561 }
1562
1563 s->state = state;
1564
1565 nt_status = normalize_name_unmap(state->mem_ctx,
1566 state->request.data.username,
1567 &real_name);
1568
1569 /* Reset the real_name pointer if we didn't do anything
1570 productive in the above call */
1571 if (!NT_STATUS_IS_OK(nt_status) &&
1572 !NT_STATUS_EQUAL(nt_status, NT_STATUS_FILE_RENAMED))
1573 {
1574 real_name = state->request.data.username;
1575 }
1576
1577 if (!parse_domain_user_talloc(state->mem_ctx, real_name,
1578 &s->domname, &s->username)) {
1579 DEBUG(5, ("Could not parse domain user: %s\n",
1580 real_name));
1581
1582 /* error out if we do not have nested group support */
1583
1584 if ( !lp_winbind_nested_groups() ) {
1585 request_error(state);
1586 return;
1587 }
1588
1589 s->domname = talloc_strdup(state->mem_ctx,
1590 get_global_sam_name());
1591 s->username = talloc_strdup(state->mem_ctx,
1592 state->request.data.username);
1593 }
1594
1595 /* Get info for the domain (either by short domain name or
1596 DNS name in the case of a UPN) */
1597
1598 s->domain = find_domain_from_name_noinit(s->domname);
1599 if (!s->domain) {
1600 char *p = strchr(s->username, '@');
1601
1602 if (p) {
1603 s->domain = find_domain_from_name_noinit(p+1);
1604 }
1605
1606 }
1607
1608 if (s->domain == NULL) {
1609 DEBUG(7, ("could not find domain entry for domain %s\n",
1610 s->domname));
1611 request_error(state);
1612 return;
1613 }
1614
1615 if ( s->domain->primary && lp_winbind_trusted_domains_only()) {
1616 DEBUG(7,("winbindd_getgroups: My domain -- rejecting "
1617 "getgroups() for %s\\%s.\n", s->domname,
1618 s->username));
1619 request_error(state);
1620 return;
1621 }
1622
1623 /* Get rid and name type from name. The following costs 1 packet */
1624
1625 winbindd_lookupname_async(state->mem_ctx,
1626 s->domname, s->username,
1627 getgroups_usersid_recv,
1628 WINBINDD_GETGROUPS, s);
1629}
1630
1631static void getgroups_usersid_recv(void *private_data, bool success,
1632 const DOM_SID *sid, enum lsa_SidType type)
1633{
1634 struct getgroups_state *s =
1635 (struct getgroups_state *)private_data;
1636
1637 if ((!success) ||
1638 ((type != SID_NAME_USER) && (type != SID_NAME_COMPUTER))) {
1639 request_error(s->state);
1640 return;
1641 }
1642
1643 sid_copy(&s->user_sid, sid);
1644
1645 winbindd_gettoken_async(s->state->mem_ctx, &s->user_sid,
1646 getgroups_tokensids_recv, s);
1647}
1648
1649static void getgroups_tokensids_recv(void *private_data, bool success,
1650 DOM_SID *token_sids, size_t num_token_sids)
1651{
1652 struct getgroups_state *s =
1653 (struct getgroups_state *)private_data;
1654
1655 /* We need at least the user sid and the primary group in the token,
1656 * otherwise it's an error */
1657
1658 if ((!success) || (num_token_sids < 2)) {
1659 request_error(s->state);
1660 return;
1661 }
1662
1663 s->token_sids = token_sids;
1664 s->num_token_sids = num_token_sids;
1665 s->i = 0;
1666
1667 s->token_gids = NULL;
1668 s->num_token_gids = 0;
1669
1670 getgroups_sid2gid_recv(s, False, 0);
1671}
1672
1673static void getgroups_sid2gid_recv(void *private_data, bool success, gid_t gid)
1674{
1675 struct getgroups_state *s =
1676 (struct getgroups_state *)private_data;
1677
1678 if (success) {
1679 if (!add_gid_to_array_unique(s->state->mem_ctx, gid,
1680 &s->token_gids,
1681 &s->num_token_gids)) {
1682 return;
1683 }
1684 }
1685
1686 if (s->i < s->num_token_sids) {
1687 const DOM_SID *sid = &s->token_sids[s->i];
1688 s->i += 1;
1689
1690 if (sid_equal(sid, &s->user_sid)) {
1691 getgroups_sid2gid_recv(s, False, 0);
1692 return;
1693 }
1694
1695 winbindd_sid2gid_async(s->state->mem_ctx, sid,
1696 getgroups_sid2gid_recv, s);
1697 return;
1698 }
1699
1700 s->state->response.data.num_entries = s->num_token_gids;
1701 if (s->num_token_gids) {
1702 /* s->token_gids are talloced */
1703 s->state->response.extra_data.data =
1704 smb_xmemdup(s->token_gids,
1705 s->num_token_gids * sizeof(gid_t));
1706 s->state->response.length += s->num_token_gids * sizeof(gid_t);
1707 }
1708 request_ok(s->state);
1709}
1710
1711/* Get user supplementary sids. This is equivalent to the
1712 winbindd_getgroups() function but it involves a SID->SIDs mapping
1713 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid
1714 idmap. This call is designed to be used with applications that need
1715 to do ACL evaluation themselves. Note that the cached info3 data is
1716 not used
1717
1718 this function assumes that the SID that comes in is a user SID. If
1719 you pass in another type of SID then you may get unpredictable
1720 results.
1721*/
1722
1723static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1724 size_t num_sids);
1725
1726void winbindd_getusersids(struct winbindd_cli_state *state)
1727{
1728 DOM_SID *user_sid;
1729
1730 /* Ensure null termination */
1731 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1732
1733 user_sid = TALLOC_P(state->mem_ctx, DOM_SID);
1734 if (user_sid == NULL) {
1735 DEBUG(1, ("talloc failed\n"));
1736 request_error(state);
1737 return;
1738 }
1739
1740 if (!string_to_sid(user_sid, state->request.data.sid)) {
1741 DEBUG(1, ("Could not get convert sid %s from string\n",
1742 state->request.data.sid));
1743 request_error(state);
1744 return;
1745 }
1746
1747 winbindd_gettoken_async(state->mem_ctx, user_sid, getusersids_recv,
1748 state);
1749}
1750
1751static void getusersids_recv(void *private_data, bool success, DOM_SID *sids,
1752 size_t num_sids)
1753{
1754 struct winbindd_cli_state *state =
1755 (struct winbindd_cli_state *)private_data;
1756 char *ret = NULL;
1757 unsigned ofs, ret_size = 0;
1758 size_t i;
1759
1760 if (!success) {
1761 request_error(state);
1762 return;
1763 }
1764
1765 /* work out the response size */
1766 for (i = 0; i < num_sids; i++) {
1767 fstring s;
1768 sid_to_fstring(s, &sids[i]);
1769 ret_size += strlen(s) + 1;
1770 }
1771
1772 /* build the reply */
1773 ret = (char *)SMB_MALLOC(ret_size);
1774 if (!ret) {
1775 DEBUG(0, ("malloc failed\n"));
1776 request_error(state);
1777 return;
1778 }
1779 ofs = 0;
1780 for (i = 0; i < num_sids; i++) {
1781 fstring s;
1782 sid_to_fstring(s, &sids[i]);
1783 safe_strcpy(ret + ofs, s, ret_size - ofs - 1);
1784 ofs += strlen(ret+ofs) + 1;
1785 }
1786
1787 /* Send data back to client */
1788 state->response.data.num_entries = num_sids;
1789 state->response.extra_data.data = ret;
1790 state->response.length += ret_size;
1791 request_ok(state);
1792}
1793
1794void winbindd_getuserdomgroups(struct winbindd_cli_state *state)
1795{
1796 DOM_SID user_sid;
1797 struct winbindd_domain *domain;
1798
1799 /* Ensure null termination */
1800 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1801
1802 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1803 DEBUG(1, ("Could not get convert sid %s from string\n",
1804 state->request.data.sid));
1805 request_error(state);
1806 return;
1807 }
1808
1809 /* Get info for the domain */
1810 if ((domain = find_domain_from_sid_noinit(&user_sid)) == NULL) {
1811 DEBUG(0,("could not find domain entry for sid %s\n",
1812 sid_string_dbg(&user_sid)));
1813 request_error(state);
1814 return;
1815 }
1816
1817 sendto_domain(state, domain);
1818}
1819
1820enum winbindd_result winbindd_dual_getuserdomgroups(struct winbindd_domain *domain,
1821 struct winbindd_cli_state *state)
1822{
1823 DOM_SID user_sid;
1824 NTSTATUS status;
1825
1826 char *sidstring;
1827 ssize_t len;
1828 DOM_SID *groups;
1829 uint32 num_groups;
1830
1831 /* Ensure null termination */
1832 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0';
1833
1834 if (!string_to_sid(&user_sid, state->request.data.sid)) {
1835 DEBUG(1, ("Could not get convert sid %s from string\n",
1836 state->request.data.sid));
1837 return WINBINDD_ERROR;
1838 }
1839
1840 status = domain->methods->lookup_usergroups(domain, state->mem_ctx,
1841 &user_sid, &num_groups,
1842 &groups);
1843 if (!NT_STATUS_IS_OK(status))
1844 return WINBINDD_ERROR;
1845
1846 if (num_groups == 0) {
1847 state->response.data.num_entries = 0;
1848 state->response.extra_data.data = NULL;
1849 return WINBINDD_OK;
1850 }
1851
1852 if (!print_sidlist(state->mem_ctx,
1853 groups, num_groups,
1854 &sidstring, &len)) {
1855 DEBUG(0, ("talloc failed\n"));
1856 return WINBINDD_ERROR;
1857 }
1858
1859 state->response.extra_data.data = SMB_STRDUP(sidstring);
1860 if (!state->response.extra_data.data) {
1861 return WINBINDD_ERROR;
1862 }
1863 state->response.length += len+1;
1864 state->response.data.num_entries = num_groups;
1865
1866 return WINBINDD_OK;
1867}
Note: See TracBrowser for help on using the repository browser.