1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Service Control Services (SVCCTL) RPC interface definition.
28  * This interface provides remote access to list SMF services
29  * from a Windows client.
30  *
31  * SVCCTL access is restricted to administrators: members of the
32  * Domain Admins or Administrators groups.
33  */
34 
35 #include <stdio.h>
36 #include <strings.h>
37 #include <smbsrv/ntstatus.h>
38 #include <smbsrv/nmpipes.h>
39 #include "svcctl_scm.h"
40 
41 #define	SVCCTL_OPENSVC_OP_UNIMPLEMENTED(S)	\
42 	((S) & SERVICE_CHANGE_CONFIG)	||	\
43 	((S) & SERVICE_PAUSE_CONTINUE)	||	\
44 	((S) & SERVICE_START)		||	\
45 	((S) & SERVICE_STOP)		||	\
46 	((S) & SERVICE_ENUMERATE_DEPENDENTS)
47 
48 static int svcctl_s_Close(void *, ndr_xa_t *);
49 static int svcctl_s_OpenManager(void *, ndr_xa_t *);
50 static int svcctl_s_OpenService(void *, ndr_xa_t *);
51 static int svcctl_s_QueryServiceStatus(void *, ndr_xa_t *);
52 static int svcctl_s_QueryServiceConfig(void *, ndr_xa_t *);
53 static int svcctl_s_EnumServicesStatus(void *, ndr_xa_t *);
54 static int svcctl_s_GetServiceDisplayNameW(void *, ndr_xa_t *);
55 static int svcctl_s_GetServiceKeyNameW(void *, ndr_xa_t *);
56 static int svcctl_s_QueryServiceConfig2W(void *, ndr_xa_t *);
57 
58 static ndr_stub_table_t svcctl_stub_table[] = {
59 	{ svcctl_s_Close,		SVCCTL_OPNUM_Close },
60 	{ svcctl_s_OpenManager,		SVCCTL_OPNUM_OpenManager },
61 	{ svcctl_s_OpenService,		SVCCTL_OPNUM_OpenService },
62 	{ svcctl_s_QueryServiceStatus,	SVCCTL_OPNUM_QueryServiceStatus },
63 	{ svcctl_s_QueryServiceConfig,	SVCCTL_OPNUM_QueryServiceConfig },
64 	{ svcctl_s_EnumServicesStatus,	SVCCTL_OPNUM_EnumServicesStatus },
65 	{ svcctl_s_GetServiceDisplayNameW,
66 		SVCCTL_OPNUM_GetServiceDisplayNameW },
67 	{ svcctl_s_GetServiceKeyNameW,	SVCCTL_OPNUM_GetServiceKeyNameW },
68 	{ svcctl_s_QueryServiceConfig2W, SVCCTL_OPNUM_QueryServiceConfig2W },
69 	{0}
70 };
71 
72 static ndr_service_t svcctl_service = {
73 	"SVCCTL",			/* name */
74 	"Service Control Services",	/* desc */
75 	"\\svcctl",			/* endpoint */
76 	PIPE_NTSVCS,			/* sec_addr_port */
77 	"367abb81-9844-35f1-ad32-98f038001003", 2,	/* abstract */
78 	NDR_TRANSFER_SYNTAX_UUID,		2,	/* transfer */
79 	0,				/* no bind_instance_size */
80 	0,				/* no bind_req() */
81 	0,				/* no unbind_and_close() */
82 	0,				/* use generic_call_stub() */
83 	&TYPEINFO(svcctl_interface),	/* interface ti */
84 	svcctl_stub_table		/* stub_table */
85 };
86 
87 /*
88  * svcctl_initialize
89  *
90  * This function registers the SVCCTL RPC interface with the RPC runtime
91  * library. It must be called in order to use either the client side
92  * or the server side functions.
93  */
94 void
95 svcctl_initialize(void)
96 {
97 	(void) ndr_svc_register(&svcctl_service);
98 }
99 
100 /*
101  * svcctl_hdlookup
102  *
103  * Handle lookup wrapper to validate the local service and/or manager context.
104  */
105 static ndr_handle_t *
106 svcctl_hdlookup(ndr_xa_t *mxa, ndr_hdid_t *id, svcctl_context_type_t type)
107 {
108 	ndr_handle_t *hd;
109 	svcctl_context_t *ctx;
110 
111 	if ((hd = ndr_hdlookup(mxa, id)) == NULL)
112 		return (NULL);
113 
114 	if ((ctx = (svcctl_context_t *)hd->nh_data) == NULL)
115 		return (NULL);
116 
117 	if ((ctx->c_type != type) || (ctx->c_ctx.uc_cp == NULL))
118 		return (NULL);
119 
120 	return (hd);
121 }
122 
123 /*
124  * svcctl_hdfree
125  *
126  * Handle deallocation wrapper to free the local service and/or manager context.
127  */
128 static void
129 svcctl_hdfree(ndr_xa_t *mxa, ndr_hdid_t *id)
130 {
131 	ndr_handle_t *hd;
132 	svcctl_context_t *ctx;
133 	svcctl_manager_context_t *mgr_ctx;
134 	svcctl_service_context_t *svc_ctx;
135 
136 	if ((hd = ndr_hdlookup(mxa, id)) != NULL) {
137 		ctx = (svcctl_context_t *)hd->nh_data;
138 
139 		switch (ctx->c_type) {
140 		case SVCCTL_MANAGER_CONTEXT:
141 			mgr_ctx = ctx->c_ctx.uc_mgr;
142 			svcctl_scm_fini(mgr_ctx);
143 			svcctl_scm_scf_handle_fini(mgr_ctx);
144 			free(mgr_ctx);
145 			break;
146 
147 		case SVCCTL_SERVICE_CONTEXT:
148 			svc_ctx = ctx->c_ctx.uc_svc;
149 			free(svc_ctx->sc_mgrid);
150 			free(svc_ctx->sc_svcname);
151 			free(svc_ctx);
152 			break;
153 
154 		default:
155 			break;
156 		}
157 
158 		free(ctx);
159 		ndr_hdfree(mxa, id);
160 	}
161 }
162 
163 /*
164  * svcctl_mgr_hdalloc
165  *
166  * Handle allocation wrapper to setup the local manager context.
167  */
168 static ndr_hdid_t *
169 svcctl_mgr_hdalloc(ndr_xa_t *mxa)
170 {
171 	svcctl_context_t *ctx;
172 	svcctl_manager_context_t *mgr_ctx;
173 
174 	if ((ctx = malloc(sizeof (svcctl_context_t))) == NULL)
175 		return (NULL);
176 	ctx->c_type = SVCCTL_MANAGER_CONTEXT;
177 
178 	if ((mgr_ctx = malloc(sizeof (svcctl_manager_context_t))) == NULL) {
179 		free(ctx);
180 		return (NULL);
181 	}
182 	bzero(mgr_ctx, sizeof (svcctl_manager_context_t));
183 
184 	if (svcctl_scm_scf_handle_init(mgr_ctx) < 0) {
185 		free(mgr_ctx);
186 		free(ctx);
187 		return (NULL);
188 	}
189 
190 	if (svcctl_scm_init(mgr_ctx) < 0) {
191 		svcctl_scm_scf_handle_fini(mgr_ctx);
192 		free(mgr_ctx);
193 		free(ctx);
194 		return (NULL);
195 	}
196 
197 	ctx->c_ctx.uc_mgr = mgr_ctx;
198 
199 	return (ndr_hdalloc(mxa, ctx));
200 }
201 
202 /*
203  * svcctl_get_mgr_ctx
204  *
205  * This function looks up a reference to local manager context.
206  */
207 static svcctl_manager_context_t *
208 svcctl_get_mgr_ctx(ndr_xa_t *mxa, ndr_hdid_t *mgr_id)
209 {
210 	ndr_handle_t *hd;
211 	svcctl_manager_context_t *mgr_ctx;
212 
213 	hd = svcctl_hdlookup(mxa, mgr_id, SVCCTL_MANAGER_CONTEXT);
214 	if (hd == NULL)
215 		return (NULL);
216 
217 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
218 
219 	return (mgr_ctx);
220 }
221 
222 /*
223  * svcctl_svc_hdalloc
224  *
225  * Handle allocation wrapper to setup the local service context.
226  */
227 static ndr_hdid_t *
228 svcctl_svc_hdalloc(ndr_xa_t *mxa, ndr_hdid_t *mgr_id, char *svc_name)
229 {
230 	svcctl_context_t *ctx;
231 	svcctl_service_context_t *svc_ctx;
232 	svcctl_manager_context_t *mgr_ctx;
233 	int max_name_sz = 0;
234 	char *svcname;
235 
236 	mgr_ctx = svcctl_get_mgr_ctx(mxa, mgr_id);
237 	if (mgr_ctx == NULL)
238 		return (NULL);
239 	max_name_sz = mgr_ctx->mc_scf_max_fmri_len;
240 
241 	if ((ctx = malloc(sizeof (svcctl_context_t))) == NULL) {
242 		svcctl_hdfree(mxa, mgr_id);
243 		return (NULL);
244 	}
245 	ctx->c_type = SVCCTL_SERVICE_CONTEXT;
246 
247 	if ((svc_ctx = malloc(sizeof (svcctl_service_context_t))) == NULL) {
248 		svcctl_hdfree(mxa, mgr_id);
249 		free(ctx);
250 		return (NULL);
251 	}
252 	bzero(svc_ctx, sizeof (svcctl_service_context_t));
253 
254 	svc_ctx->sc_mgrid = malloc(sizeof (ndr_hdid_t));
255 	svcname = malloc(max_name_sz);
256 
257 	if ((svc_ctx->sc_mgrid == NULL) || (svcname == NULL)) {
258 		free(svc_ctx->sc_mgrid);
259 		free(svc_ctx);
260 		svcctl_hdfree(mxa, mgr_id);
261 		free(ctx);
262 		return (NULL);
263 	}
264 
265 	svc_ctx->sc_svcname = svcname;
266 
267 	bcopy(mgr_id, svc_ctx->sc_mgrid, sizeof (ndr_hdid_t));
268 	(void) strlcpy(svc_ctx->sc_svcname, svc_name, max_name_sz);
269 
270 	ctx->c_ctx.uc_svc = svc_ctx;
271 
272 	return (ndr_hdalloc(mxa, ctx));
273 }
274 
275 /*
276  * svcctl_s_Close
277  *
278  * This is a request to close the SVCCTL interface specified by the
279  * handle. Free the handle and zero out the result handle for the
280  * client.
281  *
282  * Returns:
283  *	ERROR_SUCCESS
284  *	ERROR_INVALID_HANDLE
285  */
286 static int
287 svcctl_s_Close(void *arg, ndr_xa_t *mxa)
288 {
289 	struct svcctl_Close *param = arg;
290 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
291 
292 	svcctl_hdfree(mxa, id);
293 
294 	bzero(&param->result_handle, sizeof (svcctl_handle_t));
295 	param->status = ERROR_SUCCESS;
296 	return (NDR_DRC_OK);
297 }
298 
299 /*
300  * svcctl_s_OpenManager
301  *
302  * Request to open the service control manager.
303  * The caller must have administrator rights in order to open this
304  * interface.  We don't support write (SC_MANAGER_LOCK) access.
305  *
306  * Returns:
307  *	ERROR_SUCCESS
308  *	ERROR_ACCESS_DENIED
309  *
310  * On success, returns a handle for use with subsequent svcctl requests.
311  */
312 static int
313 svcctl_s_OpenManager(void *arg, ndr_xa_t *mxa)
314 {
315 	struct svcctl_OpenManager *param = arg;
316 	ndr_hdid_t *id = NULL;
317 	int rc;
318 
319 	rc = ndr_is_admin(mxa);
320 
321 	if ((rc == 0) || (param->desired_access & SC_MANAGER_LOCK) != 0) {
322 		bzero(&param->handle, sizeof (svcctl_handle_t));
323 		param->status = ERROR_ACCESS_DENIED;
324 		return (NDR_DRC_OK);
325 	}
326 
327 	id = svcctl_mgr_hdalloc(mxa);
328 	if (id) {
329 		bcopy(id, &param->handle, sizeof (svcctl_handle_t));
330 		param->status = ERROR_SUCCESS;
331 	} else {
332 		bzero(&param->handle, sizeof (svcctl_handle_t));
333 		param->status = ERROR_ACCESS_DENIED;
334 	}
335 
336 	return (NDR_DRC_OK);
337 }
338 
339 /*
340  * svcctl_s_OpenService
341  *
342  * Return a handle for use with subsequent svcctl requests.
343  *
344  * Returns:
345  *	ERROR_SUCCESS
346  *	ERROR_INVALID_HANDLE
347  *	ERROR_SERVICE_DOES_NOT_EXIST
348  *	ERROR_CALL_NOT_IMPLEMENTED
349  */
350 static int
351 svcctl_s_OpenService(void *arg, ndr_xa_t *mxa)
352 {
353 	struct svcctl_OpenService *param = arg;
354 	ndr_hdid_t *mgrid = (ndr_hdid_t *)&param->manager_handle;
355 	ndr_hdid_t *id = NULL;
356 	ndr_handle_t *hd;
357 	DWORD status;
358 	svcctl_manager_context_t *mgr_ctx;
359 	char *svc_name = (char *)param->service_name;
360 	boolean_t unimplemented_operations = B_FALSE;
361 
362 	/* Allow service handle allocations for only status & config queries */
363 	unimplemented_operations =
364 	    SVCCTL_OPENSVC_OP_UNIMPLEMENTED(param->desired_access);
365 
366 	if (unimplemented_operations) {
367 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
368 		param->status = ERROR_CALL_NOT_IMPLEMENTED;
369 		return (NDR_DRC_OK);
370 	}
371 
372 	hd = svcctl_hdlookup(mxa, mgrid, SVCCTL_MANAGER_CONTEXT);
373 	if (hd == NULL) {
374 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
375 		param->status = ERROR_INVALID_HANDLE;
376 		return (NDR_DRC_OK);
377 	}
378 
379 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
380 	status = svcctl_scm_validate_service(mgr_ctx, svc_name);
381 	if (status != ERROR_SUCCESS) {
382 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
383 		param->status = status;
384 		return (NDR_DRC_OK);
385 	}
386 
387 	id = svcctl_svc_hdalloc(mxa, mgrid, svc_name);
388 	if (id) {
389 		bcopy(id, &param->service_handle, sizeof (svcctl_handle_t));
390 		param->status = ERROR_SUCCESS;
391 	} else {
392 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
393 		param->status = ERROR_ACCESS_DENIED;
394 	}
395 
396 	return (NDR_DRC_OK);
397 }
398 
399 /*
400  * svcctl_s_QueryServiceStatus
401  *
402  * Returns:
403  *	ERROR_SUCCESS
404  *	ERROR_INVALID_HANDLE
405  */
406 static int
407 svcctl_s_QueryServiceStatus(void *arg, ndr_xa_t *mxa)
408 {
409 	struct svcctl_QueryServiceStatus *param = arg;
410 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
411 	ndr_handle_t *hd;
412 	svcctl_manager_context_t *mgr_ctx;
413 	svcctl_service_context_t *svc_ctx;
414 	svcctl_svc_node_t *svc;
415 
416 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
417 	if (hd == NULL) {
418 		bzero(param, sizeof (struct svcctl_QueryServiceStatus));
419 		param->status = ERROR_INVALID_HANDLE;
420 		return (NDR_DRC_OK);
421 	}
422 
423 	svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
424 	mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
425 	if (mgr_ctx == NULL) {
426 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
427 		param->status = ERROR_INVALID_HANDLE;
428 		return (NDR_DRC_OK);
429 	}
430 
431 	svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
432 	if (svc == NULL || svc->sn_state == NULL) {
433 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
434 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
435 		return (NDR_DRC_OK);
436 	}
437 
438 	param->service_status.service_type = SERVICE_WIN32_SHARE_PROCESS;
439 	param->service_status.cur_state = svcctl_scm_map_status(svc->sn_state);
440 	param->service_status.ctrl_accepted = 0;
441 	param->service_status.w32_exitcode = 0;
442 	param->service_status.svc_specified_exitcode = 0;
443 	param->service_status.check_point = 0;
444 	param->service_status.wait_hint = 0;
445 
446 	param->status = ERROR_SUCCESS;
447 	return (NDR_DRC_OK);
448 }
449 
450 /*
451  * svcctl_s_EnumServicesStatus
452  *
453  * Enumerate the list of services we support. Currently, this list
454  * is built in a fixed-size 1024 byte buffer - be careful not to
455  * over-run the buffer.
456  *
457  * Returns:
458  *	ERROR_SUCCESS
459  *	ERROR_INVALID_HANDLE
460  *	ERROR_NOT_ENOUGH_MEMORY
461  */
462 static int
463 svcctl_s_EnumServicesStatus(void *arg, ndr_xa_t *mxa)
464 {
465 	struct svcctl_EnumServicesStatus *param = arg;
466 	ndr_hdid_t *id = (ndr_hdid_t *)&param->manager_handle;
467 	ndr_handle_t *hd;
468 	int input_bufsize = 0;
469 	svcctl_manager_context_t *mgr_ctx;
470 
471 	hd = svcctl_hdlookup(mxa, id, SVCCTL_MANAGER_CONTEXT);
472 	if (hd == NULL) {
473 		bzero(param, sizeof (struct svcctl_EnumServicesStatus));
474 		param->services = NDR_STRDUP(mxa, "");
475 		param->status = ERROR_INVALID_HANDLE;
476 		return (NDR_DRC_OK);
477 	}
478 
479 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
480 	if (svcctl_scm_refresh(mgr_ctx) != 0) {
481 		bzero(param, sizeof (struct svcctl_EnumServicesStatus));
482 		param->services = NDR_STRDUP(mxa, "");
483 		param->status = ERROR_INVALID_DATA;
484 		return (NDR_DRC_OK);
485 	}
486 
487 	input_bufsize = param->buf_size;
488 	param->services = NDR_MALLOC(mxa, input_bufsize);
489 	if (param->services == NULL) {
490 		bzero(param, sizeof (struct svcctl_EnumServicesStatus));
491 		param->services = NDR_STRDUP(mxa, "");
492 		param->status = ERROR_NOT_ENOUGH_MEMORY;
493 		return (NDR_DRC_OK);
494 	}
495 	bzero(param->services, input_bufsize);
496 
497 	if (input_bufsize <= mgr_ctx->mc_bytes_needed) {
498 		param->bytes_needed = mgr_ctx->mc_bytes_needed;
499 		param->svc_num = 0;
500 		param->resume_handle = 0;
501 		param->status = ERROR_MORE_DATA;
502 		return (NDR_DRC_OK);
503 	}
504 
505 	svcctl_scm_enum_services(mgr_ctx, param->services);
506 
507 	param->buf_size = input_bufsize;
508 	param->bytes_needed = 0;
509 	param->svc_num = mgr_ctx->mc_scf_numsvcs;
510 	param->resume_handle = 0;
511 	param->status = ERROR_SUCCESS;
512 	return (NDR_DRC_OK);
513 }
514 
515 /*
516  * svcctl_s_QueryServiceConfig
517  *
518  * Returns:
519  *	ERROR_SUCCESS
520  *	ERROR_INVALID_HANDLE
521  */
522 static int
523 svcctl_s_QueryServiceConfig(void *arg, ndr_xa_t *mxa)
524 {
525 	struct svcctl_QueryServiceConfig *param = arg;
526 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
527 	ndr_handle_t *hd;
528 	svcctl_manager_context_t *mgr_ctx;
529 	svcctl_service_context_t *svc_ctx;
530 	svcctl_svc_node_t *svc;
531 	int bytes_needed = 0;
532 	svc_config_t *cfg;
533 
534 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
535 	if (hd == NULL) {
536 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
537 		param->status = ERROR_INVALID_HANDLE;
538 		return (NDR_DRC_OK);
539 	}
540 
541 	svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
542 	mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
543 	if (mgr_ctx == NULL) {
544 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
545 		param->status = ERROR_INVALID_HANDLE;
546 		return (NDR_DRC_OK);
547 	}
548 
549 	svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
550 	if (svc == NULL || svc->sn_fmri == NULL) {
551 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
552 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
553 		return (NDR_DRC_OK);
554 	}
555 
556 	cfg = &param->service_cfg;
557 	cfg->service_type = SERVICE_WIN32_SHARE_PROCESS;
558 	cfg->start_type = SERVICE_AUTO_START;
559 	cfg->error_control = SERVICE_AUTO_START;
560 	cfg->binary_pathname = NDR_STRDUP(mxa, "");
561 	cfg->loadorder_group = NDR_STRDUP(mxa, "");
562 	cfg->tag_id = 0;
563 	cfg->dependencies = NDR_STRDUP(mxa, "");
564 	cfg->service_startname = NDR_STRDUP(mxa, "");
565 	cfg->display_name = NDR_STRDUP(mxa, svc->sn_fmri);
566 
567 	bytes_needed = sizeof (svc_config_t);
568 	bytes_needed += SVCCTL_WNSTRLEN((const char *)cfg->binary_pathname);
569 	bytes_needed += SVCCTL_WNSTRLEN((const char *)cfg->loadorder_group);
570 	bytes_needed += SVCCTL_WNSTRLEN((const char *)cfg->dependencies);
571 	bytes_needed += SVCCTL_WNSTRLEN((const char *)cfg->service_startname);
572 	bytes_needed += SVCCTL_WNSTRLEN(svc->sn_fmri);
573 
574 	if (param->buf_size < bytes_needed) {
575 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
576 		param->cfg_bytes = bytes_needed;
577 		param->status = ERROR_MORE_DATA;
578 		return (NDR_DRC_OK);
579 	}
580 
581 	param->cfg_bytes = bytes_needed;
582 	param->status = ERROR_SUCCESS;
583 	return (NDR_DRC_OK);
584 }
585 
586 /*
587  * svcctl_s_GetServiceDisplayNameW
588  *
589  * Returns:
590  *	ERROR_SUCCESS
591  *	ERROR_INVALID_HANDLE
592  *	ERROR_SERVICE_DOES_NOT_EXIST
593  */
594 static int
595 svcctl_s_GetServiceDisplayNameW(void *arg, ndr_xa_t *mxa)
596 {
597 	struct svcctl_GetServiceDisplayNameW *param = arg;
598 	ndr_hdid_t *id = (ndr_hdid_t *)&param->manager_handle;
599 	ndr_handle_t *hd;
600 	svcctl_svc_node_t *svc;
601 	svcctl_manager_context_t *mgr_ctx;
602 
603 	hd = svcctl_hdlookup(mxa, id, SVCCTL_MANAGER_CONTEXT);
604 	if (hd == NULL) {
605 		bzero(param, sizeof (struct svcctl_GetServiceDisplayNameW));
606 		param->display_name = NDR_STRDUP(mxa, "");
607 		param->status = ERROR_INVALID_HANDLE;
608 		return (NDR_DRC_OK);
609 	}
610 
611 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
612 	svc = svcctl_scm_find_service(mgr_ctx, (char *)param->service_name);
613 	if (svc == NULL || svc->sn_fmri == NULL) {
614 		bzero(param, sizeof (struct svcctl_GetServiceDisplayNameW));
615 		param->display_name = NDR_STRDUP(mxa, "");
616 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
617 		return (NDR_DRC_OK);
618 	}
619 
620 	param->display_name = NDR_STRDUP(mxa, svc->sn_fmri);
621 	if (param->display_name == NULL) {
622 		bzero(param, sizeof (struct svcctl_GetServiceDisplayNameW));
623 		param->display_name = NDR_STRDUP(mxa, "");
624 		param->status = ERROR_NOT_ENOUGH_MEMORY;
625 		return (NDR_DRC_OK);
626 	}
627 
628 	param->buf_size = strlen(svc->sn_fmri);
629 	param->status = ERROR_SUCCESS;
630 	return (NDR_DRC_OK);
631 }
632 
633 /*
634  * svcctl_s_GetServiceKeyNameW
635  *
636  * Returns:
637  *	ERROR_SUCCESS
638  *	ERROR_INVALID_HANDLE
639  *	ERROR_SERVICE_DOES_NOT_EXIST
640  */
641 static int
642 svcctl_s_GetServiceKeyNameW(void *arg, ndr_xa_t *mxa)
643 {
644 	struct svcctl_GetServiceKeyNameW *param = arg;
645 	ndr_hdid_t *id = (ndr_hdid_t *)&param->manager_handle;
646 	ndr_handle_t *hd;
647 	svcctl_svc_node_t *svc;
648 	svcctl_manager_context_t *mgr_ctx;
649 
650 	hd = svcctl_hdlookup(mxa, id, SVCCTL_MANAGER_CONTEXT);
651 	if (hd == NULL) {
652 		bzero(param, sizeof (struct svcctl_GetServiceKeyNameW));
653 		param->key_name = NDR_STRDUP(mxa, "");
654 		param->status = ERROR_INVALID_HANDLE;
655 		return (NDR_DRC_OK);
656 	}
657 
658 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
659 	svc = svcctl_scm_find_service(mgr_ctx, (char *)param->service_name);
660 	if (svc == NULL || svc->sn_name == NULL) {
661 		bzero(param, sizeof (struct svcctl_GetServiceKeyNameW));
662 		param->key_name = NDR_STRDUP(mxa, "");
663 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
664 		return (NDR_DRC_OK);
665 	}
666 
667 	param->key_name = NDR_STRDUP(mxa, svc->sn_name);
668 	if (param->key_name == NULL) {
669 		bzero(param, sizeof (struct svcctl_GetServiceKeyNameW));
670 		param->key_name = NDR_STRDUP(mxa, "");
671 		param->status = ERROR_NOT_ENOUGH_MEMORY;
672 		return (NDR_DRC_OK);
673 	}
674 
675 	param->buf_size = strlen(svc->sn_name);
676 	param->status = ERROR_SUCCESS;
677 	return (NDR_DRC_OK);
678 }
679 
680 /*
681  * svcctl_s_QueryServiceConfig2W
682  *
683  * Returns:
684  *	ERROR_SUCCESS
685  *	ERROR_INVALID_HANDLE
686  *	ERROR_INVALID_LEVEL
687  *	ERROR_NOT_ENOUGH_MEMORY
688  */
689 static int
690 svcctl_s_QueryServiceConfig2W(void *arg, ndr_xa_t *mxa)
691 {
692 	struct svcctl_QueryServiceConfig2W *param = arg;
693 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
694 	ndr_handle_t *hd;
695 	svcctl_manager_context_t *mgr_ctx;
696 	svcctl_service_context_t *svc_ctx;
697 	svcctl_svc_node_t *svc;
698 	svc_description_t *svc_desc;
699 	svc_failure_actions_t *fac;
700 	int offset, input_bufsize, bytes_needed = 0;
701 	mts_wchar_t *wide_desc;
702 	char *desc;
703 	DWORD status;
704 
705 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
706 	if (hd == NULL) {
707 		bzero(param, sizeof (struct svcctl_QueryServiceConfig2W));
708 		param->buffer = NDR_STRDUP(mxa, "");
709 		param->status = ERROR_INVALID_HANDLE;
710 		return (NDR_DRC_OK);
711 	}
712 
713 	input_bufsize = param->buf_size;
714 	param->buffer = NDR_MALLOC(mxa, input_bufsize);
715 	if (param->buffer == NULL) {
716 		bzero(param, sizeof (struct svcctl_QueryServiceConfig2W));
717 		param->buffer = NDR_STRDUP(mxa, "");
718 		param->status = ERROR_NOT_ENOUGH_MEMORY;
719 		return (NDR_DRC_OK);
720 	}
721 	bzero(param->buffer, input_bufsize);
722 
723 	status = ERROR_SUCCESS;
724 	switch (param->info_level) {
725 	case SERVICE_CONFIG_DESCRIPTION:
726 		svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
727 		mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
728 		if (mgr_ctx == NULL) {
729 			param->status = ERROR_INVALID_HANDLE;
730 			break;
731 		}
732 
733 		svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
734 		if (svc == NULL || svc->sn_desc == NULL) {
735 			status = ERROR_SERVICE_DOES_NOT_EXIST;
736 			break;
737 		}
738 
739 		desc = svc->sn_desc;
740 		bytes_needed = SVCCTL_WNSTRLEN(desc);
741 
742 		if (input_bufsize <= bytes_needed) {
743 			param->bytes_needed = bytes_needed;
744 			param->status = ERROR_MORE_DATA;
745 			return (NDR_DRC_OK);
746 		}
747 
748 		offset = sizeof (svc_description_t);
749 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
750 		svc_desc = (svc_description_t *)param->buffer;
751 		svc_desc->desc = offset;
752 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
753 		wide_desc = (mts_wchar_t *)&param->buffer[offset];
754 		(void) mts_mbstowcs(wide_desc, desc, (strlen(desc) + 1));
755 		offset = SVCCTL_WNSTRLEN(desc);
756 
757 		param->bytes_needed = offset;
758 		break;
759 
760 	case SERVICE_CONFIG_FAILURE_ACTIONS:
761 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
762 		fac = (svc_failure_actions_t *)param->buffer;
763 		bzero(fac, sizeof (svc_failure_actions_t));
764 
765 		param->bytes_needed = input_bufsize;
766 		break;
767 
768 	default:
769 		status = ERROR_INVALID_LEVEL;
770 		break;
771 	}
772 
773 	if (status != ERROR_SUCCESS) {
774 		bzero(param, sizeof (struct svcctl_QueryServiceConfig2W));
775 		param->buffer = NDR_STRDUP(mxa, "");
776 		param->status = status;
777 		return (NDR_DRC_OK);
778 	}
779 
780 	param->status = ERROR_SUCCESS;
781 	return (NDR_DRC_OK);
782 }
783