xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/winreg_svc.c (revision 6d02032db7b674f185405d42cc8bf10a46a9ab3a)
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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Windows Registry RPC (WINREG) server-side interface.
28  *
29  * The registry is a database with a hierarchical structure similar to
30  * a file system, with keys in place of directories and values in place
31  * of files.  The top level keys are known as root keys and each key can
32  * contain subkeys and values.  As with directories and sub-directories,
33  * the terms key and subkey are used interchangeably.  Values, analogous
34  * to files, contain data.
35  *
36  * A specific subkey can be identifies by its fully qualified name (FQN),
37  * which is analogous to a file system path.  In the registry, the key
38  * separator is the '\' character, which is reserved and cannot appear
39  * in key or value names.  Registry names are case-insensitive.
40  *
41  * For example:  HKEY_LOCAL_MACHINE\System\CurrentControlSet
42  *
43  * The HKEY_LOCAL_MACHINE root key contains a subkey called System, and
44  * System contains a subkey called CurrentControlSet.
45  *
46  * The WINREG RPC interface returns Win32 error codes.
47  */
48 
49 #include <sys/utsname.h>
50 #include <strings.h>
51 
52 #include <smbsrv/libsmb.h>
53 #include <smbsrv/ntstatus.h>
54 #include <smbsrv/nterror.h>
55 #include <smbsrv/nmpipes.h>
56 #include <smbsrv/libmlsvc.h>
57 #include <smbsrv/ndl/winreg.ndl>
58 
59 /*
60  * List of supported registry keys (case-insensitive).
61  */
62 static char *winreg_keys[] = {
63 	"HKLM",
64 	"HKU",
65 	"HKLM\\SOFTWARE",
66 	"HKLM\\SYSTEM",
67 	"Application",
68 	"Security",
69 	"System",
70 	"CurrentControlSet",
71 	"SunOS",
72 	"Solaris",
73 	"System\\CurrentControlSet\\Services\\Eventlog",
74 	"System\\CurrentControlSet\\Services\\Eventlog\\Application",
75 	"System\\CurrentControlSet\\Services\\Eventlog\\"
76 						"Application\\Application",
77 	"System\\CurrentControlSet\\Services\\Eventlog\\Security",
78 	"System\\CurrentControlSet\\Services\\Eventlog\\Security\\Security",
79 	"System\\CurrentControlSet\\Services\\Eventlog\\System",
80 	"System\\CurrentControlSet\\Services\\Eventlog\\System\\System",
81 	"System\\CurrentControlSet\\Control\\ProductOptions",
82 	"SOFTWARE",
83 	"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
84 };
85 
86 typedef struct winreg_subkey {
87 	list_node_t sk_lnd;
88 	ndr_hdid_t sk_handle;
89 	char sk_name[MAXPATHLEN];
90 	boolean_t sk_predefined;
91 } winreg_subkey_t;
92 
93 typedef struct winreg_keylist {
94 	list_t kl_list;
95 	int kl_count;
96 } winreg_keylist_t;
97 
98 static winreg_keylist_t winreg_keylist;
99 static mutex_t winreg_mutex;
100 
101 static ndr_hdid_t *winreg_alloc_id(ndr_xa_t *, const char *);
102 static void winreg_dealloc_id(ndr_xa_t *, ndr_hdid_t *);
103 static boolean_t winreg_key_has_subkey(const char *);
104 static char *winreg_enum_subkey(ndr_xa_t *, const char *, uint32_t);
105 static char *winreg_lookup_value(const char *);
106 static uint32_t winreg_sd_format(smb_sd_t *);
107 uint32_t srvsvc_sd_set_relative(smb_sd_t *, uint8_t *);
108 
109 static int winreg_s_OpenHKCR(void *, ndr_xa_t *);
110 static int winreg_s_OpenHKCU(void *, ndr_xa_t *);
111 static int winreg_s_OpenHKLM(void *, ndr_xa_t *);
112 static int winreg_s_OpenHKPD(void *, ndr_xa_t *);
113 static int winreg_s_OpenHKU(void *, ndr_xa_t *);
114 static int winreg_s_OpenHKCC(void *, ndr_xa_t *);
115 static int winreg_s_OpenHKDD(void *, ndr_xa_t *);
116 static int winreg_s_OpenHKPT(void *, ndr_xa_t *);
117 static int winreg_s_OpenHKPN(void *, ndr_xa_t *);
118 static int winreg_s_OpenHK(void *, ndr_xa_t *, const char *);
119 static int winreg_s_Close(void *, ndr_xa_t *);
120 static int winreg_s_CreateKey(void *, ndr_xa_t *);
121 static int winreg_s_DeleteKey(void *, ndr_xa_t *);
122 static int winreg_s_DeleteValue(void *, ndr_xa_t *);
123 static int winreg_s_EnumKey(void *, ndr_xa_t *);
124 static int winreg_s_EnumValue(void *, ndr_xa_t *);
125 static int winreg_s_FlushKey(void *, ndr_xa_t *);
126 static int winreg_s_GetKeySec(void *, ndr_xa_t *);
127 static int winreg_s_NotifyChange(void *, ndr_xa_t *);
128 static int winreg_s_OpenKey(void *, ndr_xa_t *);
129 static int winreg_s_QueryKey(void *, ndr_xa_t *);
130 static int winreg_s_QueryValue(void *, ndr_xa_t *);
131 static int winreg_s_SetKeySec(void *, ndr_xa_t *);
132 static int winreg_s_CreateValue(void *, ndr_xa_t *);
133 static int winreg_s_Shutdown(void *, ndr_xa_t *);
134 static int winreg_s_AbortShutdown(void *, ndr_xa_t *);
135 static int winreg_s_GetVersion(void *, ndr_xa_t *);
136 
137 static ndr_stub_table_t winreg_stub_table[] = {
138 	{ winreg_s_OpenHKCR,	WINREG_OPNUM_OpenHKCR },
139 	{ winreg_s_OpenHKCU,	WINREG_OPNUM_OpenHKCU },
140 	{ winreg_s_OpenHKLM,	WINREG_OPNUM_OpenHKLM },
141 	{ winreg_s_OpenHKPD,	WINREG_OPNUM_OpenHKPD },
142 	{ winreg_s_OpenHKU,	WINREG_OPNUM_OpenHKUsers },
143 	{ winreg_s_Close,	WINREG_OPNUM_Close },
144 	{ winreg_s_CreateKey,	WINREG_OPNUM_CreateKey },
145 	{ winreg_s_DeleteKey,	WINREG_OPNUM_DeleteKey },
146 	{ winreg_s_DeleteValue,	WINREG_OPNUM_DeleteValue },
147 	{ winreg_s_EnumKey,	WINREG_OPNUM_EnumKey },
148 	{ winreg_s_EnumValue,	WINREG_OPNUM_EnumValue },
149 	{ winreg_s_FlushKey,	WINREG_OPNUM_FlushKey },
150 	{ winreg_s_GetKeySec,	WINREG_OPNUM_GetKeySec },
151 	{ winreg_s_NotifyChange,	WINREG_OPNUM_NotifyChange },
152 	{ winreg_s_OpenKey,	WINREG_OPNUM_OpenKey },
153 	{ winreg_s_QueryKey,	WINREG_OPNUM_QueryKey },
154 	{ winreg_s_QueryValue,	WINREG_OPNUM_QueryValue },
155 	{ winreg_s_SetKeySec,	WINREG_OPNUM_SetKeySec },
156 	{ winreg_s_CreateValue,	WINREG_OPNUM_CreateValue },
157 	{ winreg_s_Shutdown,	WINREG_OPNUM_Shutdown },
158 	{ winreg_s_AbortShutdown,	WINREG_OPNUM_AbortShutdown },
159 	{ winreg_s_GetVersion,	WINREG_OPNUM_GetVersion },
160 	{ winreg_s_OpenHKCC,	WINREG_OPNUM_OpenHKCC },
161 	{ winreg_s_OpenHKDD,	WINREG_OPNUM_OpenHKDD },
162 	{ winreg_s_OpenHKPT,	WINREG_OPNUM_OpenHKPT },
163 	{ winreg_s_OpenHKPN,	WINREG_OPNUM_OpenHKPN },
164 	{0}
165 };
166 
167 static ndr_service_t winreg_service = {
168 	"Winreg",			/* name */
169 	"Windows Registry",		/* desc */
170 	"\\winreg",			/* endpoint */
171 	PIPE_WINREG,			/* sec_addr_port */
172 	"338cd001-2244-31f1-aaaa-900038001003", 1,	/* abstract */
173 	NDR_TRANSFER_SYNTAX_UUID,		2,	/* transfer */
174 	0,				/* no bind_instance_size */
175 	0,				/* no bind_req() */
176 	0,				/* no unbind_and_close() */
177 	0,				/* use generic_call_stub() */
178 	&TYPEINFO(winreg_interface),	/* interface ti */
179 	winreg_stub_table		/* stub_table */
180 };
181 
182 static char winreg_sysname[SYS_NMLN];
183 static char winreg_sysver[SMB_VERSTR_LEN];
184 
185 /*
186  * winreg_initialize
187  *
188  * Initialize and register the WINREG RPC interface with the RPC runtime
189  * library. It must be called in order to use either the client side
190  * or the server side functions.
191  */
192 void
193 winreg_initialize(void)
194 {
195 	winreg_subkey_t *key;
196 	smb_version_t version;
197 	struct utsname name;
198 	char *sysname;
199 	int i;
200 
201 	(void) mutex_lock(&winreg_mutex);
202 
203 	list_create(&winreg_keylist.kl_list, sizeof (winreg_subkey_t),
204 	    offsetof(winreg_subkey_t, sk_lnd));
205 	winreg_keylist.kl_count = 0;
206 
207 	for (i = 0; i < sizeof (winreg_keys)/sizeof (winreg_keys[0]); ++i) {
208 		if ((key = malloc(sizeof (winreg_subkey_t))) != NULL) {
209 			bzero(key, sizeof (winreg_subkey_t));
210 			(void) strlcpy(key->sk_name, winreg_keys[i],
211 			    MAXPATHLEN);
212 			key->sk_predefined = B_TRUE;
213 
214 			list_insert_tail(&winreg_keylist.kl_list, key);
215 			++winreg_keylist.kl_count;
216 		}
217 	}
218 
219 	(void) mutex_unlock(&winreg_mutex);
220 
221 	if (uname(&name) < 0)
222 		sysname = "Solaris";
223 	else
224 		sysname = name.sysname;
225 
226 	(void) strlcpy(winreg_sysname, sysname, SYS_NMLN);
227 
228 	smb_config_get_version(&version);
229 	(void) snprintf(winreg_sysver, SMB_VERSTR_LEN, "%d.%d",
230 	    version.sv_major, version.sv_minor);
231 
232 	(void) ndr_svc_register(&winreg_service);
233 }
234 
235 static int
236 winreg_s_OpenHKCR(void *arg, ndr_xa_t *mxa)
237 {
238 	return (winreg_s_OpenHK(arg, mxa, "HKCR"));
239 }
240 
241 static int
242 winreg_s_OpenHKCU(void *arg, ndr_xa_t *mxa)
243 {
244 	return (winreg_s_OpenHK(arg, mxa, "HKCU"));
245 }
246 
247 static int
248 winreg_s_OpenHKLM(void *arg, ndr_xa_t *mxa)
249 {
250 	return (winreg_s_OpenHK(arg, mxa, "HKLM"));
251 }
252 
253 static int
254 winreg_s_OpenHKPD(void *arg, ndr_xa_t *mxa)
255 {
256 	return (winreg_s_OpenHK(arg, mxa, "HKPD"));
257 }
258 
259 static int
260 winreg_s_OpenHKU(void *arg, ndr_xa_t *mxa)
261 {
262 	return (winreg_s_OpenHK(arg, mxa, "HKU"));
263 }
264 
265 static int
266 winreg_s_OpenHKCC(void *arg, ndr_xa_t *mxa)
267 {
268 	return (winreg_s_OpenHK(arg, mxa, "HKCC"));
269 }
270 
271 static int
272 winreg_s_OpenHKDD(void *arg, ndr_xa_t *mxa)
273 {
274 	return (winreg_s_OpenHK(arg, mxa, "HKDD"));
275 }
276 
277 static int
278 winreg_s_OpenHKPT(void *arg, ndr_xa_t *mxa)
279 {
280 	return (winreg_s_OpenHK(arg, mxa, "HKPT"));
281 }
282 
283 static int
284 winreg_s_OpenHKPN(void *arg, ndr_xa_t *mxa)
285 {
286 	return (winreg_s_OpenHK(arg, mxa, "HKPN"));
287 }
288 
289 /*
290  * winreg_s_OpenHK
291  *
292  * Common code to open root HKEYs.
293  */
294 static int
295 winreg_s_OpenHK(void *arg, ndr_xa_t *mxa, const char *hkey)
296 {
297 	struct winreg_OpenHKCR *param = arg;
298 	ndr_hdid_t *id;
299 
300 	(void) mutex_lock(&winreg_mutex);
301 
302 	if ((id = winreg_alloc_id(mxa, hkey)) == NULL) {
303 		bzero(&param->handle, sizeof (winreg_handle_t));
304 		param->status = ERROR_ACCESS_DENIED;
305 	} else {
306 		bcopy(id, &param->handle, sizeof (winreg_handle_t));
307 		param->status = ERROR_SUCCESS;
308 	}
309 
310 	(void) mutex_unlock(&winreg_mutex);
311 	return (NDR_DRC_OK);
312 }
313 
314 /*
315  * winreg_s_Close
316  *
317  * This is a request to close the WINREG interface specified by the
318  * handle. We don't track handles (yet), so just zero out the handle
319  * and return NDR_DRC_OK. Setting the handle to zero appears to be
320  * standard behaviour.
321  */
322 static int
323 winreg_s_Close(void *arg, ndr_xa_t *mxa)
324 {
325 	struct winreg_Close *param = arg;
326 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
327 
328 	(void) mutex_lock(&winreg_mutex);
329 	winreg_dealloc_id(mxa, id);
330 	(void) mutex_unlock(&winreg_mutex);
331 
332 	bzero(&param->result_handle, sizeof (winreg_handle_t));
333 	param->status = ERROR_SUCCESS;
334 	return (NDR_DRC_OK);
335 }
336 
337 static ndr_hdid_t *
338 winreg_alloc_id(ndr_xa_t *mxa, const char *key)
339 {
340 	ndr_handle_t	*hd;
341 	ndr_hdid_t	*id;
342 	char		*data;
343 
344 	if ((data = strdup(key)) == NULL)
345 		return (NULL);
346 
347 	if ((id = ndr_hdalloc(mxa, data)) == NULL) {
348 		free(data);
349 		return (NULL);
350 	}
351 
352 	if ((hd = ndr_hdlookup(mxa, id)) != NULL)
353 		hd->nh_data_free = free;
354 
355 	return (id);
356 }
357 
358 static void
359 winreg_dealloc_id(ndr_xa_t *mxa, ndr_hdid_t *id)
360 {
361 	ndr_handle_t *hd;
362 
363 	if ((hd = ndr_hdlookup(mxa, id)) != NULL) {
364 		free(hd->nh_data);
365 		hd->nh_data = NULL;
366 	}
367 
368 	ndr_hdfree(mxa, id);
369 }
370 
371 /*
372  * winreg_s_CreateKey
373  */
374 static int
375 winreg_s_CreateKey(void *arg, ndr_xa_t *mxa)
376 {
377 	struct winreg_CreateKey *param = arg;
378 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
379 	ndr_handle_t *hd;
380 	winreg_subkey_t *key;
381 	char *subkey;
382 	DWORD *action;
383 
384 	subkey = (char *)param->subkey.str;
385 
386 	if (!ndr_is_admin(mxa) || (subkey == NULL)) {
387 		bzero(param, sizeof (struct winreg_CreateKey));
388 		param->status = ERROR_ACCESS_DENIED;
389 		return (NDR_DRC_OK);
390 	}
391 
392 	(void) mutex_lock(&winreg_mutex);
393 
394 	hd = ndr_hdlookup(mxa, id);
395 	if (hd == NULL) {
396 		(void) mutex_unlock(&winreg_mutex);
397 		bzero(param, sizeof (struct winreg_CreateKey));
398 		param->status = ERROR_INVALID_HANDLE;
399 		return (NDR_DRC_OK);
400 	}
401 
402 	if ((action = NDR_NEW(mxa, DWORD)) == NULL) {
403 		(void) mutex_unlock(&winreg_mutex);
404 		bzero(param, sizeof (struct winreg_CreateKey));
405 		param->status = ERROR_NOT_ENOUGH_MEMORY;
406 		return (NDR_DRC_OK);
407 	}
408 
409 	if (list_is_empty(&winreg_keylist.kl_list))
410 		goto new_key;
411 
412 	/*
413 	 * Check for an existing key.
414 	 */
415 	key = list_head(&winreg_keylist.kl_list);
416 	do {
417 		if (strcasecmp(subkey, key->sk_name) == 0) {
418 			bcopy(&key->sk_handle, &param->result_handle,
419 			    sizeof (winreg_handle_t));
420 
421 			(void) mutex_unlock(&winreg_mutex);
422 			*action = WINREG_ACTION_EXISTING_KEY;
423 			param->action = action;
424 			param->status = ERROR_SUCCESS;
425 			return (NDR_DRC_OK);
426 		}
427 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
428 
429 new_key:
430 	/*
431 	 * Create a new key.
432 	 */
433 	if ((id = winreg_alloc_id(mxa, subkey)) == NULL)
434 		goto no_memory;
435 
436 	if ((key = malloc(sizeof (winreg_subkey_t))) == NULL) {
437 		winreg_dealloc_id(mxa, id);
438 		goto no_memory;
439 	}
440 
441 	bcopy(id, &key->sk_handle, sizeof (ndr_hdid_t));
442 	(void) strlcpy(key->sk_name, subkey, MAXPATHLEN);
443 	key->sk_predefined = B_FALSE;
444 	list_insert_tail(&winreg_keylist.kl_list, key);
445 	++winreg_keylist.kl_count;
446 
447 	bcopy(id, &param->result_handle, sizeof (winreg_handle_t));
448 
449 	(void) mutex_unlock(&winreg_mutex);
450 	*action = WINREG_ACTION_NEW_KEY;
451 	param->action = action;
452 	param->status = ERROR_SUCCESS;
453 	return (NDR_DRC_OK);
454 
455 no_memory:
456 	(void) mutex_unlock(&winreg_mutex);
457 	bzero(param, sizeof (struct winreg_CreateKey));
458 	param->status = ERROR_NOT_ENOUGH_MEMORY;
459 	return (NDR_DRC_OK);
460 }
461 
462 /*
463  * winreg_s_DeleteKey
464  */
465 static int
466 winreg_s_DeleteKey(void *arg, ndr_xa_t *mxa)
467 {
468 	struct winreg_DeleteKey *param = arg;
469 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
470 	winreg_subkey_t *key;
471 	char *subkey;
472 
473 	subkey = (char *)param->subkey.str;
474 
475 	if (!ndr_is_admin(mxa) || (subkey == NULL)) {
476 		param->status = ERROR_ACCESS_DENIED;
477 		return (NDR_DRC_OK);
478 	}
479 
480 	(void) mutex_lock(&winreg_mutex);
481 
482 	if ((ndr_hdlookup(mxa, id) == NULL) ||
483 	    list_is_empty(&winreg_keylist.kl_list) ||
484 	    winreg_key_has_subkey(subkey)) {
485 		(void) mutex_unlock(&winreg_mutex);
486 		param->status = ERROR_ACCESS_DENIED;
487 		return (NDR_DRC_OK);
488 	}
489 
490 	key = list_head(&winreg_keylist.kl_list);
491 	do {
492 		if (strcasecmp(subkey, key->sk_name) == 0) {
493 			if (key->sk_predefined == B_TRUE) {
494 				/* Predefined keys cannot be deleted */
495 				break;
496 			}
497 
498 			list_remove(&winreg_keylist.kl_list, key);
499 			--winreg_keylist.kl_count;
500 			winreg_dealloc_id(mxa, &key->sk_handle);
501 			free(key);
502 
503 			(void) mutex_unlock(&winreg_mutex);
504 			param->status = ERROR_SUCCESS;
505 			return (NDR_DRC_OK);
506 		}
507 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
508 
509 	(void) mutex_unlock(&winreg_mutex);
510 	param->status = ERROR_ACCESS_DENIED;
511 	return (NDR_DRC_OK);
512 }
513 
514 /*
515  * Call with the winreg_mutex held.
516  */
517 static boolean_t
518 winreg_key_has_subkey(const char *subkey)
519 {
520 	winreg_subkey_t *key;
521 	int keylen;
522 
523 	if (list_is_empty(&winreg_keylist.kl_list))
524 		return (B_FALSE);
525 
526 	keylen = strlen(subkey);
527 
528 	key = list_head(&winreg_keylist.kl_list);
529 	do {
530 		if (strncasecmp(subkey, key->sk_name, keylen) == 0) {
531 			/*
532 			 * Potential match.  If sk_name is longer than
533 			 * subkey, then sk_name is a subkey of our key.
534 			 */
535 			if (keylen < strlen(key->sk_name))
536 				return (B_TRUE);
537 		}
538 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
539 
540 	return (B_FALSE);
541 }
542 
543 /*
544  * Call with the winreg_mutex held.
545  */
546 static char *
547 winreg_enum_subkey(ndr_xa_t *mxa, const char *subkey, uint32_t index)
548 {
549 	winreg_subkey_t *key;
550 	char *entry;
551 	char *p;
552 	int subkeylen;
553 	int count = 0;
554 
555 	if (subkey == NULL)
556 		return (NULL);
557 
558 	if (list_is_empty(&winreg_keylist.kl_list))
559 		return (NULL);
560 
561 	subkeylen = strlen(subkey);
562 
563 	for (key = list_head(&winreg_keylist.kl_list);
564 	    key != NULL; key = list_next(&winreg_keylist.kl_list, key)) {
565 		if (strncasecmp(subkey, key->sk_name, subkeylen) == 0) {
566 			p = key->sk_name + subkeylen;
567 
568 			if ((*p != '\\') || (*p == '\0')) {
569 				/*
570 				 * Not the same subkey or an exact match.
571 				 * We're looking for children of subkey.
572 				 */
573 				continue;
574 			}
575 
576 			++p;
577 
578 			if (count < index) {
579 				++count;
580 				continue;
581 			}
582 
583 			if ((entry = NDR_STRDUP(mxa, p)) == NULL)
584 				return (NULL);
585 
586 			if ((p = strchr(entry, '\\')) != NULL)
587 				*p = '\0';
588 
589 			return (entry);
590 		}
591 	}
592 
593 	return (NULL);
594 }
595 
596 /*
597  * winreg_s_DeleteValue
598  */
599 /*ARGSUSED*/
600 static int
601 winreg_s_DeleteValue(void *arg, ndr_xa_t *mxa)
602 {
603 	struct winreg_DeleteValue *param = arg;
604 
605 	param->status = ERROR_ACCESS_DENIED;
606 	return (NDR_DRC_OK);
607 }
608 
609 /*
610  * winreg_s_EnumKey
611  */
612 static int
613 winreg_s_EnumKey(void *arg, ndr_xa_t *mxa)
614 {
615 	struct winreg_EnumKey *param = arg;
616 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
617 	ndr_handle_t *hd;
618 	char *subkey;
619 	char *name = NULL;
620 
621 	(void) mutex_lock(&winreg_mutex);
622 
623 	if ((hd = ndr_hdlookup(mxa, id)) != NULL)
624 		name = hd->nh_data;
625 
626 	if (hd == NULL || name == NULL) {
627 		(void) mutex_unlock(&winreg_mutex);
628 		bzero(param, sizeof (struct winreg_EnumKey));
629 		param->status = ERROR_NO_MORE_ITEMS;
630 		return (NDR_DRC_OK);
631 	}
632 
633 	subkey = winreg_enum_subkey(mxa, name, param->index);
634 	if (subkey == NULL) {
635 		(void) mutex_unlock(&winreg_mutex);
636 		bzero(param, sizeof (struct winreg_EnumKey));
637 		param->status = ERROR_NO_MORE_ITEMS;
638 		return (NDR_DRC_OK);
639 	}
640 
641 	if (NDR_MSTRING(mxa, subkey, (ndr_mstring_t *)&param->name_out) == -1) {
642 		(void) mutex_unlock(&winreg_mutex);
643 		bzero(param, sizeof (struct winreg_EnumKey));
644 		param->status = ERROR_NOT_ENOUGH_MEMORY;
645 		return (NDR_DRC_OK);
646 	}
647 
648 	(void) mutex_unlock(&winreg_mutex);
649 
650 	/*
651 	 * This request requires that the length includes the null.
652 	 */
653 	param->name_out.length = param->name_out.allosize;
654 	param->status = ERROR_SUCCESS;
655 	return (NDR_DRC_OK);
656 }
657 
658 /*
659  * winreg_s_EnumValue
660  */
661 static int
662 winreg_s_EnumValue(void *arg, ndr_xa_t *mxa)
663 {
664 	struct winreg_EnumValue *param = arg;
665 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
666 
667 	if (ndr_hdlookup(mxa, id) == NULL) {
668 		bzero(param, sizeof (struct winreg_EnumValue));
669 		param->status = ERROR_NO_MORE_ITEMS;
670 		return (NDR_DRC_OK);
671 	}
672 
673 	bzero(param, sizeof (struct winreg_EnumValue));
674 	param->status = ERROR_NO_MORE_ITEMS;
675 	return (NDR_DRC_OK);
676 }
677 
678 /*
679  * winreg_s_FlushKey
680  *
681  * Flush the attributes associated with the specified open key to disk.
682  */
683 static int
684 winreg_s_FlushKey(void *arg, ndr_xa_t *mxa)
685 {
686 	struct winreg_FlushKey *param = arg;
687 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
688 
689 	if (ndr_hdlookup(mxa, id) == NULL)
690 		param->status = ERROR_INVALID_HANDLE;
691 	else
692 		param->status = ERROR_SUCCESS;
693 
694 	return (NDR_DRC_OK);
695 }
696 
697 /*
698  * winreg_s_GetKeySec
699  */
700 static int
701 winreg_s_GetKeySec(void *arg, ndr_xa_t *mxa)
702 {
703 	static struct winreg_secdesc	error_sd;
704 	struct winreg_GetKeySec		*param = arg;
705 	struct winreg_value		*sd_buf;
706 	smb_sd_t			sd;
707 	uint32_t			sd_len;
708 	uint32_t			status;
709 
710 	bzero(&sd, sizeof (smb_sd_t));
711 
712 	if ((status = winreg_sd_format(&sd)) != ERROR_SUCCESS)
713 		goto winreg_getkeysec_error;
714 
715 	sd_len = smb_sd_len(&sd, SMB_ALL_SECINFO);
716 	sd_buf = NDR_MALLOC(mxa, sd_len + sizeof (struct winreg_value));
717 
718 	param->sd = NDR_MALLOC(mxa, sizeof (struct winreg_secdesc));
719 	if ((param->sd == NULL) || (sd_buf == NULL)) {
720 		status = ERROR_NOT_ENOUGH_MEMORY;
721 		goto winreg_getkeysec_error;
722 	}
723 
724 	param->sd->sd_len = sd_len;
725 	param->sd->sd_size = sd_len;
726 	param->sd->sd_buf = sd_buf;
727 
728 	sd_buf->vc_first_is = 0;
729 	sd_buf->vc_length_is = sd_len;
730 	param->status = srvsvc_sd_set_relative(&sd, sd_buf->value);
731 
732 	smb_sd_term(&sd);
733 	return (NDR_DRC_OK);
734 
735 winreg_getkeysec_error:
736 	smb_sd_term(&sd);
737 	bzero(param, sizeof (struct winreg_GetKeySec));
738 	param->sd = &error_sd;
739 	param->status = status;
740 	return (NDR_DRC_OK);
741 }
742 
743 static uint32_t
744 winreg_sd_format(smb_sd_t *sd)
745 {
746 	smb_fssd_t	fs_sd;
747 	acl_t		*acl;
748 	uint32_t	status = ERROR_SUCCESS;
749 
750 	if (acl_fromtext("owner@:rwxpdDaARWcCos::allow", &acl) != 0)
751 		return (ERROR_NOT_ENOUGH_MEMORY);
752 
753 	smb_fssd_init(&fs_sd, SMB_ALL_SECINFO, SMB_FSSD_FLAGS_DIR);
754 	fs_sd.sd_uid = 0;
755 	fs_sd.sd_gid = 0;
756 	fs_sd.sd_zdacl = acl;
757 	fs_sd.sd_zsacl = NULL;
758 
759 	if (smb_sd_fromfs(&fs_sd, sd) != NT_STATUS_SUCCESS)
760 		status = ERROR_ACCESS_DENIED;
761 	smb_fssd_term(&fs_sd);
762 	return (status);
763 }
764 
765 /*
766  * winreg_s_NotifyChange
767  */
768 static int
769 winreg_s_NotifyChange(void *arg, ndr_xa_t *mxa)
770 {
771 	struct winreg_NotifyChange *param = arg;
772 
773 	if (ndr_is_admin(mxa))
774 		param->status = ERROR_SUCCESS;
775 	else
776 		param->status = ERROR_ACCESS_DENIED;
777 
778 	return (NDR_DRC_OK);
779 }
780 
781 /*
782  * winreg_s_OpenKey
783  *
784  * This is a request to open a windows registry key.
785  * If we recognize the key, we return a handle.
786  *
787  * Returns:
788  *	ERROR_SUCCESS		Valid handle returned.
789  *	ERROR_FILE_NOT_FOUND	No key or unable to allocate a handle.
790  */
791 static int
792 winreg_s_OpenKey(void *arg, ndr_xa_t *mxa)
793 {
794 	struct winreg_OpenKey *param = arg;
795 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
796 	ndr_handle_t *hd;
797 	char *subkey = (char *)param->name.str;
798 	winreg_subkey_t *key;
799 
800 	(void) mutex_lock(&winreg_mutex);
801 
802 	if (subkey == NULL || *subkey == '\0') {
803 		if ((hd = ndr_hdlookup(mxa, id)) != NULL)
804 			subkey = hd->nh_data;
805 	}
806 
807 	id = NULL;
808 
809 	if (subkey == NULL || list_is_empty(&winreg_keylist.kl_list)) {
810 		(void) mutex_unlock(&winreg_mutex);
811 		bzero(&param->result_handle, sizeof (winreg_handle_t));
812 		param->status = ERROR_FILE_NOT_FOUND;
813 		return (NDR_DRC_OK);
814 	}
815 
816 	key = list_head(&winreg_keylist.kl_list);
817 	do {
818 		if (strcasecmp(subkey, key->sk_name) == 0) {
819 			if (key->sk_predefined == B_TRUE)
820 				id = winreg_alloc_id(mxa, subkey);
821 			else
822 				id = &key->sk_handle;
823 
824 			if (id == NULL)
825 				break;
826 
827 			bcopy(id, &param->result_handle,
828 			    sizeof (winreg_handle_t));
829 
830 			(void) mutex_unlock(&winreg_mutex);
831 			param->status = ERROR_SUCCESS;
832 			return (NDR_DRC_OK);
833 		}
834 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
835 
836 	(void) mutex_unlock(&winreg_mutex);
837 	bzero(&param->result_handle, sizeof (winreg_handle_t));
838 	param->status = ERROR_FILE_NOT_FOUND;
839 	return (NDR_DRC_OK);
840 }
841 
842 /*
843  * winreg_s_QueryKey
844  */
845 /*ARGSUSED*/
846 static int
847 winreg_s_QueryKey(void *arg, ndr_xa_t *mxa)
848 {
849 	struct winreg_QueryKey *param = arg;
850 	int rc;
851 	winreg_string_t	*name;
852 
853 	name = (winreg_string_t	*)&param->name;
854 	bzero(param, sizeof (struct winreg_QueryKey));
855 
856 	if ((name = NDR_NEW(mxa, winreg_string_t)) != NULL)
857 		rc = NDR_MSTRING(mxa, "", (ndr_mstring_t *)name);
858 
859 	if ((name == NULL) || (rc != 0)) {
860 		bzero(param, sizeof (struct winreg_QueryKey));
861 		param->status = ERROR_NOT_ENOUGH_MEMORY;
862 		return (NDR_DRC_OK);
863 	}
864 
865 	param->status = ERROR_SUCCESS;
866 	return (NDR_DRC_OK);
867 }
868 
869 /*
870  * winreg_s_QueryValue
871  *
872  * This is a request to get the value associated with a specified name.
873  *
874  * Returns:
875  *	ERROR_SUCCESS		Value returned.
876  *	ERROR_FILE_NOT_FOUND	PrimaryModule is not supported.
877  *	ERROR_CANTREAD          No such name or memory problem.
878  */
879 static int
880 winreg_s_QueryValue(void *arg, ndr_xa_t *mxa)
881 {
882 	struct winreg_QueryValue *param = arg;
883 	struct winreg_value *pv;
884 	char *name;
885 	char *value;
886 	DWORD slen;
887 	DWORD msize;
888 
889 	name = (char *)param->value_name.str;
890 
891 	if (strcasecmp(name, "PrimaryModule") == 0) {
892 		param->status = ERROR_FILE_NOT_FOUND;
893 		return (NDR_DRC_OK);
894 	}
895 
896 	if ((value = winreg_lookup_value(name)) == NULL) {
897 		param->status = ERROR_CANTREAD;
898 		return (NDR_DRC_OK);
899 	}
900 
901 	slen = smb_wcequiv_strlen(value) + sizeof (smb_wchar_t);
902 	msize = sizeof (struct winreg_value) + slen;
903 
904 	param->value = (struct winreg_value *)NDR_MALLOC(mxa, msize);
905 	param->type = NDR_NEW(mxa, DWORD);
906 	param->value_size = NDR_NEW(mxa, DWORD);
907 	param->value_size_total = NDR_NEW(mxa, DWORD);
908 
909 	if (param->value == NULL || param->type == NULL ||
910 	    param->value_size == NULL || param->value_size_total == NULL) {
911 		param->status = ERROR_CANTREAD;
912 		return (NDR_DRC_OK);
913 	}
914 
915 	bzero(param->value, msize);
916 	pv = param->value;
917 	pv->vc_first_is = 0;
918 	pv->vc_length_is = slen;
919 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
920 	(void) ndr_mbstowcs(NULL, (smb_wchar_t *)pv->value, value, slen);
921 
922 	*param->type = 1;
923 	*param->value_size = slen;
924 	*param->value_size_total = slen;
925 
926 	param->status = ERROR_SUCCESS;
927 	return (NDR_DRC_OK);
928 }
929 
930 /*
931  * Lookup a name in the registry and return the associated value.
932  * Our registry is a case-insensitive, name-value pair table.
933  *
934  * Windows ProductType: WinNT, ServerNT, LanmanNT.
935  *	Windows NT4.0 workstation: WinNT
936  *	Windows NT4.0 server:      ServerNT
937  *
938  * If LanmanNT is used here, Windows 2000 sends LsarQueryInfoPolicy
939  * with info level 6, which we don't support.  If we use ServerNT
940  * (as reported by NT4.0 Server) Windows 2000 send requests for
941  * levels 3 and 5, which are support.
942  *
943  * On success, returns a pointer to the value.  Otherwise returns
944  * a null pointer.
945  */
946 static char *
947 winreg_lookup_value(const char *name)
948 {
949 	static struct registry {
950 		char *name;
951 		char *value;
952 	} registry[] = {
953 		{ "SystemRoot",		"C:\\" },
954 		{ "CurrentVersion",	winreg_sysver },
955 		{ "ProductType",	"ServerNT" },
956 		{ "Sources",		winreg_sysname }, /* product name */
957 		{ "EventMessageFile",	"C:\\windows\\system32\\eventlog.dll" }
958 	};
959 
960 	int i;
961 
962 	for (i = 0; i < sizeof (registry)/sizeof (registry[0]); ++i) {
963 		if (strcasecmp(registry[i].name, name) == 0)
964 			return (registry[i].value);
965 	}
966 
967 	return (NULL);
968 }
969 
970 /*
971  * winreg_s_SetKeySec
972  */
973 /*ARGSUSED*/
974 static int
975 winreg_s_SetKeySec(void *arg, ndr_xa_t *mxa)
976 {
977 	struct winreg_SetKeySec *param = arg;
978 
979 	param->status = ERROR_ACCESS_DENIED;
980 	return (NDR_DRC_OK);
981 }
982 
983 /*
984  * winreg_s_CreateValue
985  */
986 /*ARGSUSED*/
987 static int
988 winreg_s_CreateValue(void *arg, ndr_xa_t *mxa)
989 {
990 	struct winreg_CreateValue *param = arg;
991 
992 	param->status = ERROR_ACCESS_DENIED;
993 	return (NDR_DRC_OK);
994 }
995 
996 /*
997  * winreg_s_Shutdown
998  *
999  * Attempt to shutdown or reboot the system: access denied.
1000  */
1001 /*ARGSUSED*/
1002 static int
1003 winreg_s_Shutdown(void *arg, ndr_xa_t *mxa)
1004 {
1005 	struct winreg_Shutdown *param = arg;
1006 
1007 	param->status = ERROR_ACCESS_DENIED;
1008 	return (NDR_DRC_OK);
1009 }
1010 
1011 /*
1012  * winreg_s_AbortShutdown
1013  *
1014  * Abort a shutdown request.
1015  */
1016 static int
1017 winreg_s_AbortShutdown(void *arg, ndr_xa_t *mxa)
1018 {
1019 	struct winreg_AbortShutdown *param = arg;
1020 
1021 	if (ndr_is_admin(mxa))
1022 		param->status = ERROR_SUCCESS;
1023 	else
1024 		param->status = ERROR_ACCESS_DENIED;
1025 
1026 	return (NDR_DRC_OK);
1027 }
1028 
1029 /*
1030  * winreg_s_GetVersion
1031  *
1032  * Return the windows registry version.  The current version is 5.
1033  * This call is usually made prior to enumerating or querying registry
1034  * keys or values.
1035  */
1036 /*ARGSUSED*/
1037 static int
1038 winreg_s_GetVersion(void *arg, ndr_xa_t *mxa)
1039 {
1040 	struct winreg_GetVersion *param = arg;
1041 
1042 	param->version = 5;
1043 	param->status = ERROR_SUCCESS;
1044 	return (NDR_DRC_OK);
1045 }
1046