python:tests: Store keys as bytes rather than as lists of ints
[samba.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         XFILE *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 = x_fopen( filepath, O_RDONLY, 0 );
209         if (f == NULL) {
210                 DEBUG(0,("read_init_file: failed to open [%s]\n", filepath));
211                 return false;
212         }
213
214         while ((x_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
228                         p += strlen( "Description:" ) + 1;
229                         if (p == NULL) {
230                                 break;
231                         }
232
233                         desc = svcctl_cleanup_string(mem_ctx, p);
234                         if (desc != NULL) {
235                                 info->description = talloc_strdup(info, desc);
236                         }
237                 }
238         }
239
240         x_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         if (ipath == NULL || dname == NULL || description == NULL) {
396                 goto done;
397         }
398
399         /* Default to an external service if we haven't found a match */
400         if (builtin_svcs[i].servicename == NULL) {
401                 struct rcinit_file_information *init_info = NULL;
402                 char *dispname = NULL;
403
404                 ipath = talloc_asprintf(mem_ctx,
405                                         "%s/%s/%s",
406                                         get_dyn_MODULESDIR(),
407                                         SVCCTL_SCRIPT_DIR,
408                                         name);
409
410                 /* lookup common unix display names */
411                 dispname = svcctl_get_common_service_dispname(mem_ctx, name);
412                 dname = talloc_strdup(mem_ctx, dispname ? dispname : "");
413
414                 /* get info from init file itself */
415                 if (read_init_file(mem_ctx, name, &init_info)) {
416                         description = talloc_strdup(mem_ctx,
417                                                     init_info->description);
418                 } else {
419                         description = talloc_strdup(mem_ctx,
420                                                     "External Unix Service");
421                 }
422         }
423
424         if (ipath == NULL || dname == NULL || description == NULL) {
425                 goto done;
426         }
427
428         status = dcerpc_winreg_set_sz(mem_ctx,
429                                       h,
430                                       &key_hnd,
431                                       "DisplayName",
432                                       dname,
433                                       &result);
434         if (!NT_STATUS_IS_OK(status)) {
435                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
436                           nt_errstr(status)));
437                 goto done;
438         }
439         if (!W_ERROR_IS_OK(result)) {
440                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
441                           win_errstr(result)));
442                 goto done;
443         }
444
445         status = dcerpc_winreg_set_sz(mem_ctx,
446                                       h,
447                                       &key_hnd,
448                                       "ImagePath",
449                                       ipath,
450                                       &result);
451         if (!NT_STATUS_IS_OK(status)) {
452                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
453                           nt_errstr(status)));
454                 goto done;
455         }
456         if (!W_ERROR_IS_OK(result)) {
457                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
458                           win_errstr(result)));
459                 goto done;
460         }
461
462         status = dcerpc_winreg_set_sz(mem_ctx,
463                                       h,
464                                       &key_hnd,
465                                       "Description",
466                                       description,
467                                       &result);
468         if (!NT_STATUS_IS_OK(status)) {
469                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
470                           nt_errstr(status)));
471                 goto done;
472         }
473         if (!W_ERROR_IS_OK(result)) {
474                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
475                           win_errstr(result)));
476                 goto done;
477         }
478
479         sd = svcctl_gen_service_sd(mem_ctx);
480         if (sd == NULL) {
481                 DEBUG(0, ("add_new_svc_name: Failed to create default "
482                           "sec_desc!\n"));
483                 goto done;
484         }
485
486         if (is_valid_policy_hnd(&key_hnd)) {
487                 dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &result);
488         }
489         ZERO_STRUCT(key_hnd);
490
491         ZERO_STRUCT(wkey);
492         wkey.name = talloc_asprintf(mem_ctx, "%s\\%s\\Security", key, name);
493         if (wkey.name == NULL) {
494                 result = WERR_NOMEM;
495                 goto done;
496         }
497
498         ZERO_STRUCT(wkeyclass);
499         wkeyclass.name = "";
500
501         status = dcerpc_winreg_CreateKey(h,
502                                          mem_ctx,
503                                          hive_hnd,
504                                          wkey,
505                                          wkeyclass,
506                                          0,
507                                          access_mask,
508                                          NULL,
509                                          &key_hnd,
510                                          &action,
511                                          &result);
512         if (!NT_STATUS_IS_OK(status)) {
513                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
514                         wkey.name, nt_errstr(status)));
515                 goto done;
516         }
517         if (!W_ERROR_IS_OK(result)) {
518                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
519                         wkey.name, win_errstr(result)));
520                 goto done;
521         }
522
523         status = dcerpc_winreg_set_sd(mem_ctx,
524                                       h,
525                                       &key_hnd,
526                                       "Security",
527                                       sd,
528                                       &result);
529         if (!NT_STATUS_IS_OK(status)) {
530                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
531                           nt_errstr(status)));
532                 goto done;
533         }
534         if (!W_ERROR_IS_OK(result)) {
535                 DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
536                           win_errstr(result)));
537                 goto done;
538         }
539
540         ok = true;
541 done:
542         if (is_valid_policy_hnd(&key_hnd)) {
543                 dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &result);
544         }
545
546         return ok;
547 }
548
549 bool svcctl_init_winreg(struct messaging_context *msg_ctx)
550 {
551         struct dcerpc_binding_handle *h = NULL;
552         uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
553         struct policy_handle hive_hnd, key_hnd;
554         const char **service_list = lp_svcctl_list();
555         const char **subkeys = NULL;
556         uint32_t num_subkeys = 0;
557         char *key = NULL;
558         uint32_t i;
559         NTSTATUS status;
560         WERROR result = WERR_OK;
561         bool ok = false;
562         TALLOC_CTX *tmp_ctx;
563
564         tmp_ctx = talloc_stackframe();
565         if (tmp_ctx == NULL) {
566                 return false;
567         }
568
569         DEBUG(3, ("Initialise the svcctl registry keys if needed.\n"));
570
571         ZERO_STRUCT(hive_hnd);
572         ZERO_STRUCT(key_hnd);
573
574         key = talloc_strdup(tmp_ctx, TOP_LEVEL_SERVICES_KEY);
575         if (key == NULL) {
576                 goto done;
577         }
578
579         result = regdb_open();
580         if (!W_ERROR_IS_OK(result)) {
581                 DEBUG(10, ("regdb_open failed: %s\n",
582                            win_errstr(result)));
583                 goto done;
584         }
585         result = regdb_transaction_start();
586         if (!W_ERROR_IS_OK(result)) {
587                 DEBUG(10, ("regdb_transaction_start failed: %s\n",
588                            win_errstr(result)));
589                 goto done;
590         }
591
592         status = dcerpc_winreg_int_hklm_openkey(tmp_ctx,
593                                                 get_session_info_system(),
594                                                 msg_ctx,
595                                                 &h,
596                                                 key,
597                                                 false,
598                                                 access_mask,
599                                                 &hive_hnd,
600                                                 &key_hnd,
601                                                 &result);
602         if (!NT_STATUS_IS_OK(status)) {
603                 DEBUG(0, ("svcctl_init_winreg: Could not open %s - %s\n",
604                           key, nt_errstr(status)));
605                 goto done;
606         }
607         if (!W_ERROR_IS_OK(result)) {
608                 DEBUG(0, ("svcctl_init_winreg: Could not open %s - %s\n",
609                           key, win_errstr(result)));
610                 goto done;
611         }
612
613         /* get all subkeys */
614         status = dcerpc_winreg_enum_keys(tmp_ctx,
615                                          h,
616                                          &key_hnd,
617                                          &num_subkeys,
618                                          &subkeys,
619                                          &result);
620         if (!NT_STATUS_IS_OK(status)) {
621                 DEBUG(0, ("svcctl_init_winreg: Could enum keys at %s - %s\n",
622                           key, nt_errstr(status)));
623                 goto done;
624         }
625         if (!W_ERROR_IS_OK(result)) {
626                 DEBUG(0, ("svcctl_init_winreg: Could enum keys at %s - %s\n",
627                           key, win_errstr(result)));
628                 goto done;
629         }
630
631         for (i = 0; builtin_svcs[i].servicename != NULL; i++) {
632                 uint32_t j;
633                 bool skip = false;
634
635                 for (j = 0; j < num_subkeys; j++) {
636                         if (strequal(subkeys[i], builtin_svcs[i].servicename)) {
637                                 skip = true;
638                         }
639                 }
640
641                 if (skip) {
642                         continue;
643                 }
644
645                 ok = svcctl_add_service(tmp_ctx,
646                                         h,
647                                         &hive_hnd,
648                                         key,
649                                         access_mask,
650                                         builtin_svcs[i].servicename);
651                 if (!ok) {
652                         goto done;
653                 }
654         }
655
656         for (i = 0; service_list && service_list[i]; i++) {
657                 uint32_t j;
658                 bool skip = false;
659
660                 for (j = 0; j < num_subkeys; j++) {
661                         if (strequal(subkeys[i], service_list[i])) {
662                                 skip = true;
663                         }
664                 }
665
666                 if (skip) {
667                         continue;
668                 }
669
670                 ok = svcctl_add_service(tmp_ctx,
671                                         h,
672                                         &hive_hnd,
673                                         key,
674                                         access_mask,
675                                         service_list[i]);
676                 if (is_valid_policy_hnd(&key_hnd)) {
677                         dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
678                 }
679                 ZERO_STRUCT(key_hnd);
680
681                 if (!ok) {
682                         goto done;
683                 }
684         }
685
686 done:
687         if (is_valid_policy_hnd(&key_hnd)) {
688                 dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
689         }
690
691         if (ok) {
692                 result = regdb_transaction_commit();
693                 if (!W_ERROR_IS_OK(result)) {
694                         DEBUG(10, ("regdb_transaction_commit failed: %s\n",
695                                    win_errstr(result)));
696                 }
697         } else {
698                 result = regdb_transaction_cancel();
699                 if (!W_ERROR_IS_OK(result)) {
700                         DEBUG(10, ("regdb_transaction_cancel failed: %s\n",
701                                    win_errstr(result)));
702                 }
703         }
704         regdb_close();
705         talloc_free(tmp_ctx);
706         return ok;
707 }
708
709 /* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */