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