xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/svcctl_svc.c (revision e23347b1b88ce2c0847fad6e9467a1f953597aa7)
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 2009 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 <smbsrv/ntifs.h>
40 #include "svcctl_scm.h"
41 
42 #define	SVCCTL_SECURITY_BUFSIZE		256
43 #define	SVCCTL_ENUMSERVICES_MINBUFSIZE	1024
44 
45 #define	SVCCTL_OPENSVC_OP_UNIMPLEMENTED(S)	\
46 	((S) & SERVICE_CHANGE_CONFIG)	||	\
47 	((S) & SERVICE_PAUSE_CONTINUE)	||	\
48 	((S) & SERVICE_START)		||	\
49 	((S) & SERVICE_STOP)		||	\
50 	((S) & SERVICE_ENUMERATE_DEPENDENTS)
51 
52 static int svcctl_s_Close(void *, ndr_xa_t *);
53 static int svcctl_s_ControlService(void *, ndr_xa_t *);
54 static int svcctl_s_DeleteService(void *, ndr_xa_t *);
55 static int svcctl_s_QueryServiceSecurity(void *, ndr_xa_t *);
56 static int svcctl_s_SetServiceSecurity(void *, ndr_xa_t *);
57 static int svcctl_s_OpenManager(void *, ndr_xa_t *);
58 static int svcctl_s_OpenService(void *, ndr_xa_t *);
59 static int svcctl_s_QueryServiceStatus(void *, ndr_xa_t *);
60 static int svcctl_s_QueryServiceConfig(void *, ndr_xa_t *);
61 static int svcctl_s_StartService(void *, ndr_xa_t *);
62 static int svcctl_s_EnumDependentServices(void *, ndr_xa_t *);
63 static int svcctl_s_EnumServicesStatus(void *, ndr_xa_t *);
64 static int svcctl_s_GetServiceDisplayNameW(void *, ndr_xa_t *);
65 static int svcctl_s_GetServiceKeyNameW(void *, ndr_xa_t *);
66 static int svcctl_s_OpenSCManagerA(void *, ndr_xa_t *);
67 static int svcctl_s_OpenServiceA(void *, ndr_xa_t *);
68 static int svcctl_s_EnumServicesStatusA(void *, ndr_xa_t *);
69 static int svcctl_s_QueryServiceConfig2W(void *, ndr_xa_t *);
70 static int svcctl_s_QueryServiceStatusEx(void *, ndr_xa_t *);
71 
72 static ndr_stub_table_t svcctl_stub_table[] = {
73 	{ svcctl_s_Close,		SVCCTL_OPNUM_Close },
74 	{ svcctl_s_ControlService,	SVCCTL_OPNUM_ControlService },
75 	{ svcctl_s_DeleteService,	SVCCTL_OPNUM_DeleteService },
76 	{ svcctl_s_QueryServiceSecurity, SVCCTL_OPNUM_QueryServiceSecurity },
77 	{ svcctl_s_SetServiceSecurity,	SVCCTL_OPNUM_SetServiceSecurity },
78 	{ svcctl_s_OpenManager,		SVCCTL_OPNUM_OpenManager },
79 	{ svcctl_s_OpenService,		SVCCTL_OPNUM_OpenService },
80 	{ svcctl_s_QueryServiceStatus,	SVCCTL_OPNUM_QueryServiceStatus },
81 	{ svcctl_s_QueryServiceConfig,	SVCCTL_OPNUM_QueryServiceConfig },
82 	{ svcctl_s_StartService,	SVCCTL_OPNUM_StartService },
83 	{ svcctl_s_EnumDependentServices,
84 		SVCCTL_OPNUM_EnumDependentServices },
85 	{ svcctl_s_EnumServicesStatus,	SVCCTL_OPNUM_EnumServicesStatus },
86 	{ svcctl_s_GetServiceDisplayNameW,
87 		SVCCTL_OPNUM_GetServiceDisplayNameW },
88 	{ svcctl_s_GetServiceKeyNameW,	SVCCTL_OPNUM_GetServiceKeyNameW },
89 	{ svcctl_s_OpenSCManagerA,	SVCCTL_OPNUM_OpenSCManagerA },
90 	{ svcctl_s_OpenServiceA,	SVCCTL_OPNUM_OpenServiceA },
91 	{ svcctl_s_EnumServicesStatusA,	SVCCTL_OPNUM_EnumServicesStatusA },
92 	{ svcctl_s_QueryServiceConfig2W, SVCCTL_OPNUM_QueryServiceConfig2W },
93 	{ svcctl_s_QueryServiceStatusEx, SVCCTL_OPNUM_QueryServiceStatusEx },
94 	{0}
95 };
96 
97 static ndr_service_t svcctl_service = {
98 	"SVCCTL",			/* name */
99 	"Service Control Services",	/* desc */
100 	"\\svcctl",			/* endpoint */
101 	PIPE_NTSVCS,			/* sec_addr_port */
102 	"367abb81-9844-35f1-ad32-98f038001003", 2,	/* abstract */
103 	NDR_TRANSFER_SYNTAX_UUID,		2,	/* transfer */
104 	0,				/* no bind_instance_size */
105 	0,				/* no bind_req() */
106 	0,				/* no unbind_and_close() */
107 	0,				/* use generic_call_stub() */
108 	&TYPEINFO(svcctl_interface),	/* interface ti */
109 	svcctl_stub_table		/* stub_table */
110 };
111 
112 /*
113  * svcctl_initialize
114  *
115  * This function registers the SVCCTL RPC interface with the RPC runtime
116  * library. It must be called in order to use either the client side
117  * or the server side functions.
118  */
119 void
120 svcctl_initialize(void)
121 {
122 	(void) ndr_svc_register(&svcctl_service);
123 }
124 
125 /*
126  * svcctl_hdlookup
127  *
128  * Handle lookup wrapper to validate the local service and/or manager context.
129  */
130 static ndr_handle_t *
131 svcctl_hdlookup(ndr_xa_t *mxa, ndr_hdid_t *id, svcctl_context_type_t type)
132 {
133 	ndr_handle_t *hd;
134 	svcctl_context_t *ctx;
135 
136 	if ((hd = ndr_hdlookup(mxa, id)) == NULL)
137 		return (NULL);
138 
139 	if ((ctx = (svcctl_context_t *)hd->nh_data) == NULL)
140 		return (NULL);
141 
142 	if ((ctx->c_type != type) || (ctx->c_ctx.uc_cp == NULL))
143 		return (NULL);
144 
145 	return (hd);
146 }
147 
148 /*
149  * svcctl_hdfree
150  *
151  * Handle deallocation wrapper to free the local service and/or manager context.
152  */
153 static void
154 svcctl_hdfree(ndr_xa_t *mxa, ndr_hdid_t *id)
155 {
156 	ndr_handle_t *hd;
157 	svcctl_context_t *ctx;
158 	svcctl_manager_context_t *mgr_ctx;
159 	svcctl_service_context_t *svc_ctx;
160 
161 	if ((hd = ndr_hdlookup(mxa, id)) != NULL) {
162 		ctx = (svcctl_context_t *)hd->nh_data;
163 
164 		switch (ctx->c_type) {
165 		case SVCCTL_MANAGER_CONTEXT:
166 			mgr_ctx = ctx->c_ctx.uc_mgr;
167 			svcctl_scm_fini(mgr_ctx);
168 			svcctl_scm_scf_handle_fini(mgr_ctx);
169 			free(mgr_ctx);
170 			break;
171 
172 		case SVCCTL_SERVICE_CONTEXT:
173 			svc_ctx = ctx->c_ctx.uc_svc;
174 			free(svc_ctx->sc_mgrid);
175 			free(svc_ctx->sc_svcname);
176 			free(svc_ctx);
177 			break;
178 
179 		default:
180 			break;
181 		}
182 
183 		free(ctx);
184 		ndr_hdfree(mxa, id);
185 	}
186 }
187 
188 /*
189  * svcctl_mgr_hdalloc
190  *
191  * Handle allocation wrapper to setup the local manager context.
192  */
193 static ndr_hdid_t *
194 svcctl_mgr_hdalloc(ndr_xa_t *mxa)
195 {
196 	svcctl_context_t *ctx;
197 	svcctl_manager_context_t *mgr_ctx;
198 
199 	if ((ctx = malloc(sizeof (svcctl_context_t))) == NULL)
200 		return (NULL);
201 	ctx->c_type = SVCCTL_MANAGER_CONTEXT;
202 
203 	if ((mgr_ctx = malloc(sizeof (svcctl_manager_context_t))) == NULL) {
204 		free(ctx);
205 		return (NULL);
206 	}
207 	bzero(mgr_ctx, sizeof (svcctl_manager_context_t));
208 
209 	if (svcctl_scm_scf_handle_init(mgr_ctx) < 0) {
210 		free(mgr_ctx);
211 		free(ctx);
212 		return (NULL);
213 	}
214 
215 	if (svcctl_scm_init(mgr_ctx) < 0) {
216 		svcctl_scm_scf_handle_fini(mgr_ctx);
217 		free(mgr_ctx);
218 		free(ctx);
219 		return (NULL);
220 	}
221 
222 	ctx->c_ctx.uc_mgr = mgr_ctx;
223 
224 	return (ndr_hdalloc(mxa, ctx));
225 }
226 
227 /*
228  * svcctl_get_mgr_ctx
229  *
230  * This function looks up a reference to local manager context.
231  */
232 static svcctl_manager_context_t *
233 svcctl_get_mgr_ctx(ndr_xa_t *mxa, ndr_hdid_t *mgr_id)
234 {
235 	ndr_handle_t *hd;
236 	svcctl_manager_context_t *mgr_ctx;
237 
238 	hd = svcctl_hdlookup(mxa, mgr_id, SVCCTL_MANAGER_CONTEXT);
239 	if (hd == NULL)
240 		return (NULL);
241 
242 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
243 
244 	return (mgr_ctx);
245 }
246 
247 /*
248  * svcctl_svc_hdalloc
249  *
250  * Handle allocation wrapper to setup the local service context.
251  */
252 static ndr_hdid_t *
253 svcctl_svc_hdalloc(ndr_xa_t *mxa, ndr_hdid_t *mgr_id, char *svc_name)
254 {
255 	svcctl_context_t *ctx;
256 	svcctl_service_context_t *svc_ctx;
257 	svcctl_manager_context_t *mgr_ctx;
258 	int max_name_sz = 0;
259 	char *svcname;
260 
261 	mgr_ctx = svcctl_get_mgr_ctx(mxa, mgr_id);
262 	if (mgr_ctx == NULL)
263 		return (NULL);
264 	max_name_sz = mgr_ctx->mc_scf_max_fmri_len;
265 
266 	if ((ctx = malloc(sizeof (svcctl_context_t))) == NULL) {
267 		svcctl_hdfree(mxa, mgr_id);
268 		return (NULL);
269 	}
270 	ctx->c_type = SVCCTL_SERVICE_CONTEXT;
271 
272 	if ((svc_ctx = malloc(sizeof (svcctl_service_context_t))) == NULL) {
273 		svcctl_hdfree(mxa, mgr_id);
274 		free(ctx);
275 		return (NULL);
276 	}
277 	bzero(svc_ctx, sizeof (svcctl_service_context_t));
278 
279 	svc_ctx->sc_mgrid = malloc(sizeof (ndr_hdid_t));
280 	svcname = malloc(max_name_sz);
281 
282 	if ((svc_ctx->sc_mgrid == NULL) || (svcname == NULL)) {
283 		free(svc_ctx->sc_mgrid);
284 		free(svc_ctx);
285 		svcctl_hdfree(mxa, mgr_id);
286 		free(ctx);
287 		return (NULL);
288 	}
289 
290 	svc_ctx->sc_svcname = svcname;
291 
292 	bcopy(mgr_id, svc_ctx->sc_mgrid, sizeof (ndr_hdid_t));
293 	(void) strlcpy(svc_ctx->sc_svcname, svc_name, max_name_sz);
294 
295 	ctx->c_ctx.uc_svc = svc_ctx;
296 
297 	return (ndr_hdalloc(mxa, ctx));
298 }
299 
300 /*
301  * svcctl_s_Close
302  *
303  * This is a request to close the SVCCTL interface specified by the
304  * handle. Free the handle and zero out the result handle for the
305  * client.
306  *
307  * Returns:
308  *	ERROR_SUCCESS
309  *	ERROR_INVALID_HANDLE
310  */
311 static int
312 svcctl_s_Close(void *arg, ndr_xa_t *mxa)
313 {
314 	struct svcctl_Close *param = arg;
315 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
316 
317 	svcctl_hdfree(mxa, id);
318 
319 	bzero(&param->result_handle, sizeof (svcctl_handle_t));
320 	param->status = ERROR_SUCCESS;
321 	return (NDR_DRC_OK);
322 }
323 
324 /*
325  * svcctl_s_ControlService
326  */
327 static int
328 svcctl_s_ControlService(void *arg, ndr_xa_t *mxa)
329 {
330 	struct svcctl_ControlService *param = arg;
331 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
332 	ndr_handle_t *hd;
333 	svcctl_manager_context_t *mgr_ctx;
334 	svcctl_service_context_t *svc_ctx;
335 	svcctl_svc_node_t *svc;
336 
337 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
338 	if (hd == NULL) {
339 		bzero(param, sizeof (struct svcctl_ControlService));
340 		param->status = ERROR_INVALID_HANDLE;
341 		return (NDR_DRC_OK);
342 	}
343 
344 	svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
345 	mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
346 	if (mgr_ctx == NULL) {
347 		bzero(param, sizeof (struct svcctl_ControlService));
348 		param->status = ERROR_INVALID_HANDLE;
349 		return (NDR_DRC_OK);
350 	}
351 
352 	switch (param->control) {
353 	case SERVICE_CONTROL_STOP:
354 	case SERVICE_CONTROL_PAUSE:
355 	case SERVICE_CONTROL_CONTINUE:
356 	case SERVICE_CONTROL_INTERROGATE:
357 	case SERVICE_CONTROL_SHUTDOWN:
358 	case SERVICE_CONTROL_PARAMCHANGE:
359 	case SERVICE_CONTROL_NETBINDADD:
360 	case SERVICE_CONTROL_NETBINDREMOVE:
361 	case SERVICE_CONTROL_NETBINDENABLE:
362 	case SERVICE_CONTROL_NETBINDDISABLE:
363 		break;
364 	default:
365 		bzero(param, sizeof (struct svcctl_ControlService));
366 		param->status = ERROR_INVALID_PARAMETER;
367 		return (NDR_DRC_OK);
368 	}
369 
370 	svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
371 	if (svc == NULL || svc->sn_state == NULL) {
372 		bzero(param, sizeof (struct svcctl_ControlService));
373 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
374 		return (NDR_DRC_OK);
375 	}
376 
377 	param->service_status.service_type = SERVICE_WIN32_SHARE_PROCESS;
378 	param->service_status.cur_state = svcctl_scm_map_status(svc->sn_state);
379 	param->service_status.ctrl_accepted = 0;
380 	param->service_status.w32_exitcode = 0;
381 	param->service_status.svc_specified_exitcode = 0;
382 	param->service_status.check_point = 0;
383 	param->service_status.wait_hint = 0;
384 
385 	param->status = ERROR_SUCCESS;
386 	return (NDR_DRC_OK);
387 }
388 
389 /*
390  * svcctl_s_DeleteService
391  */
392 static int
393 svcctl_s_DeleteService(void *arg, ndr_xa_t *mxa)
394 {
395 	struct svcctl_DeleteService *param = arg;
396 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
397 	ndr_handle_t *hd;
398 
399 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
400 	if (hd == NULL) {
401 		param->status = ERROR_INVALID_HANDLE;
402 		return (NDR_DRC_OK);
403 	}
404 
405 	param->status = ERROR_SUCCESS;
406 	return (NDR_DRC_OK);
407 }
408 
409 /*
410  * svcctl_s_QueryServiceSecurity
411  */
412 static int
413 svcctl_s_QueryServiceSecurity(void *arg, ndr_xa_t *mxa)
414 {
415 	struct svcctl_QueryServiceSecurity *param = arg;
416 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
417 	ndr_handle_t *hd;
418 	uint32_t sec_info;
419 	uint32_t bytes_needed = 0;
420 	uint32_t status;
421 
422 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
423 	if (hd == NULL) {
424 		status = ERROR_INVALID_HANDLE;
425 		goto query_service_security_error;
426 	}
427 
428 	sec_info = param->security_info & SMB_ALL_SECINFO;
429 	if (sec_info == 0) {
430 		status = ERROR_INVALID_PARAMETER;
431 		goto query_service_security_error;
432 	}
433 
434 	if (param->buf_size < SVCCTL_SECURITY_BUFSIZE) {
435 		bytes_needed = SVCCTL_SECURITY_BUFSIZE;
436 		status = ERROR_INSUFFICIENT_BUFFER;
437 		goto query_service_security_error;
438 	}
439 
440 	param->buffer = NDR_MALLOC(mxa, SVCCTL_SECURITY_BUFSIZE);
441 	if (param->buffer == NULL) {
442 		status = ERROR_NOT_ENOUGH_MEMORY;
443 		goto query_service_security_error;
444 	}
445 
446 	bzero(param->buffer, sizeof (SVCCTL_SECURITY_BUFSIZE));
447 	param->buf_size = SVCCTL_SECURITY_BUFSIZE;
448 	param->bytes_needed = 0;
449 	param->status = ERROR_SUCCESS;
450 	return (NDR_DRC_OK);
451 
452 query_service_security_error:
453 	bzero(param, sizeof (struct svcctl_QueryServiceSecurity));
454 	param->buf_size = 0;
455 	param->buffer = NDR_MALLOC(mxa, sizeof (uint32_t));
456 	param->bytes_needed = bytes_needed;
457 	param->status = status;
458 	return (NDR_DRC_OK);
459 }
460 
461 
462 /*
463  * svcctl_s_SetServiceSecurity
464  */
465 static int
466 svcctl_s_SetServiceSecurity(void *arg, ndr_xa_t *mxa)
467 {
468 	struct svcctl_SetServiceSecurity *param = arg;
469 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
470 	ndr_handle_t *hd;
471 
472 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
473 	if (hd == NULL) {
474 		param->status = ERROR_INVALID_HANDLE;
475 		return (NDR_DRC_OK);
476 	}
477 
478 	if ((param->security_info & SMB_ALL_SECINFO) == 0) {
479 		param->status = ERROR_INVALID_PARAMETER;
480 		return (NDR_DRC_OK);
481 	}
482 
483 	param->status = ERROR_ACCESS_DENIED;
484 	return (NDR_DRC_OK);
485 }
486 
487 /*
488  * svcctl_s_OpenManager
489  *
490  * Request to open the service control manager.
491  * The caller must have administrator rights in order to open this
492  * interface.  We don't support write (SC_MANAGER_LOCK) access.
493  *
494  * Returns:
495  *	ERROR_SUCCESS
496  *	ERROR_ACCESS_DENIED
497  *
498  * On success, returns a handle for use with subsequent svcctl requests.
499  */
500 static int
501 svcctl_s_OpenManager(void *arg, ndr_xa_t *mxa)
502 {
503 	struct svcctl_OpenManager *param = arg;
504 	ndr_hdid_t *id = NULL;
505 	int rc;
506 
507 	rc = ndr_is_admin(mxa);
508 
509 	if ((rc == 0) || (param->desired_access & SC_MANAGER_LOCK) != 0) {
510 		bzero(&param->handle, sizeof (svcctl_handle_t));
511 		param->status = ERROR_ACCESS_DENIED;
512 		return (NDR_DRC_OK);
513 	}
514 
515 	id = svcctl_mgr_hdalloc(mxa);
516 	if (id) {
517 		bcopy(id, &param->handle, sizeof (svcctl_handle_t));
518 		param->status = ERROR_SUCCESS;
519 	} else {
520 		bzero(&param->handle, sizeof (svcctl_handle_t));
521 		param->status = ERROR_ACCESS_DENIED;
522 	}
523 
524 	return (NDR_DRC_OK);
525 }
526 
527 /*
528  * svcctl_s_OpenService
529  *
530  * Return a handle for use with subsequent svcctl requests.
531  *
532  * Returns:
533  *	ERROR_SUCCESS
534  *	ERROR_INVALID_HANDLE
535  *	ERROR_SERVICE_DOES_NOT_EXIST
536  *	ERROR_CALL_NOT_IMPLEMENTED
537  */
538 static int
539 svcctl_s_OpenService(void *arg, ndr_xa_t *mxa)
540 {
541 	struct svcctl_OpenService *param = arg;
542 	ndr_hdid_t *mgrid = (ndr_hdid_t *)&param->manager_handle;
543 	ndr_hdid_t *id = NULL;
544 	ndr_handle_t *hd;
545 	DWORD status;
546 	svcctl_manager_context_t *mgr_ctx;
547 	char *svc_name = (char *)param->service_name;
548 	boolean_t unimplemented_operations = B_FALSE;
549 
550 	/* Allow service handle allocations for only status & config queries */
551 	unimplemented_operations =
552 	    SVCCTL_OPENSVC_OP_UNIMPLEMENTED(param->desired_access);
553 
554 	if (unimplemented_operations) {
555 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
556 		param->status = ERROR_CALL_NOT_IMPLEMENTED;
557 		return (NDR_DRC_OK);
558 	}
559 
560 	hd = svcctl_hdlookup(mxa, mgrid, SVCCTL_MANAGER_CONTEXT);
561 	if (hd == NULL) {
562 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
563 		param->status = ERROR_INVALID_HANDLE;
564 		return (NDR_DRC_OK);
565 	}
566 
567 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
568 	status = svcctl_scm_validate_service(mgr_ctx, svc_name);
569 	if (status != ERROR_SUCCESS) {
570 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
571 		param->status = status;
572 		return (NDR_DRC_OK);
573 	}
574 
575 	id = svcctl_svc_hdalloc(mxa, mgrid, svc_name);
576 	if (id) {
577 		bcopy(id, &param->service_handle, sizeof (svcctl_handle_t));
578 		param->status = ERROR_SUCCESS;
579 	} else {
580 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
581 		param->status = ERROR_ACCESS_DENIED;
582 	}
583 
584 	return (NDR_DRC_OK);
585 }
586 
587 /*
588  * svcctl_s_QueryServiceStatus
589  *
590  * Returns:
591  *	ERROR_SUCCESS
592  *	ERROR_INVALID_HANDLE
593  */
594 static int
595 svcctl_s_QueryServiceStatus(void *arg, ndr_xa_t *mxa)
596 {
597 	struct svcctl_QueryServiceStatus *param = arg;
598 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
599 	ndr_handle_t *hd;
600 	svcctl_manager_context_t *mgr_ctx;
601 	svcctl_service_context_t *svc_ctx;
602 	svcctl_svc_node_t *svc;
603 
604 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
605 	if (hd == NULL) {
606 		bzero(param, sizeof (struct svcctl_QueryServiceStatus));
607 		param->status = ERROR_INVALID_HANDLE;
608 		return (NDR_DRC_OK);
609 	}
610 
611 	svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
612 	mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
613 	if (mgr_ctx == NULL) {
614 		bzero(param, sizeof (struct svcctl_QueryServiceStatus));
615 		param->status = ERROR_INVALID_HANDLE;
616 		return (NDR_DRC_OK);
617 	}
618 
619 	svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
620 	if (svc == NULL || svc->sn_state == NULL) {
621 		bzero(param, sizeof (struct svcctl_QueryServiceStatus));
622 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
623 		return (NDR_DRC_OK);
624 	}
625 
626 	param->service_status.service_type = SERVICE_WIN32_SHARE_PROCESS;
627 	param->service_status.cur_state = svcctl_scm_map_status(svc->sn_state);
628 	param->service_status.ctrl_accepted = 0;
629 	param->service_status.w32_exitcode = 0;
630 	param->service_status.svc_specified_exitcode = 0;
631 	param->service_status.check_point = 0;
632 	param->service_status.wait_hint = 0;
633 
634 	param->status = ERROR_SUCCESS;
635 	return (NDR_DRC_OK);
636 }
637 
638 /*
639  * svcctl_s_EnumDependentServices
640  *
641  * Enumerate the list of services that depend on the specified service.
642  */
643 static int
644 svcctl_s_EnumDependentServices(void *arg, ndr_xa_t *mxa)
645 {
646 	struct svcctl_EnumDependentServices *param = arg;
647 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
648 	ndr_handle_t *hd;
649 	svcctl_manager_context_t *mgr_ctx;
650 	svcctl_service_context_t *svc_ctx;
651 	svcctl_svc_node_t *svc;
652 	int input_bufsize = 0;
653 	uint32_t status;
654 
655 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
656 	if (hd == NULL) {
657 		status = ERROR_INVALID_HANDLE;
658 		goto enum_dependent_services_error;
659 	}
660 
661 	svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
662 	mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
663 	if (mgr_ctx == NULL) {
664 		status = ERROR_INVALID_HANDLE;
665 		goto enum_dependent_services_error;
666 	}
667 
668 	svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
669 	if (svc == NULL || svc->sn_state == NULL) {
670 		status = ERROR_SERVICE_DOES_NOT_EXIST;
671 		goto enum_dependent_services_error;
672 	}
673 
674 	switch (param->svc_state) {
675 	case SERVICE_STOPPED:
676 	case SERVICE_START_PENDING:
677 	case SERVICE_STOP_PENDING:
678 	case SERVICE_RUNNING:
679 	case SERVICE_CONTINUE_PENDING:
680 	case SERVICE_PAUSE_PENDING:
681 	case SERVICE_PAUSED:
682 		break;
683 	default:
684 		status = ERROR_INVALID_PARAMETER;
685 		goto enum_dependent_services_error;
686 	}
687 
688 	if ((input_bufsize = param->buf_size) == 0) {
689 		bzero(param, sizeof (struct svcctl_EnumDependentServices));
690 		param->buf_size = input_bufsize;
691 		param->services = NDR_STRDUP(mxa, "");
692 		param->bytes_needed = 1024;
693 		param->svc_num = 0;
694 		param->status = ERROR_MORE_DATA;
695 		return (NDR_DRC_OK);
696 	}
697 
698 	param->services = NDR_MALLOC(mxa, input_bufsize);
699 	if (param->services == NULL) {
700 		status = ERROR_NOT_ENOUGH_MEMORY;
701 		goto enum_dependent_services_error;
702 	}
703 
704 	bzero(param->services, input_bufsize);
705 	param->buf_size = input_bufsize;
706 	param->bytes_needed = 0;
707 	param->svc_num = 0;
708 	param->status = ERROR_SUCCESS;
709 	return (NDR_DRC_OK);
710 
711 enum_dependent_services_error:
712 	bzero(param, sizeof (struct svcctl_EnumDependentServices));
713 	param->services = NDR_STRDUP(mxa, "");
714 	param->status = status;
715 	return (NDR_DRC_OK);
716 }
717 
718 /*
719  * svcctl_s_EnumServicesStatus
720  *
721  * Enumerate the list of services we support.
722  */
723 static int
724 svcctl_s_EnumServicesStatus(void *arg, ndr_xa_t *mxa)
725 {
726 	struct svcctl_EnumServicesStatus *param = arg;
727 	ndr_hdid_t *id = (ndr_hdid_t *)&param->manager_handle;
728 	ndr_handle_t *hd;
729 	svcctl_manager_context_t *mgr_ctx;
730 	uint32_t buf_size = 0;
731 	uint32_t svc_num;
732 	uint32_t resume_handle = 0;
733 	uint32_t status;
734 
735 	if (param->resume_handle != NULL)
736 		resume_handle = *param->resume_handle;
737 
738 	hd = svcctl_hdlookup(mxa, id, SVCCTL_MANAGER_CONTEXT);
739 	if (hd == NULL) {
740 		status = ERROR_INVALID_HANDLE;
741 		goto enum_services_status_error;
742 	}
743 
744 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
745 	if (svcctl_scm_refresh(mgr_ctx) != 0) {
746 		status = ERROR_INVALID_HANDLE;
747 		goto enum_services_status_error;
748 	}
749 
750 	buf_size = param->buf_size;
751 	param->services = NDR_MALLOC(mxa, buf_size);
752 	if (param->services == NULL) {
753 		status = ERROR_NOT_ENOUGH_MEMORY;
754 		goto enum_services_status_error;
755 	}
756 	bzero(param->services, buf_size);
757 
758 	if (buf_size < SVCCTL_ENUMSERVICES_MINBUFSIZE) {
759 		param->bytes_needed = mgr_ctx->mc_bytes_needed;
760 		param->svc_num = 0;
761 		if (param->resume_handle)
762 			*param->resume_handle = 0;
763 		param->status = ERROR_MORE_DATA;
764 		return (NDR_DRC_OK);
765 	}
766 
767 	svc_num = svcctl_scm_enum_services(mgr_ctx, param->services,
768 	    buf_size, &resume_handle, B_TRUE);
769 
770 	param->buf_size = buf_size;
771 	param->svc_num = svc_num;
772 
773 	if (resume_handle != 0) {
774 		if (param->resume_handle != NULL)
775 			*param->resume_handle = resume_handle;
776 		param->bytes_needed = mgr_ctx->mc_bytes_needed;
777 		param->status = ERROR_MORE_DATA;
778 	} else {
779 		if (param->resume_handle)
780 			*param->resume_handle = 0;
781 		param->bytes_needed = 0;
782 		param->status = ERROR_SUCCESS;
783 	}
784 	return (NDR_DRC_OK);
785 
786 enum_services_status_error:
787 	bzero(param, sizeof (struct svcctl_EnumServicesStatus));
788 	param->services = NDR_STRDUP(mxa, "");
789 	param->status = status;
790 	return (NDR_DRC_OK);
791 }
792 
793 /*
794  * svcctl_s_QueryServiceConfig
795  *
796  * Returns:
797  *	ERROR_SUCCESS
798  *	ERROR_INVALID_HANDLE
799  */
800 static int
801 svcctl_s_QueryServiceConfig(void *arg, ndr_xa_t *mxa)
802 {
803 	struct svcctl_QueryServiceConfig *param = arg;
804 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
805 	ndr_handle_t *hd;
806 	svcctl_manager_context_t *mgr_ctx;
807 	svcctl_service_context_t *svc_ctx;
808 	svcctl_svc_node_t *svc;
809 	int bytes_needed = 0;
810 	svc_config_t *cfg;
811 
812 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
813 	if (hd == NULL) {
814 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
815 		param->status = ERROR_INVALID_HANDLE;
816 		return (NDR_DRC_OK);
817 	}
818 
819 	svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
820 	mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
821 	if (mgr_ctx == NULL) {
822 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
823 		param->status = ERROR_INVALID_HANDLE;
824 		return (NDR_DRC_OK);
825 	}
826 
827 	svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
828 	if (svc == NULL || svc->sn_fmri == NULL) {
829 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
830 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
831 		return (NDR_DRC_OK);
832 	}
833 
834 	cfg = &param->service_cfg;
835 	cfg->service_type = SERVICE_WIN32_SHARE_PROCESS;
836 	cfg->start_type = SERVICE_AUTO_START;
837 	cfg->error_control = SERVICE_AUTO_START;
838 	cfg->binary_pathname = NDR_STRDUP(mxa, "");
839 	cfg->loadorder_group = NDR_STRDUP(mxa, "");
840 	cfg->tag_id = 0;
841 	cfg->dependencies = NDR_STRDUP(mxa, "");
842 	cfg->service_startname = NDR_STRDUP(mxa, "");
843 	cfg->display_name = NDR_STRDUP(mxa, svc->sn_fmri);
844 
845 	bytes_needed = sizeof (svc_config_t);
846 	bytes_needed += SVCCTL_WNSTRLEN((const char *)cfg->binary_pathname);
847 	bytes_needed += SVCCTL_WNSTRLEN((const char *)cfg->loadorder_group);
848 	bytes_needed += SVCCTL_WNSTRLEN((const char *)cfg->dependencies);
849 	bytes_needed += SVCCTL_WNSTRLEN((const char *)cfg->service_startname);
850 	bytes_needed += SVCCTL_WNSTRLEN(svc->sn_fmri);
851 
852 	if (param->buf_size < bytes_needed) {
853 		bzero(param, sizeof (struct svcctl_QueryServiceConfig));
854 		param->cfg_bytes = bytes_needed;
855 		param->status = ERROR_INSUFFICIENT_BUFFER;
856 		return (NDR_DRC_OK);
857 	}
858 
859 	param->cfg_bytes = bytes_needed;
860 	param->status = ERROR_SUCCESS;
861 	return (NDR_DRC_OK);
862 }
863 
864 /*
865  * svcctl_s_StartService
866  */
867 static int
868 svcctl_s_StartService(void *arg, ndr_xa_t *mxa)
869 {
870 	struct svcctl_StartService *param = arg;
871 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
872 	ndr_handle_t *hd;
873 	svcctl_manager_context_t *mgr_ctx;
874 	svcctl_service_context_t *svc_ctx;
875 	svcctl_svc_node_t *svc;
876 
877 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
878 	if (hd == NULL) {
879 		param->status = ERROR_INVALID_HANDLE;
880 		return (NDR_DRC_OK);
881 	}
882 
883 	svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
884 	mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
885 	if (mgr_ctx == NULL) {
886 		param->status = ERROR_INVALID_HANDLE;
887 		return (NDR_DRC_OK);
888 	}
889 
890 	svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
891 	if (svc == NULL || svc->sn_fmri == NULL)
892 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
893 	else
894 		param->status = ERROR_SERVICE_ALREADY_RUNNING;
895 	return (NDR_DRC_OK);
896 }
897 
898 
899 /*
900  * svcctl_s_GetServiceDisplayNameW
901  *
902  * Returns:
903  *	ERROR_SUCCESS
904  *	ERROR_INVALID_HANDLE
905  *	ERROR_SERVICE_DOES_NOT_EXIST
906  */
907 static int
908 svcctl_s_GetServiceDisplayNameW(void *arg, ndr_xa_t *mxa)
909 {
910 	struct svcctl_GetServiceDisplayNameW *param = arg;
911 	ndr_hdid_t *id = (ndr_hdid_t *)&param->manager_handle;
912 	ndr_handle_t *hd;
913 	svcctl_svc_node_t *svc;
914 	svcctl_manager_context_t *mgr_ctx;
915 
916 	hd = svcctl_hdlookup(mxa, id, SVCCTL_MANAGER_CONTEXT);
917 	if (hd == NULL) {
918 		bzero(param, sizeof (struct svcctl_GetServiceDisplayNameW));
919 		param->display_name = NDR_STRDUP(mxa, "");
920 		param->status = ERROR_INVALID_HANDLE;
921 		return (NDR_DRC_OK);
922 	}
923 
924 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
925 	svc = svcctl_scm_find_service(mgr_ctx, (char *)param->service_name);
926 	if (svc == NULL || svc->sn_fmri == NULL) {
927 		bzero(param, sizeof (struct svcctl_GetServiceDisplayNameW));
928 		param->display_name = NDR_STRDUP(mxa, "");
929 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
930 		return (NDR_DRC_OK);
931 	}
932 
933 	param->display_name = NDR_STRDUP(mxa, svc->sn_fmri);
934 	if (param->display_name == NULL) {
935 		bzero(param, sizeof (struct svcctl_GetServiceDisplayNameW));
936 		param->display_name = NDR_STRDUP(mxa, "");
937 		param->status = ERROR_NOT_ENOUGH_MEMORY;
938 		return (NDR_DRC_OK);
939 	}
940 
941 	param->buf_size = strlen(svc->sn_fmri);
942 	param->status = ERROR_SUCCESS;
943 	return (NDR_DRC_OK);
944 }
945 
946 /*
947  * svcctl_s_GetServiceKeyNameW
948  *
949  * Returns:
950  *	ERROR_SUCCESS
951  *	ERROR_INVALID_HANDLE
952  *	ERROR_SERVICE_DOES_NOT_EXIST
953  */
954 static int
955 svcctl_s_GetServiceKeyNameW(void *arg, ndr_xa_t *mxa)
956 {
957 	struct svcctl_GetServiceKeyNameW *param = arg;
958 	ndr_hdid_t *id = (ndr_hdid_t *)&param->manager_handle;
959 	ndr_handle_t *hd;
960 	svcctl_svc_node_t *svc;
961 	svcctl_manager_context_t *mgr_ctx;
962 
963 	hd = svcctl_hdlookup(mxa, id, SVCCTL_MANAGER_CONTEXT);
964 	if (hd == NULL) {
965 		bzero(param, sizeof (struct svcctl_GetServiceKeyNameW));
966 		param->key_name = NDR_STRDUP(mxa, "");
967 		param->status = ERROR_INVALID_HANDLE;
968 		return (NDR_DRC_OK);
969 	}
970 
971 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
972 	svc = svcctl_scm_find_service(mgr_ctx, (char *)param->service_name);
973 	if (svc == NULL || svc->sn_name == NULL) {
974 		bzero(param, sizeof (struct svcctl_GetServiceKeyNameW));
975 		param->key_name = NDR_STRDUP(mxa, "");
976 		param->status = ERROR_SERVICE_DOES_NOT_EXIST;
977 		return (NDR_DRC_OK);
978 	}
979 
980 	param->key_name = NDR_STRDUP(mxa, svc->sn_name);
981 	if (param->key_name == NULL) {
982 		bzero(param, sizeof (struct svcctl_GetServiceKeyNameW));
983 		param->key_name = NDR_STRDUP(mxa, "");
984 		param->status = ERROR_NOT_ENOUGH_MEMORY;
985 		return (NDR_DRC_OK);
986 	}
987 
988 	param->buf_size = strlen(svc->sn_name);
989 	param->status = ERROR_SUCCESS;
990 	return (NDR_DRC_OK);
991 }
992 
993 /*
994  * svcctl_s_OpenSCManagerA
995  *
996  * Request to open the service control manager.
997  * The caller must have administrator rights in order to open this
998  * interface.  We don't support write (SC_MANAGER_LOCK) access.
999  *
1000  * Returns:
1001  *	ERROR_SUCCESS
1002  *	ERROR_ACCESS_DENIED
1003  *
1004  * On success, returns a handle for use with subsequent svcctl requests.
1005  */
1006 static int
1007 svcctl_s_OpenSCManagerA(void *arg, ndr_xa_t *mxa)
1008 {
1009 	struct svcctl_OpenSCManagerA *param = arg;
1010 	ndr_hdid_t *id = NULL;
1011 	int rc;
1012 
1013 	rc = ndr_is_admin(mxa);
1014 
1015 	if ((rc == 0) || (param->desired_access & SC_MANAGER_LOCK) != 0) {
1016 		bzero(&param->handle, sizeof (svcctl_handle_t));
1017 		param->status = ERROR_ACCESS_DENIED;
1018 		return (NDR_DRC_OK);
1019 	}
1020 
1021 	id = svcctl_mgr_hdalloc(mxa);
1022 	if (id) {
1023 		bcopy(id, &param->handle, sizeof (svcctl_handle_t));
1024 		param->status = ERROR_SUCCESS;
1025 	} else {
1026 		bzero(&param->handle, sizeof (svcctl_handle_t));
1027 		param->status = ERROR_ACCESS_DENIED;
1028 	}
1029 
1030 	return (NDR_DRC_OK);
1031 }
1032 
1033 /*
1034  * svcctl_s_OpenServiceA
1035  *
1036  * Return a handle for use with subsequent svcctl requests.
1037  *
1038  * Returns:
1039  *	ERROR_SUCCESS
1040  *	ERROR_INVALID_HANDLE
1041  *	ERROR_SERVICE_DOES_NOT_EXIST
1042  *	ERROR_CALL_NOT_IMPLEMENTED
1043  */
1044 static int
1045 svcctl_s_OpenServiceA(void *arg, ndr_xa_t *mxa)
1046 {
1047 	struct svcctl_OpenServiceA *param = arg;
1048 	ndr_hdid_t *mgrid = (ndr_hdid_t *)&param->manager_handle;
1049 	ndr_hdid_t *id = NULL;
1050 	ndr_handle_t *hd;
1051 	DWORD status;
1052 	svcctl_manager_context_t *mgr_ctx;
1053 	char *svc_name = (char *)param->service_name->value;
1054 	boolean_t unimplemented_operations = B_FALSE;
1055 
1056 	/* Allow service handle allocations for only status & config queries */
1057 	unimplemented_operations =
1058 	    SVCCTL_OPENSVC_OP_UNIMPLEMENTED(param->desired_access);
1059 
1060 	if (unimplemented_operations) {
1061 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
1062 		param->status = ERROR_CALL_NOT_IMPLEMENTED;
1063 		return (NDR_DRC_OK);
1064 	}
1065 
1066 	hd = svcctl_hdlookup(mxa, mgrid, SVCCTL_MANAGER_CONTEXT);
1067 	if (hd == NULL) {
1068 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
1069 		param->status = ERROR_INVALID_HANDLE;
1070 		return (NDR_DRC_OK);
1071 	}
1072 
1073 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
1074 	status = svcctl_scm_validate_service(mgr_ctx, svc_name);
1075 	if (status != ERROR_SUCCESS) {
1076 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
1077 		param->status = status;
1078 		return (NDR_DRC_OK);
1079 	}
1080 
1081 	id = svcctl_svc_hdalloc(mxa, mgrid, svc_name);
1082 	if (id) {
1083 		bcopy(id, &param->service_handle, sizeof (svcctl_handle_t));
1084 		param->status = ERROR_SUCCESS;
1085 	} else {
1086 		bzero(&param->service_handle, sizeof (svcctl_handle_t));
1087 		param->status = ERROR_ACCESS_DENIED;
1088 	}
1089 
1090 	return (NDR_DRC_OK);
1091 }
1092 
1093 /*
1094  * svcctl_s_EnumServicesStatusA
1095  *
1096  * Enumerate the list of services we support as ASCII.
1097  */
1098 static int
1099 svcctl_s_EnumServicesStatusA(void *arg, ndr_xa_t *mxa)
1100 {
1101 	struct svcctl_EnumServicesStatusA *param = arg;
1102 	ndr_hdid_t *id = (ndr_hdid_t *)&param->manager_handle;
1103 	ndr_handle_t *hd;
1104 	svcctl_manager_context_t *mgr_ctx;
1105 	uint32_t buf_size;
1106 	uint32_t svc_num;
1107 	uint32_t resume_handle = 0;
1108 	uint32_t status;
1109 
1110 	buf_size = param->buf_size;
1111 	if (param->resume_handle != NULL)
1112 		resume_handle = *param->resume_handle;
1113 
1114 	hd = svcctl_hdlookup(mxa, id, SVCCTL_MANAGER_CONTEXT);
1115 	if (hd == NULL) {
1116 		status = ERROR_INVALID_HANDLE;
1117 		goto enum_services_status_error;
1118 	}
1119 
1120 	mgr_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_mgr;
1121 	if (svcctl_scm_refresh(mgr_ctx) != 0) {
1122 		status = ERROR_INVALID_HANDLE;
1123 		goto enum_services_status_error;
1124 	}
1125 
1126 	param->services = NDR_MALLOC(mxa, buf_size);
1127 	if (param->services == NULL) {
1128 		status = ERROR_NOT_ENOUGH_MEMORY;
1129 		goto enum_services_status_error;
1130 	}
1131 	bzero(param->services, buf_size);
1132 
1133 	svc_num = svcctl_scm_enum_services(mgr_ctx, param->services,
1134 	    buf_size, &resume_handle, B_FALSE);
1135 
1136 	param->buf_size = buf_size;
1137 	param->svc_num = svc_num;
1138 
1139 	if (resume_handle != 0) {
1140 		if (param->resume_handle != NULL)
1141 			*param->resume_handle = resume_handle;
1142 		param->bytes_needed = mgr_ctx->mc_bytes_needed;
1143 		param->status = ERROR_MORE_DATA;
1144 	} else {
1145 		if (param->resume_handle)
1146 			*param->resume_handle = 0;
1147 		param->bytes_needed = 0;
1148 		param->status = ERROR_SUCCESS;
1149 	}
1150 	return (NDR_DRC_OK);
1151 
1152 enum_services_status_error:
1153 	bzero(param, sizeof (struct svcctl_EnumServicesStatusA));
1154 	param->services = NDR_STRDUP(mxa, "");
1155 	param->status = status;
1156 	return (NDR_DRC_OK);
1157 }
1158 
1159 /*
1160  * svcctl_s_QueryServiceConfig2W
1161  *
1162  * Returns:
1163  *	ERROR_SUCCESS
1164  *	ERROR_INVALID_HANDLE
1165  *	ERROR_INVALID_LEVEL
1166  *	ERROR_NOT_ENOUGH_MEMORY
1167  */
1168 static int
1169 svcctl_s_QueryServiceConfig2W(void *arg, ndr_xa_t *mxa)
1170 {
1171 	struct svcctl_QueryServiceConfig2W *param = arg;
1172 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
1173 	ndr_handle_t *hd;
1174 	svcctl_manager_context_t *mgr_ctx;
1175 	svcctl_service_context_t *svc_ctx;
1176 	svcctl_svc_node_t *svc;
1177 	svc_description_t *svc_desc;
1178 	svc_failure_actions_t *fac;
1179 	int offset, input_bufsize, bytes_needed = 0;
1180 	mts_wchar_t *wide_desc;
1181 	char *desc;
1182 	DWORD status;
1183 
1184 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
1185 	if (hd == NULL) {
1186 		bzero(param, sizeof (struct svcctl_QueryServiceConfig2W));
1187 		param->buffer = NDR_STRDUP(mxa, "");
1188 		param->status = ERROR_INVALID_HANDLE;
1189 		return (NDR_DRC_OK);
1190 	}
1191 
1192 	input_bufsize = param->buf_size;
1193 	param->buffer = NDR_MALLOC(mxa, input_bufsize);
1194 	if (param->buffer == NULL) {
1195 		bzero(param, sizeof (struct svcctl_QueryServiceConfig2W));
1196 		param->buffer = NDR_STRDUP(mxa, "");
1197 		param->status = ERROR_NOT_ENOUGH_MEMORY;
1198 		return (NDR_DRC_OK);
1199 	}
1200 	bzero(param->buffer, input_bufsize);
1201 
1202 	status = ERROR_SUCCESS;
1203 	switch (param->info_level) {
1204 	case SERVICE_CONFIG_DESCRIPTION:
1205 		svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
1206 		mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
1207 		if (mgr_ctx == NULL) {
1208 			param->status = ERROR_INVALID_HANDLE;
1209 			break;
1210 		}
1211 
1212 		svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
1213 		if (svc == NULL || svc->sn_desc == NULL) {
1214 			status = ERROR_SERVICE_DOES_NOT_EXIST;
1215 			break;
1216 		}
1217 
1218 		desc = svc->sn_desc;
1219 		bytes_needed = SVCCTL_WNSTRLEN(desc);
1220 
1221 		if (input_bufsize <= bytes_needed) {
1222 			param->bytes_needed = bytes_needed;
1223 			param->status = ERROR_INSUFFICIENT_BUFFER;
1224 			return (NDR_DRC_OK);
1225 		}
1226 
1227 		offset = sizeof (svc_description_t);
1228 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1229 		svc_desc = (svc_description_t *)param->buffer;
1230 		svc_desc->desc = offset;
1231 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1232 		wide_desc = (mts_wchar_t *)&param->buffer[offset];
1233 		(void) mts_mbstowcs(wide_desc, desc, (strlen(desc) + 1));
1234 		offset = SVCCTL_WNSTRLEN(desc);
1235 
1236 		param->bytes_needed = offset;
1237 		break;
1238 
1239 	case SERVICE_CONFIG_FAILURE_ACTIONS:
1240 		/*LINTED E_BAD_PTR_CAST_ALIGN*/
1241 		fac = (svc_failure_actions_t *)param->buffer;
1242 		bzero(fac, sizeof (svc_failure_actions_t));
1243 
1244 		param->bytes_needed = input_bufsize;
1245 		break;
1246 
1247 	default:
1248 		status = ERROR_INVALID_LEVEL;
1249 		break;
1250 	}
1251 
1252 	if (status != ERROR_SUCCESS) {
1253 		bzero(param, sizeof (struct svcctl_QueryServiceConfig2W));
1254 		param->buffer = NDR_STRDUP(mxa, "");
1255 		param->status = status;
1256 		return (NDR_DRC_OK);
1257 	}
1258 
1259 	param->status = ERROR_SUCCESS;
1260 	return (NDR_DRC_OK);
1261 }
1262 
1263 /*
1264  * svcctl_s_QueryServiceStatusEx
1265  */
1266 static int
1267 svcctl_s_QueryServiceStatusEx(void *arg, ndr_xa_t *mxa)
1268 {
1269 	struct svcctl_QueryServiceStatusEx *param = arg;
1270 	ndr_hdid_t *id = (ndr_hdid_t *)&param->service_handle;
1271 	ndr_handle_t *hd;
1272 	svcctl_manager_context_t *mgr_ctx;
1273 	svcctl_service_context_t *svc_ctx;
1274 	svcctl_svc_node_t *svc;
1275 	svc_status_ex_t *svc_status_ex;
1276 	uint32_t input_bufsize;
1277 	uint32_t bytes_needed;
1278 	DWORD status;
1279 
1280 	hd = svcctl_hdlookup(mxa, id, SVCCTL_SERVICE_CONTEXT);
1281 	if (hd == NULL) {
1282 		status = ERROR_INVALID_HANDLE;
1283 		goto query_service_status_ex_error;
1284 	}
1285 
1286 	svc_ctx = ((svcctl_context_t *)hd->nh_data)->c_ctx.uc_svc;
1287 	mgr_ctx = svcctl_get_mgr_ctx(mxa, svc_ctx->sc_mgrid);
1288 	if (mgr_ctx == NULL) {
1289 		status = ERROR_INVALID_HANDLE;
1290 		goto query_service_status_ex_error;
1291 	}
1292 
1293 	if (param->info_level != SC_STATUS_PROCESS_INFO) {
1294 		status = ERROR_INVALID_PARAMETER;
1295 		goto query_service_status_ex_error;
1296 	}
1297 
1298 	bytes_needed = sizeof (struct svcctl_QueryServiceStatusEx);
1299 
1300 	if ((input_bufsize = param->buf_size) < bytes_needed) {
1301 		bzero(param, sizeof (struct svcctl_QueryServiceStatusEx));
1302 		param->buf_size = input_bufsize;
1303 		param->buffer = NDR_STRDUP(mxa, "");
1304 		param->bytes_needed = bytes_needed;
1305 		param->status = ERROR_INSUFFICIENT_BUFFER;
1306 		return (NDR_DRC_OK);
1307 	}
1308 
1309 	if ((svc_status_ex = NDR_MALLOC(mxa, bytes_needed)) == NULL) {
1310 		status = ERROR_NOT_ENOUGH_MEMORY;
1311 		goto query_service_status_ex_error;
1312 	}
1313 
1314 	svc = svcctl_scm_find_service(mgr_ctx, svc_ctx->sc_svcname);
1315 	if (svc == NULL || svc->sn_state == NULL) {
1316 		status = ERROR_SERVICE_DOES_NOT_EXIST;
1317 		goto query_service_status_ex_error;
1318 	}
1319 
1320 	svc_status_ex->service_type = SERVICE_WIN32_SHARE_PROCESS;
1321 	svc_status_ex->cur_state = svcctl_scm_map_status(svc->sn_state);
1322 	svc_status_ex->ctrl_accepted = 0;
1323 	svc_status_ex->w32_exitcode = 0;
1324 	svc_status_ex->svc_specified_exitcode = 0;
1325 	svc_status_ex->check_point = 0;
1326 	svc_status_ex->wait_hint = 0;
1327 	svc_status_ex->process_id = 1;
1328 	svc_status_ex->service_flags = 1;
1329 
1330 	param->buffer = (uint8_t *)svc_status_ex;
1331 	param->buf_size = bytes_needed;
1332 	param->bytes_needed = 0;
1333 	param->status = ERROR_SUCCESS;
1334 	return (NDR_DRC_OK);
1335 
1336 query_service_status_ex_error:
1337 	bzero(param, sizeof (struct svcctl_QueryServiceStatusEx));
1338 	param->buffer = NDR_STRDUP(mxa, "");
1339 	param->status = status;
1340 	return (NDR_DRC_OK);
1341 }
1342