1aa22a58be65830fcf2871f56fbac083586eac8b
[nivanova/samba-autobuild/.git] / source3 / rpc_server / svcctl / srv_svcctl_reg.c
1 /*
2  *  Unix SMB/CIFS implementation.
3  *
4  *  SVCCTL RPC server keys initialization
5  *
6  *  Copyright (c) 2005      Marcin Krzysztof Porwit
7  *  Copyright (c) 2005      Gerald (Jerry) Carter
8  *  Copyright (c) 2011      Andreas Schneider <asn@samba.org>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 3 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "services/services.h"
27 #include "services/svc_winreg_glue.h"
28 #include "../librpc/gen_ndr/ndr_winreg_c.h"
29 #include "rpc_client/cli_winreg_int.h"
30 #include "rpc_client/cli_winreg.h"
31 #include "rpc_server/svcctl/srv_svcctl_reg.h"
32 #include "auth.h"
33 #include "registry/reg_backend_db.h"
34
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_REGISTRY
37
38 #define TOP_LEVEL_SERVICES_KEY "SYSTEM\\CurrentControlSet\\Services"
39
40 struct rcinit_file_information {
41         char *description;
42 };
43
44 struct service_display_info {
45         const char *servicename;
46         const char *daemon;
47         const char *dispname;
48         const char *description;
49 };
50
51 static struct service_display_info builtin_svcs[] = {
52         {
53                 "Spooler",
54                 "smbd",
55                 "Print Spooler",
56                 "Internal service for spooling files to print devices"
57         },
58         {
59                 "NETLOGON",
60                 "smbd",
61                 "Net Logon",
62                 "File service providing access to policy and profile data (not"
63                         "remotely manageable)"
64         },
65         {
66                 "RemoteRegistry",
67                 "smbd",
68                 "Remote Registry Service",
69                 "Internal service providing remote access to the Samba registry"
70         },
71         {
72                 "WINS",
73                 "nmbd",
74                 "Windows Internet Name Service (WINS)",
75                 "Internal service providing a NetBIOS point-to-point name server"
76                         "(not remotely manageable)"
77         },
78         { NULL, NULL, NULL, NULL }
79 };
80
81 static struct service_display_info common_unix_svcs[] = {
82   { "cups",          NULL, "Common Unix Printing System","Provides unified printing support for all operating systems" },
83   { "postfix",       NULL, "Internet Mail Service",     "Provides support for sending and receiving electonic mail" },
84   { "sendmail",      NULL, "Internet Mail Service",     "Provides support for sending and receiving electonic mail" },
85   { "portmap",       NULL, "TCP Port to RPC PortMapper",NULL },
86   { "xinetd",        NULL, "Internet Meta-Daemon",      NULL },
87   { "inet",          NULL, "Internet Meta-Daemon",      NULL },
88   { "xntpd",         NULL, "Network Time Service",      NULL },
89   { "ntpd",          NULL, "Network Time Service",      NULL },
90   { "lpd",           NULL, "BSD Print Spooler",         NULL },
91   { "nfsserver",     NULL, "Network File Service",      NULL },
92   { "cron",          NULL, "Scheduling Service",        NULL },
93   { "at",            NULL, "Scheduling Service",        NULL },
94   { "nscd",          NULL, "Name Service Cache Daemon", NULL },
95   { "slapd",         NULL, "LDAP Directory Service",    NULL },
96   { "ldap",          NULL, "LDAP DIrectory Service",    NULL },
97   { "ypbind",        NULL, "NIS Directory Service",     NULL },
98   { "courier-imap",  NULL, "IMAP4 Mail Service",        NULL },
99   { "courier-pop3",  NULL, "POP3 Mail Service",         NULL },
100   { "named",         NULL, "Domain Name Service",       NULL },
101   { "bind",          NULL, "Domain Name Service",       NULL },
102   { "httpd",         NULL, "HTTP Server",               NULL },
103   { "apache",        NULL, "HTTP Server",               "Provides s highly scalable and flexible web server "
104                                                         "capable of implementing various protocols incluing "
105                                                         "but not limited to HTTP" },
106   { "autofs",        NULL, "Automounter",               NULL },
107   { "squid",         NULL, "Web Cache Proxy ",          NULL },
108   { "perfcountd",    NULL, "Performance Monitoring Daemon", NULL },
109   { "pgsql",         NULL, "PgSQL Database Server",     "Provides service for SQL database from Postgresql.org" },
110   { "arpwatch",      NULL, "ARP Tables watcher",        "Provides service for monitoring ARP tables for changes" },
111   { "dhcpd",         NULL, "DHCP Server",               "Provides service for dynamic host configuration and IP assignment" },
112   { "nwserv",        NULL, "NetWare Server Emulator",   "Provides service for emulating Novell NetWare 3.12 server" },
113   { "proftpd",       NULL, "Professional FTP Server",   "Provides high configurable service for FTP connection and "
114                                                         "file transferring" },
115   { "ssh2",          NULL, "SSH Secure Shell",          "Provides service for secure connection for remote administration" },
116   { "sshd",          NULL, "SSH Secure Shell",          "Provides service for secure connection for remote administration" },
117   { NULL, NULL, NULL, NULL }
118 };
119
120 /********************************************************************
121  This is where we do the dirty work of filling in things like the
122  Display name, Description, etc...
123 ********************************************************************/
124 static char *svcctl_get_common_service_dispname(TALLOC_CTX *mem_ctx,
125                                                 const char *servicename)
126 {
127         uint32_t i;
128
129         for (i = 0; common_unix_svcs[i].servicename; i++) {
130                 if (strequal(servicename, common_unix_svcs[i].servicename)) {
131                         char *dispname;
132                         dispname = talloc_asprintf(mem_ctx, "%s (%s)",
133                                         common_unix_svcs[i].dispname,
134                                         common_unix_svcs[i].servicename);
135                         if (dispname == NULL) {
136                                 return NULL;
137                         }
138                         return dispname;
139                 }
140         }
141
142         return talloc_strdup(mem_ctx, servicename);
143 }
144
145 /********************************************************************
146 ********************************************************************/
147 static char *svcctl_cleanup_string(TALLOC_CTX *mem_ctx,
148                                    const char *string)
149 {
150         char *clean = NULL;
151         char *begin, *end;
152
153         clean = talloc_strdup(mem_ctx, string);
154         if (clean == NULL) {
155                 return NULL;
156         }
157         begin = clean;
158
159         /* trim any beginning whilespace */
160         while (isspace(*begin)) {
161                 begin++;
162         }
163
164         if (*begin == '\0') {
165                 return NULL;
166         }
167
168         /* trim any trailing whitespace or carriage returns.
169            Start at the end and move backwards */
170
171         end = begin + strlen(begin) - 1;
172
173         while (isspace(*end) || *end=='\n' || *end=='\r') {
174                 *end = '\0';
175                 end--;
176         }
177
178         return begin;
179 }
180
181 /********************************************************************
182 ********************************************************************/
183 static bool read_init_file(TALLOC_CTX *mem_ctx,
184                            const char *servicename,
185                            struct rcinit_file_information **service_info)
186 {
187         struct rcinit_file_information *info = NULL;
188         char *filepath = NULL;
189         char str[1024];
190         FILE *f = NULL;
191         char *p = NULL;
192
193         info = talloc_zero(mem_ctx, struct rcinit_file_information);
194         if (info == NULL) {
195                 return false;
196         }
197
198         /* attempt the file open */
199
200         filepath = talloc_asprintf(mem_ctx,
201                                    "%s/%s/%s",
202                                    get_dyn_MODULESDIR(),
203                                    SVCCTL_SCRIPT_DIR,
204                                    servicename);
205         if (filepath == NULL) {
206                 return false;
207         }
208         f = fopen( filepath, "r" );
209         if (f == NULL) {
210                 DEBUG(0,("read_init_file: failed to open [%s]\n", filepath));
211                 return false;
212         }
213
214         while ((fgets(str, sizeof(str) - 1, f)) != NULL) {
215                 /* ignore everything that is not a full line
216                    comment starting with a '#' */
217
218                 if (str[0] != '#') {
219                         continue;
220                 }
221
222                 /* Look for a line like '^#.*Description:' */
223
224                 p = strstr(str, "Description:");
225                 if (p != NULL) {
226                         char *desc;
227                         size_t len = strlen(p);
228
229                         if (len <= 12) {
230                                 break;
231                         }
232
233                         desc = svcctl_cleanup_string(mem_ctx, p + 12);
234                         if (desc != NULL) {
235                                 info->description = talloc_strdup(info, desc);
236                         }
237                 }
238         }
239
240         fclose(f);
241
242         if (info->description == NULL) {
243                 info->description = talloc_strdup(info,
244                                                   "External Unix Service");
245                 if (info->description == NULL) {
246                         return false;
247                 }
248         }
249
250         *service_info = info;
251
252         return true;
253 }
254
255 static bool svcctl_add_service(TALLOC_CTX *mem_ctx,
256                                struct dcerpc_binding_handle *h,
257                                struct policy_handle *hive_hnd,
258                                const char *key,
259                                uint32_t access_mask,
260                                const char *name)
261 {
262         enum winreg_CreateAction action = REG_ACTION_NONE;
263         struct security_descriptor *sd = NULL;
264         struct policy_handle key_hnd;
265         struct winreg_String wkey;
266         struct winreg_String wkeyclass;
267         char *description = NULL;
268         char *dname = NULL;
269         char *ipath = NULL;
270         bool ok = false;
271         uint32_t i;
272         NTSTATUS status;
273         WERROR result = WERR_OK;
274
275         ZERO_STRUCT(key_hnd);
276
277         ZERO_STRUCT(wkey);
278         wkey.name = talloc_asprintf(mem_ctx, "%s\\%s", key, name);
279         if (wkey.name == NULL) {
280                 goto done;
281         }
282
283         ZERO_STRUCT(wkeyclass);
284         wkeyclass.name = "";
285
286         status = dcerpc_winreg_CreateKey(h,
287                                          mem_ctx,
288                                          hive_hnd,
289                                          wkey,
290                                          wkeyclass,
291                                          0,
292                                          access_mask,
293                                          NULL,
294                                          &key_hnd,
295                                          &action,
296                                          &result);
297         if (!NT_STATUS_IS_OK(status)) {
298                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
299                         wkey.name, nt_errstr(status)));
300                 goto done;
301         }
302         if (!W_ERROR_IS_OK(result)) {
303                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
304                         wkey.name, win_errstr(result)));
305                 goto done;
306         }
307
308         /* These values are hardcoded in all QueryServiceConfig() replies.
309            I'm just storing them here for cosmetic purposes */
310         status = dcerpc_winreg_set_dword(mem_ctx,
311                                          h,
312                                          &key_hnd,
313                                          "Start",
314                                          SVCCTL_AUTO_START,
315                                          &result);
316         if (!NT_STATUS_IS_OK(status)) {
317                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
318                           nt_errstr(status)));
319                 goto done;
320         }
321         if (!W_ERROR_IS_OK(result)) {
322                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
323                           win_errstr(result)));
324                 goto done;
325         }
326
327         status = dcerpc_winreg_set_dword(mem_ctx,
328                                          h,
329                                          &key_hnd,
330                                          "Type",
331                                          SERVICE_TYPE_WIN32_OWN_PROCESS,
332                                          &result);
333         if (!NT_STATUS_IS_OK(status)) {
334                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
335                           nt_errstr(status)));
336                 goto done;
337         }
338         if (!W_ERROR_IS_OK(result)) {
339                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
340                           win_errstr(result)));
341                 goto done;
342         }
343
344         status = dcerpc_winreg_set_dword(mem_ctx,
345                                          h,
346                                          &key_hnd,
347                                          "ErrorControl",
348                                          SVCCTL_SVC_ERROR_NORMAL,
349                                          &result);
350         if (!NT_STATUS_IS_OK(status)) {
351                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
352                           nt_errstr(status)));
353                 goto done;
354         }
355         if (!W_ERROR_IS_OK(result)) {
356                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
357                           win_errstr(result)));
358                 goto done;
359         }
360
361         status = dcerpc_winreg_set_sz(mem_ctx,
362                                       h,
363                                       &key_hnd,
364                                       "ObjectName",
365                                       "LocalSystem",
366                                       &result);
367         if (!NT_STATUS_IS_OK(status)) {
368                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
369                           nt_errstr(status)));
370                 goto done;
371         }
372         if (!W_ERROR_IS_OK(result)) {
373                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
374                           win_errstr(result)));
375                 goto done;
376         }
377
378         /*
379          * Special considerations for internal services and the DisplayName
380          * value.
381          */
382         for (i = 0; builtin_svcs[i].servicename; i++) {
383                 if (strequal(name, builtin_svcs[i].servicename)) {
384                         ipath = talloc_asprintf(mem_ctx,
385                                                 "%s/%s/%s",
386                                                 get_dyn_MODULESDIR(),
387                                                 SVCCTL_SCRIPT_DIR,
388                                                 builtin_svcs[i].daemon);
389                         description = talloc_strdup(mem_ctx, builtin_svcs[i].description);
390                         dname = talloc_strdup(mem_ctx, builtin_svcs[i].dispname);
391                         break;
392                 }
393         }
394
395         /* Default to an external service if we haven't found a match */
396         if (builtin_svcs[i].servicename == NULL) {
397                 struct rcinit_file_information *init_info = NULL;
398                 char *dispname = NULL;
399
400                 ipath = talloc_asprintf(mem_ctx,
401                                         "%s/%s/%s",
402                                         get_dyn_MODULESDIR(),
403                                         SVCCTL_SCRIPT_DIR,
404                                         name);
405
406                 /* lookup common unix display names */
407                 dispname = svcctl_get_common_service_dispname(mem_ctx, name);
408                 dname = talloc_strdup(mem_ctx, dispname ? dispname : "");
409
410                 /* get info from init file itself */
411                 if (read_init_file(mem_ctx, name, &init_info)) {
412                         description = talloc_strdup(mem_ctx,
413                                                     init_info->description);
414                 } else {
415                         description = talloc_strdup(mem_ctx,
416                                                     "External Unix Service");
417                 }
418         }
419
420         if (ipath == NULL || dname == NULL || description == NULL) {
421                 goto done;
422         }
423
424         status = dcerpc_winreg_set_sz(mem_ctx,
425                                       h,
426                                       &key_hnd,
427                                       "DisplayName",
428                                       dname,
429                                       &result);
430         if (!NT_STATUS_IS_OK(status)) {
431                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
432                           nt_errstr(status)));
433                 goto done;
434         }
435         if (!W_ERROR_IS_OK(result)) {
436                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
437                           win_errstr(result)));
438                 goto done;
439         }
440
441         status = dcerpc_winreg_set_sz(mem_ctx,
442                                       h,
443                                       &key_hnd,
444                                       "ImagePath",
445                                       ipath,
446                                       &result);
447         if (!NT_STATUS_IS_OK(status)) {
448                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
449                           nt_errstr(status)));
450                 goto done;
451         }
452         if (!W_ERROR_IS_OK(result)) {
453                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
454                           win_errstr(result)));
455                 goto done;
456         }
457
458         status = dcerpc_winreg_set_sz(mem_ctx,
459                                       h,
460                                       &key_hnd,
461                                       "Description",
462                                       description,
463                                       &result);
464         if (!NT_STATUS_IS_OK(status)) {
465                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
466                           nt_errstr(status)));
467                 goto done;
468         }
469         if (!W_ERROR_IS_OK(result)) {
470                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
471                           win_errstr(result)));
472                 goto done;
473         }
474
475         sd = svcctl_gen_service_sd(mem_ctx);
476         if (sd == NULL) {
477                 DEBUG(0, ("add_new_svc_name: Failed to create default "
478                           "sec_desc!\n"));
479                 goto done;
480         }
481
482         if (is_valid_policy_hnd(&key_hnd)) {
483                 dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &result);
484         }
485         ZERO_STRUCT(key_hnd);
486
487         ZERO_STRUCT(wkey);
488         wkey.name = talloc_asprintf(mem_ctx, "%s\\%s\\Security", key, name);
489         if (wkey.name == NULL) {
490                 result = WERR_NOT_ENOUGH_MEMORY;
491                 goto done;
492         }
493
494         ZERO_STRUCT(wkeyclass);
495         wkeyclass.name = "";
496
497         status = dcerpc_winreg_CreateKey(h,
498                                          mem_ctx,
499                                          hive_hnd,
500                                          wkey,
501                                          wkeyclass,
502                                          0,
503                                          access_mask,
504                                          NULL,
505                                          &key_hnd,
506                                          &action,
507                                          &result);
508         if (!NT_STATUS_IS_OK(status)) {
509                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
510                         wkey.name, nt_errstr(status)));
511                 goto done;
512         }
513         if (!W_ERROR_IS_OK(result)) {
514                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
515                         wkey.name, win_errstr(result)));
516                 goto done;
517         }
518
519         status = dcerpc_winreg_set_sd(mem_ctx,
520                                       h,
521                                       &key_hnd,
522                                       "Security",
523                                       sd,
524                                       &result);
525         if (!NT_STATUS_IS_OK(status)) {
526                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
527                           nt_errstr(status)));
528                 goto done;
529         }
530         if (!W_ERROR_IS_OK(result)) {
531                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
532                           win_errstr(result)));
533                 goto done;
534         }
535
536         ok = true;
537 done:
538         if (is_valid_policy_hnd(&key_hnd)) {
539                 dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &result);
540         }
541
542         return ok;
543 }
544
545 bool svcctl_init_winreg(struct messaging_context *msg_ctx)
546 {
547         struct dcerpc_binding_handle *h = NULL;
548         uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
549         struct policy_handle hive_hnd, key_hnd;
550         const char **service_list = lp_svcctl_list();
551         const char **subkeys = NULL;
552         uint32_t num_subkeys = 0;
553         char *key = NULL;
554         uint32_t i;
555         NTSTATUS status;
556         WERROR result = WERR_OK;
557         bool ok = false;
558         TALLOC_CTX *tmp_ctx;
559
560         tmp_ctx = talloc_stackframe();
561         if (tmp_ctx == NULL) {
562                 return false;
563         }
564
565         DEBUG(3, ("Initialise the svcctl registry keys if needed.\n"));
566
567         ZERO_STRUCT(hive_hnd);
568         ZERO_STRUCT(key_hnd);
569
570         key = talloc_strdup(tmp_ctx, TOP_LEVEL_SERVICES_KEY);
571         if (key == NULL) {
572                 goto done;
573         }
574
575         result = regdb_open();
576         if (!W_ERROR_IS_OK(result)) {
577                 DEBUG(10, ("regdb_open failed: %s\n",
578                            win_errstr(result)));
579                 goto done;
580         }
581         result = regdb_transaction_start();
582         if (!W_ERROR_IS_OK(result)) {
583                 DEBUG(10, ("regdb_transaction_start failed: %s\n",
584                            win_errstr(result)));
585                 goto done;
586         }
587
588         status = dcerpc_winreg_int_hklm_openkey(tmp_ctx,
589                                                 get_session_info_system(),
590                                                 msg_ctx,
591                                                 &h,
592                                                 key,
593                                                 false,
594                                                 access_mask,
595                                                 &hive_hnd,
596                                                 &key_hnd,
597                                                 &result);
598         if (!NT_STATUS_IS_OK(status)) {
599                 DEBUG(0, ("svcctl_init_winreg: Could not open %s - %s\n",
600                           key, nt_errstr(status)));
601                 goto done;
602         }
603         if (!W_ERROR_IS_OK(result)) {
604                 DEBUG(0, ("svcctl_init_winreg: Could not open %s - %s\n",
605                           key, win_errstr(result)));
606                 goto done;
607         }
608
609         /* get all subkeys */
610         status = dcerpc_winreg_enum_keys(tmp_ctx,
611                                          h,
612                                          &key_hnd,
613                                          &num_subkeys,
614                                          &subkeys,
615                                          &result);
616         if (!NT_STATUS_IS_OK(status)) {
617                 DEBUG(0, ("svcctl_init_winreg: Could enum keys at %s - %s\n",
618                           key, nt_errstr(status)));
619                 goto done;
620         }
621         if (!W_ERROR_IS_OK(result)) {
622                 DEBUG(0, ("svcctl_init_winreg: Could enum keys at %s - %s\n",
623                           key, win_errstr(result)));
624                 goto done;
625         }
626
627         for (i = 0; builtin_svcs[i].servicename != NULL; i++) {
628                 uint32_t j;
629                 bool skip = false;
630
631                 for (j = 0; j < num_subkeys; j++) {
632                         if (strequal(subkeys[i], builtin_svcs[i].servicename)) {
633                                 skip = true;
634                         }
635                 }
636
637                 if (skip) {
638                         continue;
639                 }
640
641                 ok = svcctl_add_service(tmp_ctx,
642                                         h,
643                                         &hive_hnd,
644                                         key,
645                                         access_mask,
646                                         builtin_svcs[i].servicename);
647                 if (!ok) {
648                         goto done;
649                 }
650         }
651
652         for (i = 0; service_list && service_list[i]; i++) {
653                 uint32_t j;
654                 bool skip = false;
655
656                 for (j = 0; j < num_subkeys; j++) {
657                         if (strequal(subkeys[i], service_list[i])) {
658                                 skip = true;
659                         }
660                 }
661
662                 if (skip) {
663                         continue;
664                 }
665
666                 ok = svcctl_add_service(tmp_ctx,
667                                         h,
668                                         &hive_hnd,
669                                         key,
670                                         access_mask,
671                                         service_list[i]);
672                 if (is_valid_policy_hnd(&key_hnd)) {
673                         dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
674                 }
675                 ZERO_STRUCT(key_hnd);
676
677                 if (!ok) {
678                         goto done;
679                 }
680         }
681
682 done:
683         if (is_valid_policy_hnd(&key_hnd)) {
684                 dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
685         }
686
687         if (ok) {
688                 result = regdb_transaction_commit();
689                 if (!W_ERROR_IS_OK(result)) {
690                         DEBUG(10, ("regdb_transaction_commit failed: %s\n",
691                                    win_errstr(result)));
692                 }
693         } else {
694                 result = regdb_transaction_cancel();
695                 if (!W_ERROR_IS_OK(result)) {
696                         DEBUG(10, ("regdb_transaction_cancel failed: %s\n",
697                                    win_errstr(result)));
698                 }
699         }
700         regdb_close();
701         talloc_free(tmp_ctx);
702         return ok;
703 }
704
705 /* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */