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