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