Adjust samba 3 to the new gpo API. Still untested code.
[amitay/samba.git] / 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 #include "../libgpo/gpext/gpext.h"
22 #include "librpc/gen_ndr/ndr_misc.h"
23 #include "lib/util/dlinklist.h"
24
25 static struct gp_extension *extensions = NULL;
26
27 /****************************************************************
28 ****************************************************************/
29
30 struct 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
40 struct 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
65 static 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
82 static 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
99 NTSTATUS 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
119 NTSTATUS 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
179 static 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
212 static 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
235 static 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
259 static 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
305 static 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
335 NTSTATUS 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
365 static 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
383 static 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
399 static 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
420 static 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
454 static 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
478 static 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
530 NTSTATUS 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
546 NTSTATUS 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                                                        &reg_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
629 NTSTATUS 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
647 void 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
670 NTSTATUS 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
684 NTSTATUS 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 }