1/* 2 * Unix SMB/CIFS implementation. 3 * Group Policy Support 4 * Copyright (C) Guenther Deschner 2007-2008 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20#include "includes.h" 21#include "../libgpo/gpext/gpext.h" 22#include "librpc/gen_ndr/ndr_misc.h" 23#include "lib/util/dlinklist.h" 24 25static struct gp_extension *extensions = NULL; 26 27/**************************************************************** 28****************************************************************/ 29 30struct gp_extension *get_gp_extension_list(void) 31{ 32 return extensions; 33} 34 35/**************************************************************** 36****************************************************************/ 37 38/* see http://support.microsoft.com/kb/216358/en-us/ for more info */ 39 40struct gp_extension_reg_table gpext_reg_vals[] = { 41 { "DllName", REG_EXPAND_SZ }, 42 { "ProcessGroupPolicy", REG_SZ }, 43 { "NoMachinePolicy", REG_DWORD }, 44 { "NoUserPolicy", REG_DWORD }, 45 { "NoSlowLink", REG_DWORD }, 46 { "NoBackgroundPolicy", REG_DWORD }, 47 { "NoGPOListChanges", REG_DWORD }, 48 { "PerUserLocalSettings", REG_DWORD }, 49 { "RequiresSuccessfulRegistry", REG_DWORD }, 50 { "EnableAsynchronousProcessing", REG_DWORD }, 51 { "ExtensionDebugLevel", REG_DWORD }, 52 /* new */ 53 { "GenerateGroupPolicy", REG_SZ }, /* not supported on w2k */ 54 { "NotifyLinkTransition", REG_DWORD }, 55 { "ProcessGroupPolicyEx", REG_SZ }, /* not supported on w2k */ 56 { "ExtensionEventSource", REG_MULTI_SZ }, /* not supported on w2k */ 57 { "GenerateGroupPolicy", REG_SZ }, 58 { "MaxNoGPOListChangesInterval", REG_DWORD }, 59 { NULL, REG_NONE } 60}; 61 62/**************************************************************** 63****************************************************************/ 64 65static struct gp_extension *get_extension_by_name(struct gp_extension *be, 66 const char *name) 67{ 68 struct gp_extension *b; 69 70 for (b = be; b; b = b->next) { 71 if (strequal(b->name, name)) { 72 return b; 73 } 74 } 75 76 return NULL; 77} 78 79/**************************************************************** 80****************************************************************/ 81 82static struct gp_extension_methods *get_methods_by_name(struct gp_extension *be, 83 const char *name) 84{ 85 struct gp_extension *b; 86 87 for (b = be; b; b = b->next) { 88 if (strequal(b->name, name)) { 89 return b->methods; 90 } 91 } 92 93 return NULL; 94} 95 96/**************************************************************** 97****************************************************************/ 98 99NTSTATUS unregister_gp_extension(const char *name) 100{ 101 struct gp_extension *ext; 102 103 ext = get_extension_by_name(extensions, name); 104 if (!ext) { 105 return NT_STATUS_OK; 106 } 107 108 DLIST_REMOVE(extensions, ext); 109 talloc_free(ext); 110 111 DEBUG(2,("Successfully removed GP extension '%s'\n", name)); 112 113 return NT_STATUS_OK; 114} 115 116/**************************************************************** 117****************************************************************/ 118 119NTSTATUS register_gp_extension(TALLOC_CTX *gpext_ctx, 120 int version, 121 const char *name, 122 const char *guid, 123 struct gp_extension_methods *methods) 124{ 125 struct gp_extension_methods *test; 126 struct gp_extension *entry; 127 NTSTATUS status; 128 129 if (!gpext_ctx) { 130 return NT_STATUS_INTERNAL_DB_ERROR; 131 } 132 133 if ((version != SMB_GPEXT_INTERFACE_VERSION)) { 134 DEBUG(0,("Failed to register gp extension.\n" 135 "The module was compiled against " 136 "SMB_GPEXT_INTERFACE_VERSION %d,\n" 137 "current SMB_GPEXT_INTERFACE_VERSION is %d.\n" 138 "Please recompile against the current " 139 "version of samba!\n", 140 version, SMB_GPEXT_INTERFACE_VERSION)); 141 return NT_STATUS_OBJECT_TYPE_MISMATCH; 142 } 143 144 if (!guid || !name || !name[0] || !methods) { 145 DEBUG(0,("Called with NULL pointer or empty name!\n")); 146 return NT_STATUS_INVALID_PARAMETER; 147 } 148 149 test = get_methods_by_name(extensions, name); 150 if (test) { 151 DEBUG(0,("GP extension module %s already registered!\n", 152 name)); 153 return NT_STATUS_OBJECT_NAME_COLLISION; 154 } 155 156 entry = talloc_zero(gpext_ctx, struct gp_extension); 157 NT_STATUS_HAVE_NO_MEMORY(entry); 158 159 entry->name = talloc_strdup(gpext_ctx, name); 160 NT_STATUS_HAVE_NO_MEMORY(entry->name); 161 162 entry->guid = talloc_zero(gpext_ctx, struct GUID); 163 NT_STATUS_HAVE_NO_MEMORY(entry->guid); 164 status = GUID_from_string(guid, entry->guid); 165 NT_STATUS_NOT_OK_RETURN(status); 166 167 entry->methods = methods; 168 DLIST_ADD(extensions, entry); 169 170 DEBUG(2,("Successfully added GP extension '%s' %s\n", 171 name, GUID_string2(gpext_ctx, entry->guid))); 172 173 return NT_STATUS_OK; 174} 175 176/**************************************************************** 177****************************************************************/ 178 179static NTSTATUS gp_extension_init_module(TALLOC_CTX *mem_ctx, 180 const char *name, 181 struct gp_extension **gpext) 182{ 183 NTSTATUS status; 184 struct gp_extension *ext = NULL; 185 186 ext = talloc_zero(mem_ctx, struct gp_extension); 187 NT_STATUS_HAVE_NO_MEMORY(gpext); 188 189 ext->methods = get_methods_by_name(extensions, name); 190 if (!ext->methods) { 191 192 status = smb_probe_module(SAMBA_SUBSYSTEM_GPEXT, 193 name); 194 if (!NT_STATUS_IS_OK(status)) { 195 return status; 196 } 197 198 ext->methods = get_methods_by_name(extensions, name); 199 if (!ext->methods) { 200 return NT_STATUS_DLL_INIT_FAILED; 201 } 202 } 203 204 *gpext = ext; 205 206 return NT_STATUS_OK; 207} 208 209/**************************************************************** 210****************************************************************/ 211 212static bool add_gp_extension_reg_entry_to_array(TALLOC_CTX *mem_ctx, 213 struct gp_extension_reg_entry *entry, 214 struct gp_extension_reg_entry **entries, 215 size_t *num) 216{ 217 *entries = talloc_realloc(mem_ctx, *entries, 218 struct gp_extension_reg_entry, 219 (*num)+1); 220 if (*entries == NULL) { 221 *num = 0; 222 return false; 223 } 224 225 (*entries)[*num].value = entry->value; 226 (*entries)[*num].data = entry->data; 227 228 *num += 1; 229 return true; 230} 231 232/**************************************************************** 233****************************************************************/ 234 235static bool add_gp_extension_reg_info_entry_to_array(TALLOC_CTX *mem_ctx, 236 struct gp_extension_reg_info_entry *entry, 237 struct gp_extension_reg_info_entry **entries, 238 size_t *num) 239{ 240 *entries = talloc_realloc(mem_ctx, *entries, 241 struct gp_extension_reg_info_entry, 242 (*num)+1); 243 if (*entries == NULL) { 244 *num = 0; 245 return false; 246 } 247 248 (*entries)[*num].guid = entry->guid; 249 (*entries)[*num].num_entries = entry->num_entries; 250 (*entries)[*num].entries = entry->entries; 251 252 *num += 1; 253 return true; 254} 255 256/**************************************************************** 257****************************************************************/ 258 259static NTSTATUS gp_ext_info_add_reg(TALLOC_CTX *mem_ctx, 260 struct gp_extension_reg_info_entry *entry, 261 const char *value, 262 enum winreg_Type type, 263 const char *data_s) 264{ 265 struct gp_extension_reg_entry *reg_entry = NULL; 266 struct registry_value *data = NULL; 267 268 reg_entry = talloc_zero(mem_ctx, struct gp_extension_reg_entry); 269 NT_STATUS_HAVE_NO_MEMORY(reg_entry); 270 271 data = talloc_zero(mem_ctx, struct registry_value); 272 NT_STATUS_HAVE_NO_MEMORY(data); 273 274 data->type = type; 275 276 switch (type) { 277 case REG_SZ: 278 case REG_EXPAND_SZ: 279 data->v.sz.str = talloc_strdup(mem_ctx, data_s); 280 NT_STATUS_HAVE_NO_MEMORY(data->v.sz.str); 281 data->v.sz.len = strlen(data_s); 282 break; 283 case REG_DWORD: 284 data->v.dword = atoi(data_s); 285 break; 286 default: 287 return NT_STATUS_NOT_SUPPORTED; 288 } 289 290 reg_entry->value = value; 291 reg_entry->data = data; 292 293 if (!add_gp_extension_reg_entry_to_array(mem_ctx, reg_entry, 294 &entry->entries, 295 &entry->num_entries)) { 296 return NT_STATUS_NO_MEMORY; 297 } 298 299 return NT_STATUS_OK; 300} 301 302/**************************************************************** 303****************************************************************/ 304 305static NTSTATUS gp_ext_info_add_reg_table(TALLOC_CTX *mem_ctx, 306 const char *module, 307 struct gp_extension_reg_info_entry *entry, 308 struct gp_extension_reg_table *table) 309{ 310 NTSTATUS status; 311 const char *module_name = NULL; 312 int i; 313 314 module_name = talloc_asprintf(mem_ctx, "%s.%s", module, shlib_ext()); 315 NT_STATUS_HAVE_NO_MEMORY(module_name); 316 317 status = gp_ext_info_add_reg(mem_ctx, entry, 318 "DllName", REG_EXPAND_SZ, module_name); 319 NT_STATUS_NOT_OK_RETURN(status); 320 321 for (i=0; table[i].val; i++) { 322 status = gp_ext_info_add_reg(mem_ctx, entry, 323 table[i].val, 324 table[i].type, 325 table[i].data); 326 NT_STATUS_NOT_OK_RETURN(status); 327 } 328 329 return status; 330} 331 332/**************************************************************** 333****************************************************************/ 334 335NTSTATUS gp_ext_info_add_entry(TALLOC_CTX *mem_ctx, 336 const char *module, 337 const char *ext_guid, 338 struct gp_extension_reg_table *table, 339 struct gp_extension_reg_info *info) 340{ 341 NTSTATUS status; 342 struct gp_extension_reg_info_entry *entry = NULL; 343 344 entry = TALLOC_ZERO_P(mem_ctx, struct gp_extension_reg_info_entry); 345 NT_STATUS_HAVE_NO_MEMORY(entry); 346 347 status = GUID_from_string(ext_guid, &entry->guid); 348 NT_STATUS_NOT_OK_RETURN(status); 349 350 status = gp_ext_info_add_reg_table(mem_ctx, module, entry, table); 351 NT_STATUS_NOT_OK_RETURN(status); 352 353 if (!add_gp_extension_reg_info_entry_to_array(mem_ctx, entry, 354 &info->entries, 355 &info->num_entries)) { 356 return NT_STATUS_NO_MEMORY; 357 } 358 359 return NT_STATUS_OK; 360} 361 362/**************************************************************** 363****************************************************************/ 364 365static bool gp_extension_reg_info_verify_entry(struct gp_extension_reg_entry *entry) 366{ 367 int i; 368 369 for (i=0; gpext_reg_vals[i].val; i++) { 370 371 if ((strequal(entry->value, gpext_reg_vals[i].val)) && 372 (entry->data->type == gpext_reg_vals[i].type)) { 373 return true; 374 } 375 } 376 377 return false; 378} 379 380/**************************************************************** 381****************************************************************/ 382 383static bool gp_extension_reg_info_verify(struct gp_extension_reg_info_entry *entry) 384{ 385 int i; 386 387 for (i=0; i < entry->num_entries; i++) { 388 if (!gp_extension_reg_info_verify_entry(&entry->entries[i])) { 389 return false; 390 } 391 } 392 393 return true; 394} 395 396/**************************************************************** 397****************************************************************/ 398 399static WERROR gp_extension_store_reg_vals(TALLOC_CTX *mem_ctx, 400 struct registry_key *key, 401 struct gp_extension_reg_info_entry *entry) 402{ 403 WERROR werr = WERR_OK; 404 size_t i; 405 406 for (i=0; i < entry->num_entries; i++) { 407 408 werr = reg_setvalue(key, 409 entry->entries[i].value, 410 entry->entries[i].data); 411 W_ERROR_NOT_OK_RETURN(werr); 412 } 413 414 return werr; 415} 416 417/**************************************************************** 418****************************************************************/ 419 420static WERROR gp_extension_store_reg_entry(TALLOC_CTX *mem_ctx, 421 struct gp_registry_context *reg_ctx, 422 struct gp_extension_reg_info_entry *entry) 423{ 424 WERROR werr; 425 struct registry_key *key = NULL; 426 const char *subkeyname = NULL; 427 428 if (!gp_extension_reg_info_verify(entry)) { 429 return WERR_INVALID_PARAM; 430 } 431 432 subkeyname = GUID_string2(mem_ctx, &entry->guid); 433 W_ERROR_HAVE_NO_MEMORY(subkeyname); 434 435 strupper_m(CONST_DISCARD(char *,subkeyname)); 436 437 werr = gp_store_reg_subkey(mem_ctx, 438 subkeyname, 439 reg_ctx->curr_key, 440 &key); 441 W_ERROR_NOT_OK_RETURN(werr); 442 443 werr = gp_extension_store_reg_vals(mem_ctx, 444 key, 445 entry); 446 W_ERROR_NOT_OK_RETURN(werr); 447 448 return werr; 449} 450 451/**************************************************************** 452****************************************************************/ 453 454static WERROR gp_extension_store_reg(TALLOC_CTX *mem_ctx, 455 struct gp_registry_context *reg_ctx, 456 struct gp_extension_reg_info *info) 457{ 458 WERROR werr = WERR_OK; 459 int i; 460 461 if (!info) { 462 return WERR_OK; 463 } 464 465 for (i=0; i < info->num_entries; i++) { 466 werr = gp_extension_store_reg_entry(mem_ctx, 467 reg_ctx, 468 &info->entries[i]); 469 W_ERROR_NOT_OK_RETURN(werr); 470 } 471 472 return werr; 473} 474 475/**************************************************************** 476****************************************************************/ 477 478static NTSTATUS gp_glob_ext_list(TALLOC_CTX *mem_ctx, 479 const char ***ext_list, 480 size_t *ext_list_len) 481{ 482 SMB_STRUCT_DIR *dir = NULL; 483 SMB_STRUCT_DIRENT *dirent = NULL; 484 485 dir = sys_opendir(modules_path(SAMBA_SUBSYSTEM_GPEXT)); 486 if (!dir) { 487 return map_nt_error_from_unix(errno); 488 } 489 490 while ((dirent = sys_readdir(dir))) { 491 492 fstring name; /* forgive me... */ 493 char *p; 494 495 if ((strequal(dirent->d_name, ".")) || 496 (strequal(dirent->d_name, ".."))) { 497 continue; 498 } 499 500 p = strrchr(dirent->d_name, '.'); 501 if (!p) { 502 sys_closedir(dir); 503 return NT_STATUS_NO_MEMORY; 504 } 505 506 if (!strcsequal(p+1, shlib_ext())) { 507 DEBUG(10,("gp_glob_ext_list: not a *.so file: %s\n", 508 dirent->d_name)); 509 continue; 510 } 511 512 fstrcpy(name, dirent->d_name); 513 name[PTR_DIFF(p, dirent->d_name)] = 0; 514 515 if (!add_string_to_array(mem_ctx, name, ext_list, 516 (int *)ext_list_len)) { 517 sys_closedir(dir); 518 return NT_STATUS_NO_MEMORY; 519 } 520 } 521 522 sys_closedir(dir); 523 524 return NT_STATUS_OK; 525} 526 527/**************************************************************** 528****************************************************************/ 529 530NTSTATUS shutdown_gp_extensions(void) 531{ 532 struct gp_extension *ext = NULL; 533 534 for (ext = extensions; ext; ext = ext->next) { 535 if (ext->methods && ext->methods->shutdown) { 536 ext->methods->shutdown(); 537 } 538 } 539 540 return NT_STATUS_OK; 541} 542 543/**************************************************************** 544****************************************************************/ 545 546NTSTATUS init_gp_extensions(TALLOC_CTX *mem_ctx) 547{ 548 NTSTATUS status; 549 WERROR werr; 550 int i = 0; 551 const char **ext_array = NULL; 552 size_t ext_array_len = 0; 553 struct gp_extension *gpext = NULL; 554 struct gp_registry_context *reg_ctx = NULL; 555 556 if (get_gp_extension_list()) { 557 return NT_STATUS_OK; 558 } 559 560 status = gp_glob_ext_list(mem_ctx, &ext_array, &ext_array_len); 561 NT_STATUS_NOT_OK_RETURN(status); 562 563 for (i=0; i<ext_array_len; i++) { 564 565 struct gp_extension_reg_info *info = NULL; 566 567 status = gp_extension_init_module(mem_ctx, ext_array[i], 568 &gpext); 569 if (!NT_STATUS_IS_OK(status)) { 570 goto out; 571 } 572 573 if (gpext->methods->get_reg_config) { 574 575 status = gpext->methods->initialize(mem_ctx); 576 if (!NT_STATUS_IS_OK(status)) { 577 gpext->methods->shutdown(); 578 goto out; 579 } 580 581 status = gpext->methods->get_reg_config(mem_ctx, 582 &info); 583 if (!NT_STATUS_IS_OK(status)) { 584 gpext->methods->shutdown(); 585 goto out; 586 } 587 588 if (!reg_ctx) { 589 NT_USER_TOKEN *token; 590 591 token = registry_create_system_token(mem_ctx); 592 NT_STATUS_HAVE_NO_MEMORY(token); 593 594 werr = gp_init_reg_ctx(mem_ctx, 595 KEY_WINLOGON_GPEXT_PATH, 596 REG_KEY_WRITE, 597 token, 598 ®_ctx); 599 if (!W_ERROR_IS_OK(werr)) { 600 status = werror_to_ntstatus(werr); 601 gpext->methods->shutdown(); 602 goto out; 603 } 604 } 605 606 werr = gp_extension_store_reg(mem_ctx, reg_ctx, info); 607 if (!W_ERROR_IS_OK(werr)) { 608 DEBUG(1,("gp_extension_store_reg failed: %s\n", 609 win_errstr(werr))); 610 TALLOC_FREE(info); 611 gpext->methods->shutdown(); 612 status = werror_to_ntstatus(werr); 613 goto out; 614 } 615 TALLOC_FREE(info); 616 } 617 618 } 619 620 out: 621 TALLOC_FREE(reg_ctx); 622 623 return status; 624} 625 626/**************************************************************** 627****************************************************************/ 628 629NTSTATUS free_gp_extensions(void) 630{ 631 struct gp_extension *ext, *ext_next = NULL; 632 633 for (ext = extensions; ext; ext = ext_next) { 634 ext_next = ext->next; 635 DLIST_REMOVE(extensions, ext); 636 TALLOC_FREE(ext); 637 } 638 639 extensions = NULL; 640 641 return NT_STATUS_OK; 642} 643 644/**************************************************************** 645****************************************************************/ 646 647void debug_gpext_header(int lvl, 648 const char *name, 649 uint32_t flags, 650 struct GROUP_POLICY_OBJECT *gpo, 651 const char *extension_guid, 652 const char *snapin_guid) 653{ 654 char *flags_str = NULL; 655 656 DEBUG(lvl,("%s\n", name)); 657 DEBUGADD(lvl,("\tgpo: %s (%s)\n", gpo->name, 658 gpo->display_name)); 659 DEBUGADD(lvl,("\tcse extension: %s (%s)\n", extension_guid, 660 cse_gpo_guid_string_to_name(extension_guid))); 661 DEBUGADD(lvl,("\tgplink: %s\n", gpo->link)); 662 DEBUGADD(lvl,("\tsnapin: %s (%s)\n", snapin_guid, 663 cse_snapin_gpo_guid_string_to_name(snapin_guid))); 664 665 flags_str = gpo_flag_str(NULL, flags); 666 DEBUGADD(lvl,("\tflags: 0x%08x %s\n", flags, flags_str)); 667 TALLOC_FREE(flags_str); 668} 669 670NTSTATUS process_gpo_list_with_extension(ADS_STRUCT *ads, 671 TALLOC_CTX *mem_ctx, 672 uint32_t flags, 673 const NT_USER_TOKEN *token, 674 struct GROUP_POLICY_OBJECT *gpo_list, 675 const char *extension_guid, 676 const char *snapin_guid) 677{ 678 return NT_STATUS_OK; 679} 680 681/**************************************************************** 682****************************************************************/ 683 684NTSTATUS gpext_process_extension(ADS_STRUCT *ads, 685 TALLOC_CTX *mem_ctx, 686 uint32_t flags, 687 const NT_USER_TOKEN *token, 688 struct registry_key *root_key, 689 struct GROUP_POLICY_OBJECT *gpo, 690 const char *extension_guid, 691 const char *snapin_guid) 692{ 693 NTSTATUS status; 694 struct gp_extension *ext = NULL; 695 struct GUID guid; 696 bool cse_found = false; 697 698 status = init_gp_extensions(mem_ctx); 699 if (!NT_STATUS_IS_OK(status)) { 700 DEBUG(1,("init_gp_extensions failed: %s\n", 701 nt_errstr(status))); 702 return status; 703 } 704 705 status = GUID_from_string(extension_guid, &guid); 706 if (!NT_STATUS_IS_OK(status)) { 707 return status; 708 } 709 710 for (ext = extensions; ext; ext = ext->next) { 711 712 if (GUID_equal(ext->guid, &guid)) { 713 cse_found = true; 714 break; 715 } 716 } 717 718 if (!cse_found) { 719 goto no_ext; 720 } 721 722 status = ext->methods->initialize(mem_ctx); 723 NT_STATUS_NOT_OK_RETURN(status); 724 725 status = ext->methods->process_group_policy(ads, 726 mem_ctx, 727 flags, 728 root_key, 729 token, 730 gpo, 731 extension_guid, 732 snapin_guid); 733 if (!NT_STATUS_IS_OK(status)) { 734 ext->methods->shutdown(); 735 } 736 737 return status; 738 739 no_ext: 740 if (flags & GPO_INFO_FLAG_VERBOSE) { 741 DEBUG(0,("process_extension: no extension available for:\n")); 742 DEBUGADD(0,("%s (%s) (snapin: %s)\n", 743 extension_guid, 744 cse_gpo_guid_string_to_name(extension_guid), 745 snapin_guid)); 746 } 747 748 return NT_STATUS_OK; 749} 750