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 2 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, write to the Free Software 23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 24*/ 25 26#include "includes.h" 27#include "winbindd.h" 28 29extern BOOL opt_nocache; 30 31#undef DBGC_CLASS 32#define DBGC_CLASS DBGC_WINBIND 33 34/********************************************************************* 35*********************************************************************/ 36 37static int gr_mem_buffer( char **buffer, char **members, int num_members ) 38{ 39 int i; 40 int len = 0; 41 int idx = 0; 42 43 if ( num_members == 0 ) { 44 *buffer = NULL; 45 return 0; 46 } 47 48 for ( i=0; i<num_members; i++ ) 49 len += strlen(members[i])+1; 50 51 *buffer = SMB_XMALLOC_ARRAY(char, len); 52 for ( i=0; i<num_members; i++ ) { 53 snprintf( &(*buffer)[idx], len-idx, "%s,", members[i]); 54 idx += strlen(members[i])+1; 55 } 56 /* terminate with NULL */ 57 (*buffer)[len-1] = '\0'; 58 59 return len; 60} 61 62/*************************************************************** 63 Empty static struct for negative caching. 64****************************************************************/ 65 66/* Fill a grent structure from various other information */ 67 68static BOOL fill_grent(struct winbindd_gr *gr, const char *dom_name, 69 const char *gr_name, gid_t unix_gid) 70{ 71 fstring full_group_name; 72 /* Fill in uid/gid */ 73 fill_domain_username(full_group_name, dom_name, gr_name); 74 75 gr->gr_gid = unix_gid; 76 77 /* Group name and password */ 78 79 safe_strcpy(gr->gr_name, full_group_name, sizeof(gr->gr_name) - 1); 80 safe_strcpy(gr->gr_passwd, "x", sizeof(gr->gr_passwd) - 1); 81 82 return True; 83} 84 85/* Fill in the group membership field of a NT group given by group_sid */ 86 87static BOOL fill_grent_mem(struct winbindd_domain *domain, 88 DOM_SID *group_sid, 89 enum SID_NAME_USE group_name_type, 90 int *num_gr_mem, char **gr_mem, int *gr_mem_len) 91{ 92 DOM_SID **sid_mem = NULL; 93 uint32 num_names = 0; 94 uint32 *name_types = NULL; 95 unsigned int buf_len, buf_ndx, i; 96 char **names = NULL, *buf; 97 BOOL result = False; 98 TALLOC_CTX *mem_ctx; 99 NTSTATUS status; 100 fstring sid_string; 101 102 if (!(mem_ctx = talloc_init("fill_grent_mem(%s)", domain->name))) 103 return False; 104 105 /* Initialise group membership information */ 106 107 DEBUG(10, ("group SID %s\n", sid_to_string(sid_string, group_sid))); 108 109 *num_gr_mem = 0; 110 111 /* HACK ALERT!! This whole routine does not cope with group members 112 * from more than one domain, ie aliases. Thus we have to work it out 113 * ourselves in a special routine. */ 114 115 if (domain->internal) 116 return fill_passdb_alias_grmem(domain, group_sid, 117 num_gr_mem, 118 gr_mem, gr_mem_len); 119 120 if ( !((group_name_type==SID_NAME_DOM_GRP) || 121 ((group_name_type==SID_NAME_ALIAS) && domain->primary)) ) 122 { 123 DEBUG(1, ("SID %s in domain %s isn't a domain group (%d)\n", 124 sid_to_string(sid_string, group_sid), domain->name, 125 group_name_type)); 126 goto done; 127 } 128 129 /* Lookup group members */ 130 status = domain->methods->lookup_groupmem(domain, mem_ctx, group_sid, &num_names, 131 &sid_mem, &names, &name_types); 132 if (!NT_STATUS_IS_OK(status)) { 133 DEBUG(1, ("could not lookup membership for group rid %s in domain %s (error: %s)\n", 134 sid_to_string(sid_string, group_sid), domain->name, nt_errstr(status))); 135 136 goto done; 137 } 138 139 DEBUG(10, ("looked up %d names\n", num_names)); 140 141 if (DEBUGLEVEL >= 10) { 142 for (i = 0; i < num_names; i++) 143 DEBUG(10, ("\t%20s %s %d\n", names[i], sid_to_string(sid_string, sid_mem[i]), 144 name_types[i])); 145 } 146 147 /* Add members to list */ 148 149 buf = NULL; 150 buf_len = buf_ndx = 0; 151 152 again: 153 154 for (i = 0; i < num_names; i++) { 155 char *the_name; 156 fstring name; 157 int len; 158 159 the_name = names[i]; 160 161 DEBUG(10, ("processing name %s\n", the_name)); 162 163 /* FIXME: need to cope with groups within groups. These 164 occur in Universal groups on a Windows 2000 native mode 165 server. */ 166 167 /* make sure to allow machine accounts */ 168 169 if (name_types[i] != SID_NAME_USER && name_types[i] != SID_NAME_COMPUTER) { 170 DEBUG(3, ("name %s isn't a domain user\n", the_name)); 171 continue; 172 } 173 174 /* Append domain name */ 175 176 fill_domain_username(name, domain->name, the_name); 177 178 len = strlen(name); 179 180 /* Add to list or calculate buffer length */ 181 182 if (!buf) { 183 buf_len += len + 1; /* List is comma separated */ 184 (*num_gr_mem)++; 185 DEBUG(10, ("buf_len + %d = %d\n", len + 1, buf_len)); 186 } else { 187 DEBUG(10, ("appending %s at ndx %d\n", name, len)); 188 safe_strcpy(&buf[buf_ndx], name, len); 189 buf_ndx += len; 190 buf[buf_ndx] = ','; 191 buf_ndx++; 192 } 193 } 194 195 /* Allocate buffer */ 196 197 if (!buf && buf_len != 0) { 198 if (!(buf = SMB_MALLOC(buf_len))) { 199 DEBUG(1, ("out of memory\n")); 200 result = False; 201 goto done; 202 } 203 memset(buf, 0, buf_len); 204 goto again; 205 } 206 207 if (buf && buf_ndx > 0) { 208 buf[buf_ndx - 1] = '\0'; 209 } 210 211 *gr_mem = buf; 212 *gr_mem_len = buf_len; 213 214 DEBUG(10, ("num_mem = %d, len = %d, mem = %s\n", *num_gr_mem, 215 buf_len, *num_gr_mem ? buf : "NULL")); 216 result = True; 217 218done: 219 220 talloc_destroy(mem_ctx); 221 222 DEBUG(10, ("fill_grent_mem returning %d\n", result)); 223 224 return result; 225} 226 227/* Return a group structure from a group name */ 228 229enum winbindd_result winbindd_getgrnam(struct winbindd_cli_state *state) 230{ 231 DOM_SID group_sid; 232 WINBINDD_GR *grp; 233 struct winbindd_domain *domain; 234 enum SID_NAME_USE name_type; 235 fstring name_domain, name_group; 236 char *tmp, *gr_mem; 237 int gr_mem_len; 238 gid_t gid; 239 240 /* Ensure null termination */ 241 state->request.data.groupname[sizeof(state->request.data.groupname)-1]='\0'; 242 243 DEBUG(3, ("[%5lu]: getgrnam %s\n", (unsigned long)state->pid, 244 state->request.data.groupname)); 245 246 /* Parse domain and groupname */ 247 248 memset(name_group, 0, sizeof(fstring)); 249 250 tmp = state->request.data.groupname; 251 252 parse_domain_user(tmp, name_domain, name_group); 253 254 /* if no domain or our local domain, then do a local tdb search */ 255 256 if ( (!*name_domain || strequal(name_domain, get_global_sam_name())) && 257 ((grp = wb_getgrnam(name_group)) != NULL) ) { 258 259 char *buffer = NULL; 260 261 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) ); 262 263 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem ); 264 265 state->response.data.gr.gr_mem_ofs = 0; 266 state->response.length += gr_mem_len; 267 state->response.extra_data = buffer; /* give the memory away */ 268 269 return WINBINDD_OK; 270 } 271 272 /* if no domain or our local domain and no local tdb group, default to 273 * our local domain for aliases */ 274 275 if ( !*name_domain || strequal(name_domain, get_global_sam_name()) ) { 276 fstrcpy(name_domain, get_global_sam_name()); 277 } 278 279 /* Get info for the domain */ 280 281 if ((domain = find_domain_from_name(name_domain)) == NULL) { 282 DEBUG(3, ("could not get domain sid for domain %s\n", 283 name_domain)); 284 return WINBINDD_ERROR; 285 } 286 /* should we deal with users for our domain? */ 287 288 if ( lp_winbind_trusted_domains_only() && domain->primary) { 289 DEBUG(7,("winbindd_getgrnam: My domain -- rejecting getgrnam() for %s\\%s.\n", 290 name_domain, name_group)); 291 return WINBINDD_ERROR; 292 } 293 294 /* Get rid and name type from name */ 295 296 if (!winbindd_lookup_sid_by_name(domain, domain->name, name_group, &group_sid, 297 &name_type)) { 298 DEBUG(1, ("group %s in domain %s does not exist\n", 299 name_group, name_domain)); 300 return WINBINDD_ERROR; 301 } 302 303 if ( !((name_type==SID_NAME_DOM_GRP) || 304 ((name_type==SID_NAME_ALIAS) && domain->primary) || 305 ((name_type==SID_NAME_ALIAS) && domain->internal)) ) 306 { 307 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 308 name_group, name_type)); 309 return WINBINDD_ERROR; 310 } 311 312 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &gid, 0))) { 313 DEBUG(1, ("error converting unix gid to sid\n")); 314 return WINBINDD_ERROR; 315 } 316 317 if (!fill_grent(&state->response.data.gr, name_domain, 318 name_group, gid) || 319 !fill_grent_mem(domain, &group_sid, name_type, 320 &state->response.data.gr.num_gr_mem, 321 &gr_mem, &gr_mem_len)) { 322 return WINBINDD_ERROR; 323 } 324 325 /* Group membership lives at start of extra data */ 326 327 state->response.data.gr.gr_mem_ofs = 0; 328 329 state->response.length += gr_mem_len; 330 state->response.extra_data = gr_mem; 331 332 return WINBINDD_OK; 333} 334 335/* Return a group structure from a gid number */ 336 337enum winbindd_result winbindd_getgrgid(struct winbindd_cli_state *state) 338{ 339 struct winbindd_domain *domain; 340 WINBINDD_GR *grp; 341 DOM_SID group_sid; 342 enum SID_NAME_USE name_type; 343 fstring dom_name; 344 fstring group_name; 345 int gr_mem_len; 346 char *gr_mem; 347 348 DEBUG(3, ("[%5lu]: getgrgid %lu\n", (unsigned long)state->pid, 349 (unsigned long)state->request.data.gid)); 350 351 /* Bug out if the gid isn't in the winbind range */ 352 353 if ((state->request.data.gid < server_state.gid_low) || 354 (state->request.data.gid > server_state.gid_high)) 355 return WINBINDD_ERROR; 356 357 /* alway try local tdb lookup first */ 358 if ( ( grp=wb_getgrgid(state->request.data.gid)) != NULL ) { 359 char *buffer = NULL; 360 361 memcpy( &state->response.data.gr, grp, sizeof(WINBINDD_GR) ); 362 363 gr_mem_len = gr_mem_buffer( &buffer, grp->gr_mem, grp->num_gr_mem ); 364 365 state->response.data.gr.gr_mem_ofs = 0; 366 state->response.length += gr_mem_len; 367 state->response.extra_data = buffer; /* give away the memory */ 368 369 return WINBINDD_OK; 370 } 371 372 /* Get rid from gid */ 373 if (!NT_STATUS_IS_OK(idmap_gid_to_sid(&group_sid, state->request.data.gid))) { 374 DEBUG(1, ("could not convert gid %lu to rid\n", 375 (unsigned long)state->request.data.gid)); 376 return WINBINDD_ERROR; 377 } 378 379 /* Get name from sid */ 380 381 if (!winbindd_lookup_name_by_sid(&group_sid, dom_name, group_name, &name_type)) { 382 DEBUG(1, ("could not lookup sid\n")); 383 return WINBINDD_ERROR; 384 } 385 386 /* Fill in group structure */ 387 388 domain = find_domain_from_sid(&group_sid); 389 390 if (!domain) { 391 DEBUG(1,("Can't find domain from sid\n")); 392 return WINBINDD_ERROR; 393 } 394 395 if ( !((name_type==SID_NAME_DOM_GRP) || 396 ((name_type==SID_NAME_ALIAS) && domain->primary) || 397 ((name_type==SID_NAME_ALIAS) && domain->internal)) ) 398 { 399 DEBUG(1, ("name '%s' is not a local or domain group: %d\n", 400 group_name, name_type)); 401 return WINBINDD_ERROR; 402 } 403 404 if (!fill_grent(&state->response.data.gr, dom_name, group_name, 405 state->request.data.gid) || 406 !fill_grent_mem(domain, &group_sid, name_type, 407 &state->response.data.gr.num_gr_mem, 408 &gr_mem, &gr_mem_len)) 409 return WINBINDD_ERROR; 410 411 /* Group membership lives at start of extra data */ 412 413 state->response.data.gr.gr_mem_ofs = 0; 414 415 state->response.length += gr_mem_len; 416 state->response.extra_data = gr_mem; 417 418 return WINBINDD_OK; 419} 420 421/* 422 * set/get/endgrent functions 423 */ 424 425/* "Rewind" file pointer for group database enumeration */ 426 427enum winbindd_result winbindd_setgrent(struct winbindd_cli_state *state) 428{ 429 struct winbindd_domain *domain; 430 431 DEBUG(3, ("[%5lu]: setgrent\n", (unsigned long)state->pid)); 432 433 /* Check user has enabled this */ 434 435 if (!lp_winbind_enum_groups()) 436 return WINBINDD_ERROR; 437 438 /* Free old static data if it exists */ 439 440 if (state->getgrent_state != NULL) { 441 free_getent_state(state->getgrent_state); 442 state->getgrent_state = NULL; 443 } 444 445 /* Create sam pipes for each domain we know about */ 446 447 for (domain = domain_list(); domain != NULL; domain = domain->next) { 448 struct getent_state *domain_state; 449 450 /* Create a state record for this domain */ 451 452 /* don't add our domaina if we are a PDC or if we 453 are a member of a Samba domain */ 454 455 if ( lp_winbind_trusted_domains_only() && domain->primary ) 456 { 457 continue; 458 } 459 460 461 if ((domain_state = SMB_MALLOC_P(struct getent_state)) == NULL) { 462 DEBUG(1, ("winbindd_setgrent: malloc failed for domain_state!\n")); 463 return WINBINDD_ERROR; 464 } 465 466 ZERO_STRUCTP(domain_state); 467 468 fstrcpy(domain_state->domain_name, domain->name); 469 470 /* Add to list of open domains */ 471 472 DLIST_ADD(state->getgrent_state, domain_state); 473 } 474 475 state->getgrent_initialized = True; 476 477 return WINBINDD_OK; 478} 479 480/* Close file pointer to ntdom group database */ 481 482enum winbindd_result winbindd_endgrent(struct winbindd_cli_state *state) 483{ 484 DEBUG(3, ("[%5lu]: endgrent\n", (unsigned long)state->pid)); 485 486 free_getent_state(state->getgrent_state); 487 state->getgrent_initialized = False; 488 state->getgrent_state = NULL; 489 490 return WINBINDD_OK; 491} 492 493/* Get the list of domain groups and domain aliases for a domain. We fill in 494 the sam_entries and num_sam_entries fields with domain group information. 495 The dispinfo_ndx field is incremented to the index of the next group to 496 fetch. Return True if some groups were returned, False otherwise. */ 497 498static BOOL get_sam_group_entries(struct getent_state *ent) 499{ 500 NTSTATUS status; 501 uint32 num_entries; 502 struct acct_info *name_list = NULL, *tmp_name_list = NULL; 503 TALLOC_CTX *mem_ctx; 504 BOOL result = False; 505 struct acct_info *sam_grp_entries = NULL; 506 struct winbindd_domain *domain; 507 508 if (ent->got_sam_entries) 509 return False; 510 511 if (!(mem_ctx = talloc_init("get_sam_group_entries(%s)", 512 ent->domain_name))) { 513 DEBUG(1, ("get_sam_group_entries: could not create talloc context!\n")); 514 return False; 515 } 516 517 /* Free any existing group info */ 518 519 SAFE_FREE(ent->sam_entries); 520 ent->num_sam_entries = 0; 521 ent->got_sam_entries = True; 522 523 /* Enumerate domain groups */ 524 525 num_entries = 0; 526 527 if (!(domain = find_domain_from_name(ent->domain_name))) { 528 DEBUG(3, ("no such domain %s in get_sam_group_entries\n", ent->domain_name)); 529 goto done; 530 } 531 532 /* always get the domain global groups */ 533 534 status = domain->methods->enum_dom_groups(domain, mem_ctx, &num_entries, &sam_grp_entries); 535 536 if (!NT_STATUS_IS_OK(status)) { 537 DEBUG(3, ("get_sam_group_entries: could not enumerate domain groups! Error: %s\n", nt_errstr(status))); 538 result = False; 539 goto done; 540 } 541 542 /* Copy entries into return buffer */ 543 544 if (num_entries) { 545 if ( !(name_list = SMB_MALLOC_ARRAY(struct acct_info, num_entries)) ) { 546 DEBUG(0,("get_sam_group_entries: Failed to malloc memory for %d domain groups!\n", 547 num_entries)); 548 result = False; 549 goto done; 550 } 551 memcpy( name_list, sam_grp_entries, num_entries * sizeof(struct acct_info) ); 552 } 553 554 ent->num_sam_entries = num_entries; 555 556 /* get the domain local groups if we are a member of a native win2k domain 557 and are not using LDAP to get the groups */ 558 559 if ( ( lp_security() != SEC_ADS && domain->native_mode 560 && domain->primary) || domain->internal ) 561 { 562 DEBUG(4,("get_sam_group_entries: Native Mode 2k domain; enumerating local groups as well\n")); 563 564 status = domain->methods->enum_local_groups(domain, mem_ctx, &num_entries, &sam_grp_entries); 565 566 if ( !NT_STATUS_IS_OK(status) ) { 567 DEBUG(3,("get_sam_group_entries: Failed to enumerate domain local groups!\n")); 568 num_entries = 0; 569 } 570 else 571 DEBUG(4,("get_sam_group_entries: Returned %d local groups\n", num_entries)); 572 573 /* Copy entries into return buffer */ 574 575 if ( num_entries ) { 576 if ( !(tmp_name_list = SMB_REALLOC_ARRAY( name_list, struct acct_info, ent->num_sam_entries+num_entries)) ) 577 { 578 DEBUG(0,("get_sam_group_entries: Failed to realloc more memory for %d local groups!\n", 579 num_entries)); 580 result = False; 581 SAFE_FREE( name_list ); 582 goto done; 583 } 584 585 name_list = tmp_name_list; 586 587 memcpy( &name_list[ent->num_sam_entries], sam_grp_entries, 588 num_entries * sizeof(struct acct_info) ); 589 } 590 591 ent->num_sam_entries += num_entries; 592 } 593 594 595 /* Fill in remaining fields */ 596 597 ent->sam_entries = name_list; 598 ent->sam_entry_index = 0; 599 600 result = (ent->num_sam_entries > 0); 601 602 done: 603 talloc_destroy(mem_ctx); 604 605 return result; 606} 607 608/* Fetch next group entry from ntdom database */ 609 610#define MAX_GETGRENT_GROUPS 500 611 612enum winbindd_result winbindd_getgrent(struct winbindd_cli_state *state) 613{ 614 struct getent_state *ent; 615 struct winbindd_gr *group_list = NULL; 616 int num_groups, group_list_ndx = 0, i, gr_mem_list_len = 0; 617 char *new_extra_data, *gr_mem_list = NULL; 618 619 DEBUG(3, ("[%5lu]: getgrent\n", (unsigned long)state->pid)); 620 621 /* Check user has enabled this */ 622 623 if (!lp_winbind_enum_groups()) 624 return WINBINDD_ERROR; 625 626 num_groups = MIN(MAX_GETGRENT_GROUPS, state->request.data.num_entries); 627 628 if ((state->response.extra_data = SMB_MALLOC_ARRAY(struct winbindd_gr, num_groups)) == NULL) 629 return WINBINDD_ERROR; 630 631 memset(state->response.extra_data, '\0', 632 num_groups * sizeof(struct winbindd_gr) ); 633 634 state->response.data.num_entries = 0; 635 636 group_list = (struct winbindd_gr *)state->response.extra_data; 637 638 if (!state->getgrent_initialized) 639 winbindd_setgrent(state); 640 641 if (!(ent = state->getgrent_state)) 642 return WINBINDD_ERROR; 643 644 /* Start sending back groups */ 645 646 for (i = 0; i < num_groups; i++) { 647 struct acct_info *name_list = NULL; 648 fstring domain_group_name; 649 uint32 result; 650 gid_t group_gid; 651 int gr_mem_len; 652 char *gr_mem, *new_gr_mem_list; 653 DOM_SID group_sid; 654 struct winbindd_domain *domain; 655 656 /* Do we need to fetch another chunk of groups? */ 657 658 tryagain: 659 660 DEBUG(10, ("entry_index = %d, num_entries = %d\n", 661 ent->sam_entry_index, ent->num_sam_entries)); 662 663 if (ent->num_sam_entries == ent->sam_entry_index) { 664 665 while(ent && !get_sam_group_entries(ent)) { 666 struct getent_state *next_ent; 667 668 DEBUG(10, ("freeing state info for domain %s\n", ent->domain_name)); 669 670 /* Free state information for this domain */ 671 672 SAFE_FREE(ent->sam_entries); 673 674 next_ent = ent->next; 675 DLIST_REMOVE(state->getgrent_state, ent); 676 677 SAFE_FREE(ent); 678 ent = next_ent; 679 } 680 681 /* No more domains */ 682 683 if (!ent) 684 break; 685 } 686 687 name_list = ent->sam_entries; 688 689 if (!(domain = 690 find_domain_from_name(ent->domain_name))) { 691 DEBUG(3, ("No such domain %s in winbindd_getgrent\n", ent->domain_name)); 692 result = False; 693 goto done; 694 } 695 696 /* Lookup group info */ 697 698 sid_copy(&group_sid, &domain->sid); 699 sid_append_rid(&group_sid, name_list[ent->sam_entry_index].rid); 700 701 if (!NT_STATUS_IS_OK(idmap_sid_to_gid(&group_sid, &group_gid, 0))) { 702 703 DEBUG(1, ("could not look up gid for group %s\n", 704 name_list[ent->sam_entry_index].acct_name)); 705 706 ent->sam_entry_index++; 707 goto tryagain; 708 } 709 710 DEBUG(10, ("got gid %lu for group %lu\n", (unsigned long)group_gid, 711 (unsigned long)name_list[ent->sam_entry_index].rid)); 712 713 /* Fill in group entry */ 714 715 fill_domain_username(domain_group_name, ent->domain_name, 716 name_list[ent->sam_entry_index].acct_name); 717 718 result = fill_grent(&group_list[group_list_ndx], 719 ent->domain_name, 720 name_list[ent->sam_entry_index].acct_name, 721 group_gid); 722 723 /* Fill in group membership entry */ 724 725 if (result) { 726 DOM_SID member_sid; 727 group_list[group_list_ndx].num_gr_mem = 0; 728 gr_mem = NULL; 729 gr_mem_len = 0; 730 731 /* Get group membership */ 732 if (state->request.cmd == WINBINDD_GETGRLST) { 733 result = True; 734 } else { 735 sid_copy(&member_sid, &domain->sid); 736 sid_append_rid(&member_sid, name_list[ent->sam_entry_index].rid); 737 result = fill_grent_mem( 738 domain, 739 &member_sid, 740 SID_NAME_DOM_GRP, 741 &group_list[group_list_ndx].num_gr_mem, 742 &gr_mem, &gr_mem_len); 743 } 744 } 745 746 if (result) { 747 /* Append to group membership list */ 748 new_gr_mem_list = SMB_REALLOC( gr_mem_list, gr_mem_list_len + gr_mem_len); 749 750 if (!new_gr_mem_list && (group_list[group_list_ndx].num_gr_mem != 0)) { 751 DEBUG(0, ("out of memory\n")); 752 SAFE_FREE(gr_mem_list); 753 gr_mem_list_len = 0; 754 break; 755 } 756 757 DEBUG(10, ("list_len = %d, mem_len = %d\n", 758 gr_mem_list_len, gr_mem_len)); 759 760 gr_mem_list = new_gr_mem_list; 761 762 memcpy(&gr_mem_list[gr_mem_list_len], gr_mem, 763 gr_mem_len); 764 765 SAFE_FREE(gr_mem); 766 767 group_list[group_list_ndx].gr_mem_ofs = 768 gr_mem_list_len; 769 770 gr_mem_list_len += gr_mem_len; 771 } 772 773 ent->sam_entry_index++; 774 775 /* Add group to return list */ 776 777 if (result) { 778 779 DEBUG(10, ("adding group num_entries = %d\n", 780 state->response.data.num_entries)); 781 782 group_list_ndx++; 783 state->response.data.num_entries++; 784 785 state->response.length += 786 sizeof(struct winbindd_gr); 787 788 } else { 789 DEBUG(0, ("could not lookup domain group %s\n", 790 domain_group_name)); 791 } 792 } 793 794 /* Copy the list of group memberships to the end of the extra data */ 795 796 if (group_list_ndx == 0) 797 goto done; 798 799 new_extra_data = SMB_REALLOC( 800 state->response.extra_data, 801 group_list_ndx * sizeof(struct winbindd_gr) + gr_mem_list_len); 802 803 if (!new_extra_data) { 804 DEBUG(0, ("out of memory\n")); 805 group_list_ndx = 0; 806 SAFE_FREE(state->response.extra_data); 807 SAFE_FREE(gr_mem_list); 808 809 return WINBINDD_ERROR; 810 } 811 812 state->response.extra_data = new_extra_data; 813 814 memcpy(&((char *)state->response.extra_data) 815 [group_list_ndx * sizeof(struct winbindd_gr)], 816 gr_mem_list, gr_mem_list_len); 817 818 SAFE_FREE(gr_mem_list); 819 820 state->response.length += gr_mem_list_len; 821 822 DEBUG(10, ("returning %d groups, length = %d\n", 823 group_list_ndx, gr_mem_list_len)); 824 825 /* Out of domains */ 826 827 done: 828 829 return (group_list_ndx > 0) ? WINBINDD_OK : WINBINDD_ERROR; 830} 831 832/* List domain groups without mapping to unix ids */ 833 834enum winbindd_result winbindd_list_groups(struct winbindd_cli_state *state) 835{ 836 uint32 total_entries = 0; 837 struct winbindd_domain *domain; 838 const char *which_domain; 839 char *extra_data = NULL; 840 char *ted = NULL; 841 unsigned int extra_data_len = 0, i; 842 843 DEBUG(3, ("[%5lu]: list groups\n", (unsigned long)state->pid)); 844 845 /* Ensure null termination */ 846 state->request.domain_name[sizeof(state->request.domain_name)-1]='\0'; 847 which_domain = state->request.domain_name; 848 849 /* Enumerate over trusted domains */ 850 851 for (domain = domain_list(); domain; domain = domain->next) { 852 struct getent_state groups; 853 854 /* if we have a domain name restricting the request and this 855 one in the list doesn't match, then just bypass the remainder 856 of the loop */ 857 858 if ( *which_domain && !strequal(which_domain, domain->name) ) 859 continue; 860 861 ZERO_STRUCT(groups); 862 863 /* Get list of sam groups */ 864 865 fstrcpy(groups.domain_name, domain->name); 866 867 get_sam_group_entries(&groups); 868 869 if (groups.num_sam_entries == 0) { 870 /* this domain is empty or in an error state */ 871 continue; 872 } 873 874 /* keep track the of the total number of groups seen so 875 far over all domains */ 876 total_entries += groups.num_sam_entries; 877 878 /* Allocate some memory for extra data. Note that we limit 879 account names to sizeof(fstring) = 128 characters. */ 880 ted = SMB_REALLOC(extra_data, sizeof(fstring) * total_entries); 881 882 if (!ted) { 883 DEBUG(0,("failed to enlarge buffer!\n")); 884 SAFE_FREE(extra_data); 885 return WINBINDD_ERROR; 886 } else 887 extra_data = ted; 888 889 /* Pack group list into extra data fields */ 890 for (i = 0; i < groups.num_sam_entries; i++) { 891 char *group_name = ((struct acct_info *) 892 groups.sam_entries)[i].acct_name; 893 fstring name; 894 895 fill_domain_username(name, domain->name, group_name); 896 /* Append to extra data */ 897 memcpy(&extra_data[extra_data_len], name, 898 strlen(name)); 899 extra_data_len += strlen(name); 900 extra_data[extra_data_len++] = ','; 901 } 902 903 SAFE_FREE(groups.sam_entries); 904 } 905 906 /* Assign extra_data fields in response structure */ 907 if (extra_data) { 908 extra_data[extra_data_len - 1] = '\0'; 909 state->response.extra_data = extra_data; 910 state->response.length += extra_data_len; 911 } 912 913 /* No domains may have responded but that's still OK so don't 914 return an error. */ 915 916 return WINBINDD_OK; 917} 918 919static void add_local_gids_from_sid(DOM_SID *sid, gid_t **gids, int *num) 920{ 921 gid_t gid; 922 DOM_SID *aliases; 923 int j, num_aliases; 924 925 DEBUG(10, ("Adding local gids from SID: %s\n", 926 sid_string_static(sid))); 927 928 /* Don't expand aliases if not explicitly activated -- for now 929 -- jerry */ 930 931 if (!lp_winbind_nested_groups()) 932 return; 933 934 /* Add nested group memberships */ 935 936 if (!pdb_enum_alias_memberships(sid, 1, &aliases, &num_aliases)) 937 return; 938 939 for (j=0; j<num_aliases; j++) { 940 enum SID_NAME_USE type; 941 942 if (!local_sid_to_gid(&gid, &aliases[j], &type)) { 943 DEBUG(1, ("Got an alias membership with no alias\n")); 944 continue; 945 } 946 947 if ((type != SID_NAME_ALIAS) && (type != SID_NAME_WKN_GRP)) { 948 DEBUG(1, ("Got an alias membership in a non-alias\n")); 949 continue; 950 } 951 952 add_gid_to_array_unique(gid, gids, num); 953 } 954 SAFE_FREE(aliases); 955} 956 957static void add_gids_from_user_sid(DOM_SID *sid, gid_t **gids, int *num) 958{ 959 DEBUG(10, ("Adding gids from user SID: %s\n", 960 sid_string_static(sid))); 961 962 add_local_gids_from_sid(sid, gids, num); 963} 964 965static void add_gids_from_group_sid(DOM_SID *sid, gid_t **gids, int *num) 966{ 967 gid_t gid; 968 969 DEBUG(10, ("Adding gids from group SID: %s\n", 970 sid_string_static(sid))); 971 972 if (NT_STATUS_IS_OK(idmap_sid_to_gid(sid, &gid, 0))) 973 add_gid_to_array_unique(gid, gids, num); 974 975 add_local_gids_from_sid(sid, gids, num); 976} 977 978/* Get user supplementary groups. This is much quicker than trying to 979 invert the groups database. We merge the groups from the gids and 980 other_sids info3 fields as trusted domain, universal group 981 memberships, and nested groups (win2k native mode only) are not 982 returned by the getgroups RPC call but are present in the info3. */ 983 984enum winbindd_result winbindd_getgroups(struct winbindd_cli_state *state) 985{ 986 fstring name_domain, name_user; 987 DOM_SID user_sid, group_sid; 988 enum SID_NAME_USE name_type; 989 uint32 num_groups = 0; 990 uint32 num_gids = 0; 991 NTSTATUS status; 992 DOM_SID **user_grpsids; 993 struct winbindd_domain *domain; 994 enum winbindd_result result = WINBINDD_ERROR; 995 gid_t *gid_list = NULL; 996 unsigned int i; 997 TALLOC_CTX *mem_ctx; 998 NET_USER_INFO_3 *info3 = NULL; 999 1000 /* Ensure null termination */ 1001 state->request.data.username[sizeof(state->request.data.username)-1]='\0'; 1002 1003 DEBUG(3, ("[%5lu]: getgroups %s\n", (unsigned long)state->pid, 1004 state->request.data.username)); 1005 1006 if (!(mem_ctx = talloc_init("winbindd_getgroups(%s)", 1007 state->request.data.username))) 1008 return WINBINDD_ERROR; 1009 1010 /* Parse domain and username */ 1011 1012 parse_domain_user(state->request.data.username, 1013 name_domain, name_user); 1014 1015 /* Get info for the domain */ 1016 1017 if ((domain = find_domain_from_name(name_domain)) == NULL) { 1018 DEBUG(7, ("could not find domain entry for domain %s\n", 1019 name_domain)); 1020 goto done; 1021 } 1022 1023 if ( domain->primary && lp_winbind_trusted_domains_only()) { 1024 DEBUG(7,("winbindd_getpwnam: My domain -- rejecting getgroups() for %s\\%s.\n", 1025 name_domain, name_user)); 1026 return WINBINDD_ERROR; 1027 } 1028 1029 /* Get rid and name type from name. The following costs 1 packet */ 1030 1031 if (!winbindd_lookup_sid_by_name(domain, domain->name, name_user, &user_sid, 1032 &name_type)) { 1033 DEBUG(4, ("user '%s' does not exist\n", name_user)); 1034 goto done; 1035 } 1036 1037 if (name_type != SID_NAME_USER && name_type != SID_NAME_COMPUTER) { 1038 DEBUG(1, ("name '%s' is not a user name: %d\n", 1039 name_user, name_type)); 1040 goto done; 1041 } 1042 1043 add_gids_from_user_sid(&user_sid, &gid_list, &num_gids); 1044 1045 /* Treat the info3 cache as authoritative as the 1046 lookup_usergroups() function may return cached data. */ 1047 1048 if ( !opt_nocache && (info3 = netsamlogon_cache_get(mem_ctx, &user_sid))) { 1049 1050 DEBUG(10, ("winbindd_getgroups: info3 has %d groups, %d other sids\n", 1051 info3->num_groups2, info3->num_other_sids)); 1052 1053 num_groups = info3->num_other_sids + info3->num_groups2; 1054 1055 /* Go through each other sid and convert it to a gid */ 1056 1057 for (i = 0; i < info3->num_other_sids; i++) { 1058 fstring name; 1059 fstring dom_name; 1060 enum SID_NAME_USE sid_type; 1061 1062 /* Is this sid known to us? It can either be 1063 a trusted domain sid or a foreign sid. */ 1064 1065 if (!winbindd_lookup_name_by_sid( &info3->other_sids[i].sid, 1066 dom_name, name, &sid_type)) 1067 { 1068 DEBUG(10, ("winbindd_getgroups: could not lookup name for %s\n", 1069 sid_string_static(&info3->other_sids[i].sid))); 1070 continue; 1071 } 1072 1073 /* Check it is a domain group or an alias (domain local group) 1074 in a win2k native mode domain. */ 1075 1076 if ( !((sid_type==SID_NAME_DOM_GRP) || 1077 ((sid_type==SID_NAME_ALIAS) && domain->primary)) ) 1078 { 1079 DEBUG(10, ("winbindd_getgroups: sid type %d " 1080 "for %s is not a domain group\n", 1081 sid_type, 1082 sid_string_static( 1083 &info3->other_sids[i].sid))); 1084 continue; 1085 } 1086 1087 add_gids_from_group_sid(&info3->other_sids[i].sid, 1088 &gid_list, &num_gids); 1089 } 1090 1091 for (i = 0; i < info3->num_groups2; i++) { 1092 1093 /* create the group SID */ 1094 1095 sid_copy( &group_sid, &domain->sid ); 1096 sid_append_rid( &group_sid, info3->gids[i].g_rid ); 1097 1098 add_gids_from_group_sid(&group_sid, &gid_list, 1099 &num_gids); 1100 } 1101 1102 SAFE_FREE(info3); 1103 1104 } else { 1105 status = domain->methods->lookup_usergroups(domain, mem_ctx, 1106 &user_sid, &num_groups, 1107 &user_grpsids); 1108 if (!NT_STATUS_IS_OK(status)) 1109 goto done; 1110 1111 if (state->response.extra_data) 1112 goto done; 1113 1114 for (i = 0; i < num_groups; i++) { 1115 add_gids_from_group_sid(user_grpsids[i], 1116 &gid_list, &num_gids); 1117 } 1118 } 1119 1120 /* We want at least one group... */ 1121 if (gid_list == NULL) 1122 goto done; 1123 1124 remove_duplicate_gids( &num_gids, gid_list ); 1125 1126 /* Send data back to client */ 1127 1128 state->response.data.num_entries = num_gids; 1129 state->response.extra_data = gid_list; 1130 state->response.length += num_gids * sizeof(gid_t); 1131 1132 result = WINBINDD_OK; 1133 1134 done: 1135 1136 talloc_destroy(mem_ctx); 1137 1138 return result; 1139} 1140 1141static void add_sid_to_parray_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid, 1142 DOM_SID ***sids, int *num_sids) 1143{ 1144 int i; 1145 1146 for (i=0; i<(*num_sids); i++) { 1147 if (sid_compare(sid, (*sids)[i]) == 0) 1148 return; 1149 } 1150 1151 *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID *, *num_sids+1); 1152 1153 if (*sids == NULL) 1154 return; 1155 1156 (*sids)[*num_sids] = TALLOC_P(mem_ctx, DOM_SID); 1157 sid_copy((*sids)[*num_sids], sid); 1158 *num_sids += 1; 1159 return; 1160} 1161 1162static void add_local_sids_from_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, 1163 DOM_SID ***user_grpsids, 1164 int *num_groups) 1165{ 1166 DOM_SID *aliases = NULL; 1167 int i, num_aliases = 0; 1168 1169 if (!pdb_enum_alias_memberships(sid, 1, &aliases, &num_aliases)) 1170 return; 1171 1172 if (num_aliases == 0) 1173 return; 1174 1175 for (i=0; i<num_aliases; i++) 1176 add_sid_to_parray_unique(mem_ctx, &aliases[i], user_grpsids, 1177 num_groups); 1178 1179 SAFE_FREE(aliases); 1180 1181 return; 1182} 1183 1184/* Get user supplementary sids. This is equivalent to the 1185 winbindd_getgroups() function but it involves a SID->SIDs mapping 1186 rather than a NAME->SID->SIDS->GIDS mapping, which means we avoid 1187 idmap. This call is designed to be used with applications that need 1188 to do ACL evaluation themselves. Note that the cached info3 data is 1189 not used 1190 1191 this function assumes that the SID that comes in is a user SID. If 1192 you pass in another type of SID then you may get unpredictable 1193 results. 1194*/ 1195enum winbindd_result winbindd_getusersids(struct winbindd_cli_state *state) 1196{ 1197 DOM_SID user_sid; 1198 NTSTATUS status; 1199 DOM_SID **user_grpsids; 1200 struct winbindd_domain *domain; 1201 enum winbindd_result result = WINBINDD_ERROR; 1202 unsigned int i; 1203 TALLOC_CTX *mem_ctx; 1204 char *ret = NULL; 1205 uint32 num_groups; 1206 unsigned ofs, ret_size = 0; 1207 1208 /* Ensure null termination */ 1209 state->request.data.sid[sizeof(state->request.data.sid)-1]='\0'; 1210 1211 if (!string_to_sid(&user_sid, state->request.data.sid)) { 1212 DEBUG(1, ("Could not get convert sid %s from string\n", state->request.data.sid)); 1213 return WINBINDD_ERROR; 1214 } 1215 1216 if (!(mem_ctx = talloc_init("winbindd_getusersids(%s)", 1217 state->request.data.username))) { 1218 return WINBINDD_ERROR; 1219 } 1220 1221 /* Get info for the domain */ 1222 if ((domain = find_domain_from_sid(&user_sid)) == NULL) { 1223 DEBUG(0,("could not find domain entry for sid %s\n", 1224 sid_string_static(&user_sid))); 1225 goto done; 1226 } 1227 1228 status = domain->methods->lookup_usergroups(domain, mem_ctx, 1229 &user_sid, &num_groups, 1230 &user_grpsids); 1231 if (!NT_STATUS_IS_OK(status)) 1232 goto done; 1233 1234 if (num_groups == 0) { 1235 goto no_groups; 1236 } 1237 1238 domain = find_our_domain(); 1239 1240 if (domain == NULL) { 1241 DEBUG(0, ("Could not find our domain\n")); 1242 goto done; 1243 } 1244 1245 /* Note that I do not check for AD or its mode. XP in a real NT4 1246 * domain also asks for this info. -- vl */ 1247 1248 if (!IS_DC) { 1249 uint32 *alias_rids = NULL; 1250 int num_aliases; 1251 1252 /* We need to include the user SID to expand */ 1253 user_grpsids = TALLOC_REALLOC_ARRAY(mem_ctx, user_grpsids, 1254 DOM_SID *, num_groups+1); 1255 user_grpsids[num_groups] = &user_sid; 1256 1257 status = domain->methods->lookup_useraliases(domain, mem_ctx, 1258 num_groups, 1259 user_grpsids+1, 1260 &num_aliases, 1261 &alias_rids); 1262 1263 if (!NT_STATUS_IS_OK(status)) { 1264 DEBUG(3, ("Could not expand alias sids: %s\n", 1265 nt_errstr(status))); 1266 goto done; 1267 } 1268 1269 for (i=0; i<num_aliases; i++) { 1270 DOM_SID sid; 1271 sid_copy(&sid, &domain->sid); 1272 sid_append_rid(&sid, alias_rids[i]); 1273 add_sid_to_parray_unique(mem_ctx, &sid, &user_grpsids, 1274 &num_groups); 1275 } 1276 } 1277 1278 if (lp_winbind_nested_groups()) { 1279 int k; 1280 /* num_groups is changed during the loop, that's why we have 1281 to count down here.*/ 1282 1283 for (k=num_groups-1; k>=0; k--) { 1284 add_local_sids_from_sid(mem_ctx, user_grpsids[k], 1285 &user_grpsids, &num_groups); 1286 } 1287 1288 add_local_sids_from_sid(mem_ctx, &user_sid, &user_grpsids, 1289 &num_groups); 1290 } 1291 1292 /* work out the response size */ 1293 for (i = 0; i < num_groups; i++) { 1294 const char *s = sid_string_static(user_grpsids[i]); 1295 ret_size += strlen(s) + 1; 1296 } 1297 1298 /* build the reply */ 1299 ret = SMB_MALLOC(ret_size); 1300 if (!ret) goto done; 1301 ofs = 0; 1302 for (i = 0; i < num_groups; i++) { 1303 const char *s = sid_string_static(user_grpsids[i]); 1304 safe_strcpy(ret + ofs, s, ret_size - ofs - 1); 1305 ofs += strlen(ret+ofs) + 1; 1306 } 1307 1308no_groups: 1309 /* Send data back to client */ 1310 state->response.data.num_entries = num_groups; 1311 state->response.extra_data = ret; 1312 state->response.length += ret_size; 1313 result = WINBINDD_OK; 1314 1315 done: 1316 talloc_destroy(mem_ctx); 1317 1318 return result; 1319} 1320 1321