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"
30 #include "lib/util/string_wrappers.h"
32 static struct gp_extension
*extensions
= NULL
;
34 /****************************************************************
35 ****************************************************************/
37 struct gp_extension
*gpext_get_gp_extension_list(void)
42 /****************************************************************
43 ****************************************************************/
46 struct gp_extension_reg_table gpext_reg_vals
[] = {
49 .type
= REG_EXPAND_SZ
,
52 .val
= "ProcessGroupPolicy",
56 .val
= "NoMachinePolicy",
60 .val
= "NoUserPolicy",
68 .val
= "NoBackgroundPolicy",
72 .val
= "NoGPOListChanges",
76 .val
= "PerUserLocalSettings",
80 .val
= "RequiresSuccessfulRegistry",
84 .val
= "EnableAsynchronousProcessing",
88 .val
= "ExtensionDebugLevel",
93 .val
= "GenerateGroupPolicy", /* not supported on w2k */
97 .val
= "NotifyLinkTransition",
101 .val
= "ProcessGroupPolicyEx", /* not supported on w2k */
105 .val
= "ExtensionEventSource", /* not supported on w2k */
106 .type
= REG_MULTI_SZ
,
109 .val
= "MaxNoGPOListChangesInterval",
115 /****************************************************************
116 ****************************************************************/
118 static struct gp_extension
*get_extension_by_name(struct gp_extension
*be
,
121 struct gp_extension
*b
;
123 for (b
= be
; b
; b
= b
->next
) {
124 if (strequal(b
->name
, name
)) {
132 /****************************************************************
133 ****************************************************************/
135 static struct gp_extension_methods
*get_methods_by_name(struct gp_extension
*be
,
138 struct gp_extension
*b
;
140 for (b
= be
; b
; b
= b
->next
) {
141 if (strequal(b
->name
, name
)) {
149 /****************************************************************
150 ****************************************************************/
152 NTSTATUS
gpext_unregister_gp_extension(const char *name
)
154 struct gp_extension
*ext
;
156 ext
= get_extension_by_name(extensions
, name
);
161 DLIST_REMOVE(extensions
, ext
);
164 DEBUG(2,("Successfully removed GP extension '%s'\n", name
));
169 /****************************************************************
170 ****************************************************************/
172 NTSTATUS
gpext_register_gp_extension(TALLOC_CTX
*gpext_ctx
,
176 struct gp_extension_methods
*methods
)
178 struct gp_extension_methods
*test
;
179 struct gp_extension
*entry
;
183 return NT_STATUS_INTERNAL_DB_ERROR
;
186 if ((version
!= SMB_GPEXT_INTERFACE_VERSION
)) {
187 DEBUG(0,("Failed to register gp extension.\n"
188 "The module was compiled against "
189 "SMB_GPEXT_INTERFACE_VERSION %d,\n"
190 "current SMB_GPEXT_INTERFACE_VERSION is %d.\n"
191 "Please recompile against the current "
192 "version of samba!\n",
193 version
, SMB_GPEXT_INTERFACE_VERSION
));
194 return NT_STATUS_OBJECT_TYPE_MISMATCH
;
197 if (!guid
|| !name
|| !name
[0] || !methods
) {
198 DEBUG(0,("Called with NULL pointer or empty name!\n"));
199 return NT_STATUS_INVALID_PARAMETER
;
202 test
= get_methods_by_name(extensions
, name
);
204 DEBUG(0,("GP extension module %s already registered!\n",
206 return NT_STATUS_OBJECT_NAME_COLLISION
;
209 entry
= talloc_zero(gpext_ctx
, struct gp_extension
);
210 NT_STATUS_HAVE_NO_MEMORY(entry
);
212 entry
->name
= talloc_strdup(gpext_ctx
, name
);
213 NT_STATUS_HAVE_NO_MEMORY(entry
->name
);
215 entry
->guid
= talloc_zero(gpext_ctx
, struct GUID
);
216 NT_STATUS_HAVE_NO_MEMORY(entry
->guid
);
217 status
= GUID_from_string(guid
, entry
->guid
);
218 NT_STATUS_NOT_OK_RETURN(status
);
220 entry
->methods
= methods
;
221 DLIST_ADD(extensions
, entry
);
223 DEBUG(2,("Successfully added GP extension '%s' %s\n",
224 name
, GUID_string2(gpext_ctx
, entry
->guid
)));
229 /****************************************************************
230 ****************************************************************/
232 static NTSTATUS
gp_extension_init_module(TALLOC_CTX
*mem_ctx
,
234 struct gp_extension
**gpext
)
237 struct gp_extension
*ext
= NULL
;
239 ext
= talloc_zero(mem_ctx
, struct gp_extension
);
240 NT_STATUS_HAVE_NO_MEMORY(gpext
);
242 ext
->methods
= get_methods_by_name(extensions
, name
);
245 status
= smb_probe_module(SAMBA_SUBSYSTEM_GPEXT
,
247 if (!NT_STATUS_IS_OK(status
)) {
251 ext
->methods
= get_methods_by_name(extensions
, name
);
253 return NT_STATUS_DLL_INIT_FAILED
;
262 /****************************************************************
263 ****************************************************************/
265 static bool add_gp_extension_reg_entry_to_array(TALLOC_CTX
*mem_ctx
,
266 struct gp_extension_reg_entry
*entry
,
267 struct gp_extension_reg_entry
**entries
,
270 *entries
= talloc_realloc(mem_ctx
, *entries
,
271 struct gp_extension_reg_entry
,
273 if (*entries
== NULL
) {
278 (*entries
)[*num
].value
= entry
->value
;
279 (*entries
)[*num
].data
= entry
->data
;
285 /****************************************************************
286 ****************************************************************/
288 static bool add_gp_extension_reg_info_entry_to_array(TALLOC_CTX
*mem_ctx
,
289 struct gp_extension_reg_info_entry
*entry
,
290 struct gp_extension_reg_info_entry
**entries
,
293 *entries
= talloc_realloc(mem_ctx
, *entries
,
294 struct gp_extension_reg_info_entry
,
296 if (*entries
== NULL
) {
301 (*entries
)[*num
].guid
= entry
->guid
;
302 (*entries
)[*num
].num_entries
= entry
->num_entries
;
303 (*entries
)[*num
].entries
= entry
->entries
;
309 /****************************************************************
310 ****************************************************************/
312 static NTSTATUS
gp_ext_info_add_reg(TALLOC_CTX
*mem_ctx
,
313 struct gp_extension_reg_info_entry
*entry
,
315 enum winreg_Type type
,
318 struct gp_extension_reg_entry
*reg_entry
= NULL
;
319 struct registry_value
*data
= NULL
;
321 reg_entry
= talloc_zero(mem_ctx
, struct gp_extension_reg_entry
);
322 NT_STATUS_HAVE_NO_MEMORY(reg_entry
);
324 data
= talloc_zero(mem_ctx
, struct registry_value
);
325 NT_STATUS_HAVE_NO_MEMORY(data
);
332 if (!push_reg_sz(mem_ctx
, &data
->data
, data_s
)) {
333 return NT_STATUS_NO_MEMORY
;
337 uint32_t v
= atoi(data_s
);
338 data
->data
= data_blob_talloc(mem_ctx
, NULL
, 4);
339 SIVAL(data
->data
.data
, 0, v
);
343 return NT_STATUS_NOT_SUPPORTED
;
346 reg_entry
->value
= value
;
347 reg_entry
->data
= data
;
349 if (!add_gp_extension_reg_entry_to_array(mem_ctx
, reg_entry
,
351 &entry
->num_entries
)) {
352 return NT_STATUS_NO_MEMORY
;
358 /****************************************************************
359 ****************************************************************/
361 static NTSTATUS
gp_ext_info_add_reg_table(TALLOC_CTX
*mem_ctx
,
363 struct gp_extension_reg_info_entry
*entry
,
364 struct gp_extension_reg_table
*table
)
367 const char *module_name
= NULL
;
370 module_name
= talloc_asprintf(mem_ctx
, "%s.%s", module
, shlib_ext());
371 NT_STATUS_HAVE_NO_MEMORY(module_name
);
373 status
= gp_ext_info_add_reg(mem_ctx
, entry
,
374 "DllName", REG_EXPAND_SZ
, module_name
);
375 NT_STATUS_NOT_OK_RETURN(status
);
377 for (i
=0; table
[i
].val
; i
++) {
378 status
= gp_ext_info_add_reg(mem_ctx
, entry
,
382 NT_STATUS_NOT_OK_RETURN(status
);
388 /****************************************************************
389 ****************************************************************/
391 NTSTATUS
gpext_info_add_entry(TALLOC_CTX
*mem_ctx
,
393 const char *ext_guid
,
394 struct gp_extension_reg_table
*table
,
395 struct gp_extension_reg_info
*info
)
398 struct gp_extension_reg_info_entry
*entry
= NULL
;
400 entry
= talloc_zero(mem_ctx
, struct gp_extension_reg_info_entry
);
401 NT_STATUS_HAVE_NO_MEMORY(entry
);
403 status
= GUID_from_string(ext_guid
, &entry
->guid
);
404 NT_STATUS_NOT_OK_RETURN(status
);
406 status
= gp_ext_info_add_reg_table(mem_ctx
, module
, entry
, table
);
407 NT_STATUS_NOT_OK_RETURN(status
);
409 if (!add_gp_extension_reg_info_entry_to_array(mem_ctx
, entry
,
411 &info
->num_entries
)) {
412 return NT_STATUS_NO_MEMORY
;
418 /****************************************************************
419 ****************************************************************/
421 static bool gp_extension_reg_info_verify_entry(struct gp_extension_reg_entry
*entry
)
425 for (i
=0; gpext_reg_vals
[i
].val
; i
++) {
427 if ((strequal(entry
->value
, gpext_reg_vals
[i
].val
)) &&
428 (entry
->data
->type
== gpext_reg_vals
[i
].type
)) {
436 /****************************************************************
437 ****************************************************************/
439 static bool gp_extension_reg_info_verify(struct gp_extension_reg_info_entry
*entry
)
443 for (i
=0; i
< entry
->num_entries
; i
++) {
444 if (!gp_extension_reg_info_verify_entry(&entry
->entries
[i
])) {
452 /****************************************************************
453 ****************************************************************/
455 static WERROR
gp_extension_store_reg_vals(TALLOC_CTX
*mem_ctx
,
456 struct registry_key
*key
,
457 struct gp_extension_reg_info_entry
*entry
)
459 WERROR werr
= WERR_OK
;
462 for (i
=0; i
< entry
->num_entries
; i
++) {
464 werr
= reg_setvalue(key
,
465 entry
->entries
[i
].value
,
466 entry
->entries
[i
].data
);
467 W_ERROR_NOT_OK_RETURN(werr
);
473 /****************************************************************
474 ****************************************************************/
476 static WERROR
gp_extension_store_reg_entry(TALLOC_CTX
*mem_ctx
,
477 struct gp_registry_context
*reg_ctx
,
478 struct gp_extension_reg_info_entry
*entry
)
481 struct registry_key
*key
= NULL
;
482 const char *subkeyname
= NULL
;
484 if (!gp_extension_reg_info_verify(entry
)) {
485 return WERR_INVALID_PARAMETER
;
488 subkeyname
= GUID_string2(mem_ctx
, &entry
->guid
);
489 W_ERROR_HAVE_NO_MEMORY(subkeyname
);
491 if (!strupper_m(discard_const_p(char, subkeyname
))) {
492 return WERR_INVALID_PARAMETER
;
495 werr
= gp_store_reg_subkey(mem_ctx
,
499 W_ERROR_NOT_OK_RETURN(werr
);
501 werr
= gp_extension_store_reg_vals(mem_ctx
,
504 W_ERROR_NOT_OK_RETURN(werr
);
509 /****************************************************************
510 ****************************************************************/
512 static WERROR
gp_extension_store_reg(TALLOC_CTX
*mem_ctx
,
513 struct gp_registry_context
*reg_ctx
,
514 struct gp_extension_reg_info
*info
)
516 WERROR werr
= WERR_OK
;
523 for (i
=0; i
< info
->num_entries
; i
++) {
524 werr
= gp_extension_store_reg_entry(mem_ctx
,
527 W_ERROR_NOT_OK_RETURN(werr
);
533 /****************************************************************
534 ****************************************************************/
536 static NTSTATUS
gp_glob_ext_list(TALLOC_CTX
*mem_ctx
,
537 const char ***ext_list
,
538 size_t *ext_list_len
)
541 struct dirent
*dirent
= NULL
;
543 dir
= opendir(modules_path(talloc_tos(),
544 SAMBA_SUBSYSTEM_GPEXT
));
546 return map_nt_error_from_unix_common(errno
);
549 while ((dirent
= readdir(dir
))) {
551 fstring name
; /* forgive me... */
554 if ((strequal(dirent
->d_name
, ".")) ||
555 (strequal(dirent
->d_name
, ".."))) {
559 p
= strrchr(dirent
->d_name
, '.');
562 return NT_STATUS_NO_MEMORY
;
565 if (!strcsequal(p
+1, shlib_ext())) {
566 DEBUG(10,("gp_glob_ext_list: not a *.so file: %s\n",
571 fstrcpy(name
, dirent
->d_name
);
572 name
[PTR_DIFF(p
, dirent
->d_name
)] = 0;
574 if (!add_string_to_array(mem_ctx
, name
, ext_list
,
577 return NT_STATUS_NO_MEMORY
;
586 /****************************************************************
587 ****************************************************************/
589 NTSTATUS
gpext_shutdown_gp_extensions(void)
591 struct gp_extension
*ext
= NULL
;
593 for (ext
= extensions
; ext
; ext
= ext
->next
) {
594 if (ext
->methods
&& ext
->methods
->shutdown
) {
595 ext
->methods
->shutdown();
602 /****************************************************************
603 ****************************************************************/
605 NTSTATUS
gpext_init_gp_extensions(TALLOC_CTX
*mem_ctx
)
610 const char **ext_array
= NULL
;
611 size_t ext_array_len
= 0;
612 struct gp_extension
*gpext
= NULL
;
613 struct gp_registry_context
*reg_ctx
= NULL
;
615 if (gpext_get_gp_extension_list()) {
619 status
= gp_glob_ext_list(mem_ctx
, &ext_array
, &ext_array_len
);
620 NT_STATUS_NOT_OK_RETURN(status
);
622 for (i
=0; i
<ext_array_len
; i
++) {
624 struct gp_extension_reg_info
*info
= NULL
;
626 status
= gp_extension_init_module(mem_ctx
, ext_array
[i
],
628 if (!NT_STATUS_IS_OK(status
)) {
632 if (gpext
->methods
->get_reg_config
) {
634 status
= gpext
->methods
->initialize(mem_ctx
);
635 if (!NT_STATUS_IS_OK(status
)) {
636 gpext
->methods
->shutdown();
640 status
= gpext
->methods
->get_reg_config(mem_ctx
,
642 if (!NT_STATUS_IS_OK(status
)) {
643 gpext
->methods
->shutdown();
648 struct security_token
*token
;
650 token
= registry_create_system_token(mem_ctx
);
651 NT_STATUS_HAVE_NO_MEMORY(token
);
653 werr
= gp_init_reg_ctx(mem_ctx
,
654 KEY_WINLOGON_GPEXT_PATH
,
658 if (!W_ERROR_IS_OK(werr
)) {
659 status
= werror_to_ntstatus(werr
);
660 gpext
->methods
->shutdown();
665 werr
= gp_extension_store_reg(mem_ctx
, reg_ctx
, info
);
666 if (!W_ERROR_IS_OK(werr
)) {
667 DEBUG(1,("gp_extension_store_reg failed: %s\n",
670 gpext
->methods
->shutdown();
671 status
= werror_to_ntstatus(werr
);
680 TALLOC_FREE(reg_ctx
);
685 /****************************************************************
686 ****************************************************************/
688 NTSTATUS
gpext_free_gp_extensions(void)
690 struct gp_extension
*ext
, *ext_next
= NULL
;
692 for (ext
= extensions
; ext
; ext
= ext_next
) {
693 ext_next
= ext
->next
;
694 DLIST_REMOVE(extensions
, ext
);
703 /****************************************************************
704 ****************************************************************/
706 void gpext_debug_header(int lvl
,
709 const struct GROUP_POLICY_OBJECT
*gpo
,
710 const char *extension_guid
,
711 const char *snapin_guid
)
713 char *flags_str
= NULL
;
715 DEBUG(lvl
,("%s\n", name
));
716 DEBUGADD(lvl
,("\tgpo: %s (%s)\n", gpo
->name
,
718 DEBUGADD(lvl
,("\tcse extension: %s (%s)\n", extension_guid
,
719 cse_gpo_guid_string_to_name(extension_guid
)));
720 DEBUGADD(lvl
,("\tgplink: %s\n", gpo
->link
));
721 DEBUGADD(lvl
,("\tsnapin: %s (%s)\n", snapin_guid
,
722 cse_snapin_gpo_guid_string_to_name(snapin_guid
)));
724 flags_str
= gpo_flag_str(NULL
, flags
);
725 DEBUGADD(lvl
,("\tflags: 0x%08x %s\n", flags
, flags_str
));
726 TALLOC_FREE(flags_str
);
729 /****************************************************************
730 ****************************************************************/
732 static NTSTATUS
gpext_check_gpo_for_gpext_presence(TALLOC_CTX
*mem_ctx
,
734 const struct GROUP_POLICY_OBJECT
*gpo
,
735 const struct GUID
*guid
,
736 bool *gpext_guid_present
)
738 struct GP_EXT
*gp_ext
= NULL
;
742 *gpext_guid_present
= false;
745 if (gpo
->link_type
== GP_LINK_LOCAL
) {
749 ok
= gpo_get_gp_ext_from_gpo(mem_ctx
, flags
, gpo
, &gp_ext
);
751 return NT_STATUS_INVALID_PARAMETER
;
754 if (gp_ext
== NULL
) {
758 for (i
= 0; i
< gp_ext
->num_exts
; i
++) {
762 status
= GUID_from_string(gp_ext
->extensions_guid
[i
], &guid2
);
763 if (!NT_STATUS_IS_OK(status
)) {
766 if (GUID_equal(guid
, &guid2
)) {
767 *gpext_guid_present
= true;
775 /****************************************************************
776 ****************************************************************/
778 NTSTATUS
gpext_process_extension(TALLOC_CTX
*mem_ctx
,
780 const struct security_token
*token
,
781 struct registry_key
*root_key
,
782 const struct GROUP_POLICY_OBJECT
*deleted_gpo_list
,
783 const struct GROUP_POLICY_OBJECT
*changed_gpo_list
,
784 const char *extension_guid_filter
)
787 struct gp_extension
*ext
= NULL
;
788 const struct GROUP_POLICY_OBJECT
*gpo
;
789 struct GUID extension_guid_filter_guid
;
791 status
= gpext_init_gp_extensions(mem_ctx
);
792 if (!NT_STATUS_IS_OK(status
)) {
793 DEBUG(1,("gpext_init_gp_extensions failed: %s\n",
798 if (extension_guid_filter
) {
799 status
= GUID_from_string(extension_guid_filter
,
800 &extension_guid_filter_guid
);
801 if (!NT_STATUS_IS_OK(status
)) {
806 for (ext
= extensions
; ext
; ext
= ext
->next
) {
808 struct GROUP_POLICY_OBJECT
*deleted_gpo_list_filtered
= NULL
;
809 struct GROUP_POLICY_OBJECT
*changed_gpo_list_filtered
= NULL
;
811 if (extension_guid_filter
) {
812 if (!GUID_equal(&extension_guid_filter_guid
, ext
->guid
)) {
817 for (gpo
= deleted_gpo_list
; gpo
; gpo
= gpo
->next
) {
819 bool is_present
= false;
821 status
= gpext_check_gpo_for_gpext_presence(mem_ctx
,
826 if (!NT_STATUS_IS_OK(status
)) {
831 struct GROUP_POLICY_OBJECT
*new_gpo
;
833 status
= gpo_copy(mem_ctx
, gpo
, &new_gpo
);
834 if (!NT_STATUS_IS_OK(status
)) {
838 DLIST_ADD(deleted_gpo_list_filtered
, new_gpo
);
842 for (gpo
= changed_gpo_list
; gpo
; gpo
= gpo
->next
) {
844 bool is_present
= false;
846 status
= gpext_check_gpo_for_gpext_presence(mem_ctx
,
851 if (!NT_STATUS_IS_OK(status
)) {
856 struct GROUP_POLICY_OBJECT
*new_gpo
;
858 status
= gpo_copy(mem_ctx
, gpo
, &new_gpo
);
859 if (!NT_STATUS_IS_OK(status
)) {
863 DLIST_ADD(changed_gpo_list_filtered
, new_gpo
);
867 status
= ext
->methods
->initialize(mem_ctx
);
868 NT_STATUS_NOT_OK_RETURN(status
);
870 status
= ext
->methods
->process_group_policy(mem_ctx
,
874 deleted_gpo_list_filtered
,
875 changed_gpo_list_filtered
);
876 if (!NT_STATUS_IS_OK(status
)) {
877 ext
->methods
->shutdown();