2 * Unix SMB/CIFS implementation.
4 * Copyright (C) Guenther Deschner 2007-2008
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.
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.
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/>.
21 #include "../libgpo/gpo.h"
22 #include "../libgpo/gpext/gpext.h"
23 #include "librpc/gen_ndr/ndr_misc.h"
24 #include "lib/util/dlinklist.h"
25 #include "../libcli/registry/util_reg.h"
26 #include "libgpo/gpo_proto.h"
28 #include "registry/reg_api.h"
29 #include "lib/util/util_paths.h"
31 static struct gp_extension
*extensions
= NULL
;
33 /****************************************************************
34 ****************************************************************/
36 struct gp_extension
*gpext_get_gp_extension_list(void)
41 /****************************************************************
42 ****************************************************************/
44 /* see http://support.microsoft.com/kb/216358/en-us/ for more info */
46 struct gp_extension_reg_table gpext_reg_vals
[] = {
47 { "DllName", REG_EXPAND_SZ
},
48 { "ProcessGroupPolicy", REG_SZ
},
49 { "NoMachinePolicy", REG_DWORD
},
50 { "NoUserPolicy", REG_DWORD
},
51 { "NoSlowLink", REG_DWORD
},
52 { "NoBackgroundPolicy", REG_DWORD
},
53 { "NoGPOListChanges", REG_DWORD
},
54 { "PerUserLocalSettings", REG_DWORD
},
55 { "RequiresSuccessfulRegistry", REG_DWORD
},
56 { "EnableAsynchronousProcessing", REG_DWORD
},
57 { "ExtensionDebugLevel", REG_DWORD
},
59 { "GenerateGroupPolicy", REG_SZ
}, /* not supported on w2k */
60 { "NotifyLinkTransition", REG_DWORD
},
61 { "ProcessGroupPolicyEx", REG_SZ
}, /* not supported on w2k */
62 { "ExtensionEventSource", REG_MULTI_SZ
}, /* not supported on w2k */
63 { "GenerateGroupPolicy", REG_SZ
},
64 { "MaxNoGPOListChangesInterval", REG_DWORD
},
68 /****************************************************************
69 ****************************************************************/
71 static struct gp_extension
*get_extension_by_name(struct gp_extension
*be
,
74 struct gp_extension
*b
;
76 for (b
= be
; b
; b
= b
->next
) {
77 if (strequal(b
->name
, name
)) {
85 /****************************************************************
86 ****************************************************************/
88 static struct gp_extension_methods
*get_methods_by_name(struct gp_extension
*be
,
91 struct gp_extension
*b
;
93 for (b
= be
; b
; b
= b
->next
) {
94 if (strequal(b
->name
, name
)) {
102 /****************************************************************
103 ****************************************************************/
105 NTSTATUS
gpext_unregister_gp_extension(const char *name
)
107 struct gp_extension
*ext
;
109 ext
= get_extension_by_name(extensions
, name
);
114 DLIST_REMOVE(extensions
, ext
);
117 DEBUG(2,("Successfully removed GP extension '%s'\n", name
));
122 /****************************************************************
123 ****************************************************************/
125 NTSTATUS
gpext_register_gp_extension(TALLOC_CTX
*gpext_ctx
,
129 struct gp_extension_methods
*methods
)
131 struct gp_extension_methods
*test
;
132 struct gp_extension
*entry
;
136 return NT_STATUS_INTERNAL_DB_ERROR
;
139 if ((version
!= SMB_GPEXT_INTERFACE_VERSION
)) {
140 DEBUG(0,("Failed to register gp extension.\n"
141 "The module was compiled against "
142 "SMB_GPEXT_INTERFACE_VERSION %d,\n"
143 "current SMB_GPEXT_INTERFACE_VERSION is %d.\n"
144 "Please recompile against the current "
145 "version of samba!\n",
146 version
, SMB_GPEXT_INTERFACE_VERSION
));
147 return NT_STATUS_OBJECT_TYPE_MISMATCH
;
150 if (!guid
|| !name
|| !name
[0] || !methods
) {
151 DEBUG(0,("Called with NULL pointer or empty name!\n"));
152 return NT_STATUS_INVALID_PARAMETER
;
155 test
= get_methods_by_name(extensions
, name
);
157 DEBUG(0,("GP extension module %s already registered!\n",
159 return NT_STATUS_OBJECT_NAME_COLLISION
;
162 entry
= talloc_zero(gpext_ctx
, struct gp_extension
);
163 NT_STATUS_HAVE_NO_MEMORY(entry
);
165 entry
->name
= talloc_strdup(gpext_ctx
, name
);
166 NT_STATUS_HAVE_NO_MEMORY(entry
->name
);
168 entry
->guid
= talloc_zero(gpext_ctx
, struct GUID
);
169 NT_STATUS_HAVE_NO_MEMORY(entry
->guid
);
170 status
= GUID_from_string(guid
, entry
->guid
);
171 NT_STATUS_NOT_OK_RETURN(status
);
173 entry
->methods
= methods
;
174 DLIST_ADD(extensions
, entry
);
176 DEBUG(2,("Successfully added GP extension '%s' %s\n",
177 name
, GUID_string2(gpext_ctx
, entry
->guid
)));
182 /****************************************************************
183 ****************************************************************/
185 static NTSTATUS
gp_extension_init_module(TALLOC_CTX
*mem_ctx
,
187 struct gp_extension
**gpext
)
190 struct gp_extension
*ext
= NULL
;
192 ext
= talloc_zero(mem_ctx
, struct gp_extension
);
193 NT_STATUS_HAVE_NO_MEMORY(gpext
);
195 ext
->methods
= get_methods_by_name(extensions
, name
);
198 status
= smb_probe_module(SAMBA_SUBSYSTEM_GPEXT
,
200 if (!NT_STATUS_IS_OK(status
)) {
204 ext
->methods
= get_methods_by_name(extensions
, name
);
206 return NT_STATUS_DLL_INIT_FAILED
;
215 /****************************************************************
216 ****************************************************************/
218 static bool add_gp_extension_reg_entry_to_array(TALLOC_CTX
*mem_ctx
,
219 struct gp_extension_reg_entry
*entry
,
220 struct gp_extension_reg_entry
**entries
,
223 *entries
= talloc_realloc(mem_ctx
, *entries
,
224 struct gp_extension_reg_entry
,
226 if (*entries
== NULL
) {
231 (*entries
)[*num
].value
= entry
->value
;
232 (*entries
)[*num
].data
= entry
->data
;
238 /****************************************************************
239 ****************************************************************/
241 static bool add_gp_extension_reg_info_entry_to_array(TALLOC_CTX
*mem_ctx
,
242 struct gp_extension_reg_info_entry
*entry
,
243 struct gp_extension_reg_info_entry
**entries
,
246 *entries
= talloc_realloc(mem_ctx
, *entries
,
247 struct gp_extension_reg_info_entry
,
249 if (*entries
== NULL
) {
254 (*entries
)[*num
].guid
= entry
->guid
;
255 (*entries
)[*num
].num_entries
= entry
->num_entries
;
256 (*entries
)[*num
].entries
= entry
->entries
;
262 /****************************************************************
263 ****************************************************************/
265 static NTSTATUS
gp_ext_info_add_reg(TALLOC_CTX
*mem_ctx
,
266 struct gp_extension_reg_info_entry
*entry
,
268 enum winreg_Type type
,
271 struct gp_extension_reg_entry
*reg_entry
= NULL
;
272 struct registry_value
*data
= NULL
;
274 reg_entry
= talloc_zero(mem_ctx
, struct gp_extension_reg_entry
);
275 NT_STATUS_HAVE_NO_MEMORY(reg_entry
);
277 data
= talloc_zero(mem_ctx
, struct registry_value
);
278 NT_STATUS_HAVE_NO_MEMORY(data
);
285 if (!push_reg_sz(mem_ctx
, &data
->data
, data_s
)) {
286 return NT_STATUS_NO_MEMORY
;
290 uint32_t v
= atoi(data_s
);
291 data
->data
= data_blob_talloc(mem_ctx
, NULL
, 4);
292 SIVAL(data
->data
.data
, 0, v
);
296 return NT_STATUS_NOT_SUPPORTED
;
299 reg_entry
->value
= value
;
300 reg_entry
->data
= data
;
302 if (!add_gp_extension_reg_entry_to_array(mem_ctx
, reg_entry
,
304 &entry
->num_entries
)) {
305 return NT_STATUS_NO_MEMORY
;
311 /****************************************************************
312 ****************************************************************/
314 static NTSTATUS
gp_ext_info_add_reg_table(TALLOC_CTX
*mem_ctx
,
316 struct gp_extension_reg_info_entry
*entry
,
317 struct gp_extension_reg_table
*table
)
320 const char *module_name
= NULL
;
323 module_name
= talloc_asprintf(mem_ctx
, "%s.%s", module
, shlib_ext());
324 NT_STATUS_HAVE_NO_MEMORY(module_name
);
326 status
= gp_ext_info_add_reg(mem_ctx
, entry
,
327 "DllName", REG_EXPAND_SZ
, module_name
);
328 NT_STATUS_NOT_OK_RETURN(status
);
330 for (i
=0; table
[i
].val
; i
++) {
331 status
= gp_ext_info_add_reg(mem_ctx
, entry
,
335 NT_STATUS_NOT_OK_RETURN(status
);
341 /****************************************************************
342 ****************************************************************/
344 NTSTATUS
gpext_info_add_entry(TALLOC_CTX
*mem_ctx
,
346 const char *ext_guid
,
347 struct gp_extension_reg_table
*table
,
348 struct gp_extension_reg_info
*info
)
351 struct gp_extension_reg_info_entry
*entry
= NULL
;
353 entry
= talloc_zero(mem_ctx
, struct gp_extension_reg_info_entry
);
354 NT_STATUS_HAVE_NO_MEMORY(entry
);
356 status
= GUID_from_string(ext_guid
, &entry
->guid
);
357 NT_STATUS_NOT_OK_RETURN(status
);
359 status
= gp_ext_info_add_reg_table(mem_ctx
, module
, entry
, table
);
360 NT_STATUS_NOT_OK_RETURN(status
);
362 if (!add_gp_extension_reg_info_entry_to_array(mem_ctx
, entry
,
364 &info
->num_entries
)) {
365 return NT_STATUS_NO_MEMORY
;
371 /****************************************************************
372 ****************************************************************/
374 static bool gp_extension_reg_info_verify_entry(struct gp_extension_reg_entry
*entry
)
378 for (i
=0; gpext_reg_vals
[i
].val
; i
++) {
380 if ((strequal(entry
->value
, gpext_reg_vals
[i
].val
)) &&
381 (entry
->data
->type
== gpext_reg_vals
[i
].type
)) {
389 /****************************************************************
390 ****************************************************************/
392 static bool gp_extension_reg_info_verify(struct gp_extension_reg_info_entry
*entry
)
396 for (i
=0; i
< entry
->num_entries
; i
++) {
397 if (!gp_extension_reg_info_verify_entry(&entry
->entries
[i
])) {
405 /****************************************************************
406 ****************************************************************/
408 static WERROR
gp_extension_store_reg_vals(TALLOC_CTX
*mem_ctx
,
409 struct registry_key
*key
,
410 struct gp_extension_reg_info_entry
*entry
)
412 WERROR werr
= WERR_OK
;
415 for (i
=0; i
< entry
->num_entries
; i
++) {
417 werr
= reg_setvalue(key
,
418 entry
->entries
[i
].value
,
419 entry
->entries
[i
].data
);
420 W_ERROR_NOT_OK_RETURN(werr
);
426 /****************************************************************
427 ****************************************************************/
429 static WERROR
gp_extension_store_reg_entry(TALLOC_CTX
*mem_ctx
,
430 struct gp_registry_context
*reg_ctx
,
431 struct gp_extension_reg_info_entry
*entry
)
434 struct registry_key
*key
= NULL
;
435 const char *subkeyname
= NULL
;
437 if (!gp_extension_reg_info_verify(entry
)) {
438 return WERR_INVALID_PARAMETER
;
441 subkeyname
= GUID_string2(mem_ctx
, &entry
->guid
);
442 W_ERROR_HAVE_NO_MEMORY(subkeyname
);
444 if (!strupper_m(discard_const_p(char, subkeyname
))) {
445 return WERR_INVALID_PARAMETER
;
448 werr
= gp_store_reg_subkey(mem_ctx
,
452 W_ERROR_NOT_OK_RETURN(werr
);
454 werr
= gp_extension_store_reg_vals(mem_ctx
,
457 W_ERROR_NOT_OK_RETURN(werr
);
462 /****************************************************************
463 ****************************************************************/
465 static WERROR
gp_extension_store_reg(TALLOC_CTX
*mem_ctx
,
466 struct gp_registry_context
*reg_ctx
,
467 struct gp_extension_reg_info
*info
)
469 WERROR werr
= WERR_OK
;
476 for (i
=0; i
< info
->num_entries
; i
++) {
477 werr
= gp_extension_store_reg_entry(mem_ctx
,
480 W_ERROR_NOT_OK_RETURN(werr
);
486 /****************************************************************
487 ****************************************************************/
489 static NTSTATUS
gp_glob_ext_list(TALLOC_CTX
*mem_ctx
,
490 const char ***ext_list
,
491 size_t *ext_list_len
)
494 struct dirent
*dirent
= NULL
;
496 dir
= opendir(modules_path(talloc_tos(),
497 SAMBA_SUBSYSTEM_GPEXT
));
499 return map_nt_error_from_unix_common(errno
);
502 while ((dirent
= readdir(dir
))) {
504 fstring name
; /* forgive me... */
507 if ((strequal(dirent
->d_name
, ".")) ||
508 (strequal(dirent
->d_name
, ".."))) {
512 p
= strrchr(dirent
->d_name
, '.');
515 return NT_STATUS_NO_MEMORY
;
518 if (!strcsequal(p
+1, shlib_ext())) {
519 DEBUG(10,("gp_glob_ext_list: not a *.so file: %s\n",
524 fstrcpy(name
, dirent
->d_name
);
525 name
[PTR_DIFF(p
, dirent
->d_name
)] = 0;
527 if (!add_string_to_array(mem_ctx
, name
, ext_list
,
530 return NT_STATUS_NO_MEMORY
;
539 /****************************************************************
540 ****************************************************************/
542 NTSTATUS
gpext_shutdown_gp_extensions(void)
544 struct gp_extension
*ext
= NULL
;
546 for (ext
= extensions
; ext
; ext
= ext
->next
) {
547 if (ext
->methods
&& ext
->methods
->shutdown
) {
548 ext
->methods
->shutdown();
555 /****************************************************************
556 ****************************************************************/
558 NTSTATUS
gpext_init_gp_extensions(TALLOC_CTX
*mem_ctx
)
563 const char **ext_array
= NULL
;
564 size_t ext_array_len
= 0;
565 struct gp_extension
*gpext
= NULL
;
566 struct gp_registry_context
*reg_ctx
= NULL
;
568 if (gpext_get_gp_extension_list()) {
572 status
= gp_glob_ext_list(mem_ctx
, &ext_array
, &ext_array_len
);
573 NT_STATUS_NOT_OK_RETURN(status
);
575 for (i
=0; i
<ext_array_len
; i
++) {
577 struct gp_extension_reg_info
*info
= NULL
;
579 status
= gp_extension_init_module(mem_ctx
, ext_array
[i
],
581 if (!NT_STATUS_IS_OK(status
)) {
585 if (gpext
->methods
->get_reg_config
) {
587 status
= gpext
->methods
->initialize(mem_ctx
);
588 if (!NT_STATUS_IS_OK(status
)) {
589 gpext
->methods
->shutdown();
593 status
= gpext
->methods
->get_reg_config(mem_ctx
,
595 if (!NT_STATUS_IS_OK(status
)) {
596 gpext
->methods
->shutdown();
601 struct security_token
*token
;
603 token
= registry_create_system_token(mem_ctx
);
604 NT_STATUS_HAVE_NO_MEMORY(token
);
606 werr
= gp_init_reg_ctx(mem_ctx
,
607 KEY_WINLOGON_GPEXT_PATH
,
611 if (!W_ERROR_IS_OK(werr
)) {
612 status
= werror_to_ntstatus(werr
);
613 gpext
->methods
->shutdown();
618 werr
= gp_extension_store_reg(mem_ctx
, reg_ctx
, info
);
619 if (!W_ERROR_IS_OK(werr
)) {
620 DEBUG(1,("gp_extension_store_reg failed: %s\n",
623 gpext
->methods
->shutdown();
624 status
= werror_to_ntstatus(werr
);
633 TALLOC_FREE(reg_ctx
);
638 /****************************************************************
639 ****************************************************************/
641 NTSTATUS
gpext_free_gp_extensions(void)
643 struct gp_extension
*ext
, *ext_next
= NULL
;
645 for (ext
= extensions
; ext
; ext
= ext_next
) {
646 ext_next
= ext
->next
;
647 DLIST_REMOVE(extensions
, ext
);
656 /****************************************************************
657 ****************************************************************/
659 void gpext_debug_header(int lvl
,
662 const struct GROUP_POLICY_OBJECT
*gpo
,
663 const char *extension_guid
,
664 const char *snapin_guid
)
666 char *flags_str
= NULL
;
668 DEBUG(lvl
,("%s\n", name
));
669 DEBUGADD(lvl
,("\tgpo: %s (%s)\n", gpo
->name
,
671 DEBUGADD(lvl
,("\tcse extension: %s (%s)\n", extension_guid
,
672 cse_gpo_guid_string_to_name(extension_guid
)));
673 DEBUGADD(lvl
,("\tgplink: %s\n", gpo
->link
));
674 DEBUGADD(lvl
,("\tsnapin: %s (%s)\n", snapin_guid
,
675 cse_snapin_gpo_guid_string_to_name(snapin_guid
)));
677 flags_str
= gpo_flag_str(NULL
, flags
);
678 DEBUGADD(lvl
,("\tflags: 0x%08x %s\n", flags
, flags_str
));
679 TALLOC_FREE(flags_str
);
682 /****************************************************************
683 ****************************************************************/
685 static NTSTATUS
gpext_check_gpo_for_gpext_presence(TALLOC_CTX
*mem_ctx
,
687 const struct GROUP_POLICY_OBJECT
*gpo
,
688 const struct GUID
*guid
,
689 bool *gpext_guid_present
)
691 struct GP_EXT
*gp_ext
= NULL
;
695 *gpext_guid_present
= false;
698 if (gpo
->link_type
== GP_LINK_LOCAL
) {
702 ok
= gpo_get_gp_ext_from_gpo(mem_ctx
, flags
, gpo
, &gp_ext
);
704 return NT_STATUS_INVALID_PARAMETER
;
707 if (gp_ext
== NULL
) {
711 for (i
= 0; i
< gp_ext
->num_exts
; i
++) {
715 status
= GUID_from_string(gp_ext
->extensions_guid
[i
], &guid2
);
716 if (!NT_STATUS_IS_OK(status
)) {
719 if (GUID_equal(guid
, &guid2
)) {
720 *gpext_guid_present
= true;
728 /****************************************************************
729 ****************************************************************/
731 NTSTATUS
gpext_process_extension(TALLOC_CTX
*mem_ctx
,
733 const struct security_token
*token
,
734 struct registry_key
*root_key
,
735 const struct GROUP_POLICY_OBJECT
*deleted_gpo_list
,
736 const struct GROUP_POLICY_OBJECT
*changed_gpo_list
,
737 const char *extension_guid_filter
)
740 struct gp_extension
*ext
= NULL
;
741 const struct GROUP_POLICY_OBJECT
*gpo
;
742 struct GUID extension_guid_filter_guid
;
744 status
= gpext_init_gp_extensions(mem_ctx
);
745 if (!NT_STATUS_IS_OK(status
)) {
746 DEBUG(1,("gpext_init_gp_extensions failed: %s\n",
751 if (extension_guid_filter
) {
752 status
= GUID_from_string(extension_guid_filter
,
753 &extension_guid_filter_guid
);
754 if (!NT_STATUS_IS_OK(status
)) {
759 for (ext
= extensions
; ext
; ext
= ext
->next
) {
761 struct GROUP_POLICY_OBJECT
*deleted_gpo_list_filtered
= NULL
;
762 struct GROUP_POLICY_OBJECT
*changed_gpo_list_filtered
= NULL
;
764 if (extension_guid_filter
) {
765 if (!GUID_equal(&extension_guid_filter_guid
, ext
->guid
)) {
770 for (gpo
= deleted_gpo_list
; gpo
; gpo
= gpo
->next
) {
772 bool is_present
= false;
774 status
= gpext_check_gpo_for_gpext_presence(mem_ctx
,
779 if (!NT_STATUS_IS_OK(status
)) {
784 struct GROUP_POLICY_OBJECT
*new_gpo
;
786 status
= gpo_copy(mem_ctx
, gpo
, &new_gpo
);
787 if (!NT_STATUS_IS_OK(status
)) {
791 DLIST_ADD(deleted_gpo_list_filtered
, new_gpo
);
795 for (gpo
= changed_gpo_list
; gpo
; gpo
= gpo
->next
) {
797 bool is_present
= false;
799 status
= gpext_check_gpo_for_gpext_presence(mem_ctx
,
804 if (!NT_STATUS_IS_OK(status
)) {
809 struct GROUP_POLICY_OBJECT
*new_gpo
;
811 status
= gpo_copy(mem_ctx
, gpo
, &new_gpo
);
812 if (!NT_STATUS_IS_OK(status
)) {
816 DLIST_ADD(changed_gpo_list_filtered
, new_gpo
);
820 status
= ext
->methods
->initialize(mem_ctx
);
821 NT_STATUS_NOT_OK_RETURN(status
);
823 status
= ext
->methods
->process_group_policy(mem_ctx
,
827 deleted_gpo_list_filtered
,
828 changed_gpo_list_filtered
);
829 if (!NT_STATUS_IS_OK(status
)) {
830 ext
->methods
->shutdown();