xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/winreg_svc.c (revision 6e375c8351497b82ffa4f33cbf61d712999b4605)
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  * Windows Registry RPC (WINREG) server-side interface.
28  *
29  * The WINREG RPC interface returns Win32 error codes.
30  *
31  * HKLM		Hive Key Local Machine
32  * HKU		Hive Key Users
33  */
34 
35 #include <sys/utsname.h>
36 #include <strings.h>
37 
38 #include <smbsrv/libsmb.h>
39 #include <smbsrv/ntstatus.h>
40 #include <smbsrv/nterror.h>
41 #include <smbsrv/nmpipes.h>
42 #include <smbsrv/libmlsvc.h>
43 #include <smbsrv/ndl/winreg.ndl>
44 
45 #define	WINREG_LOGR_SYSTEMKEY	\
46 	"System\\CurrentControlSet\\Services\\Eventlog\\System"
47 
48 /*
49  * Local handle management keys.
50  */
51 static int winreg_hk;
52 static int winreg_hklm;
53 static int winreg_hkuser;
54 static int winreg_hkkey;
55 
56 /*
57  * List of supported registry keys (case-insensitive).
58  */
59 static char *winreg_keys[] = {
60 	"System\\CurrentControlSet\\Services\\Eventlog",
61 	"System\\CurrentControlSet\\Services\\Eventlog\\System",
62 	"System\\CurrentControlSet\\Control\\ProductOptions",
63 	"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
64 };
65 
66 typedef struct winreg_subkey {
67 	list_node_t sk_lnd;
68 	ndr_hdid_t sk_handle;
69 	char sk_name[MAXPATHLEN];
70 	boolean_t sk_predefined;
71 } winreg_subkey_t;
72 
73 typedef struct winreg_keylist {
74 	list_t kl_list;
75 	int kl_count;
76 } winreg_keylist_t;
77 
78 static winreg_keylist_t winreg_keylist;
79 
80 static boolean_t winreg_key_has_subkey(const char *);
81 static char *winreg_lookup_value(const char *);
82 static char *winreg_lookup_eventlog_registry(char *, char *);
83 
84 static int winreg_s_OpenHK(void *, ndr_xa_t *);
85 static int winreg_s_OpenHKLM(void *, ndr_xa_t *);
86 static int winreg_s_OpenHKUsers(void *, ndr_xa_t *);
87 static int winreg_s_Close(void *, ndr_xa_t *);
88 static int winreg_s_CreateKey(void *, ndr_xa_t *);
89 static int winreg_s_DeleteKey(void *, ndr_xa_t *);
90 static int winreg_s_DeleteValue(void *, ndr_xa_t *);
91 static int winreg_s_EnumKey(void *, ndr_xa_t *);
92 static int winreg_s_EnumValue(void *, ndr_xa_t *);
93 static int winreg_s_FlushKey(void *, ndr_xa_t *);
94 static int winreg_s_GetKeySec(void *, ndr_xa_t *);
95 static int winreg_s_NotifyChange(void *, ndr_xa_t *);
96 static int winreg_s_OpenKey(void *, ndr_xa_t *);
97 static int winreg_s_QueryKey(void *, ndr_xa_t *);
98 static int winreg_s_QueryValue(void *, ndr_xa_t *);
99 static int winreg_s_SetKeySec(void *, ndr_xa_t *);
100 static int winreg_s_CreateValue(void *, ndr_xa_t *);
101 static int winreg_s_Shutdown(void *, ndr_xa_t *);
102 static int winreg_s_AbortShutdown(void *, ndr_xa_t *);
103 static int winreg_s_GetVersion(void *, ndr_xa_t *);
104 
105 static ndr_stub_table_t winreg_stub_table[] = {
106 	{ winreg_s_OpenHK,	WINREG_OPNUM_OpenHKCR },
107 	{ winreg_s_OpenHK,	WINREG_OPNUM_OpenHKCU },
108 	{ winreg_s_OpenHKLM,	WINREG_OPNUM_OpenHKLM },
109 	{ winreg_s_OpenHK,	WINREG_OPNUM_OpenHKPD },
110 	{ winreg_s_OpenHKUsers,	WINREG_OPNUM_OpenHKUsers },
111 	{ winreg_s_Close,	WINREG_OPNUM_Close },
112 	{ winreg_s_CreateKey,	WINREG_OPNUM_CreateKey },
113 	{ winreg_s_DeleteKey,	WINREG_OPNUM_DeleteKey },
114 	{ winreg_s_DeleteValue,	WINREG_OPNUM_DeleteValue },
115 	{ winreg_s_EnumKey,	WINREG_OPNUM_EnumKey },
116 	{ winreg_s_EnumValue,	WINREG_OPNUM_EnumValue },
117 	{ winreg_s_FlushKey,	WINREG_OPNUM_FlushKey },
118 	{ winreg_s_GetKeySec,	WINREG_OPNUM_GetKeySec },
119 	{ winreg_s_NotifyChange,	WINREG_OPNUM_NotifyChange },
120 	{ winreg_s_OpenKey,	WINREG_OPNUM_OpenKey },
121 	{ winreg_s_QueryKey,	WINREG_OPNUM_QueryKey },
122 	{ winreg_s_QueryValue,	WINREG_OPNUM_QueryValue },
123 	{ winreg_s_SetKeySec,	WINREG_OPNUM_SetKeySec },
124 	{ winreg_s_CreateValue,	WINREG_OPNUM_CreateValue },
125 	{ winreg_s_Shutdown,	WINREG_OPNUM_Shutdown },
126 	{ winreg_s_AbortShutdown,	WINREG_OPNUM_AbortShutdown },
127 	{ winreg_s_GetVersion,	WINREG_OPNUM_GetVersion },
128 	{ winreg_s_OpenHK,	WINREG_OPNUM_OpenHKCC },
129 	{ winreg_s_OpenHK,	WINREG_OPNUM_OpenHKDD },
130 	{ winreg_s_OpenHK,	WINREG_OPNUM_OpenHKPT },
131 	{ winreg_s_OpenHK,	WINREG_OPNUM_OpenHKPN },
132 	{0}
133 };
134 
135 static ndr_service_t winreg_service = {
136 	"Winreg",			/* name */
137 	"Windows Registry",		/* desc */
138 	"\\winreg",			/* endpoint */
139 	PIPE_WINREG,			/* sec_addr_port */
140 	"338cd001-2244-31f1-aaaa-900038001003", 1,	/* abstract */
141 	NDR_TRANSFER_SYNTAX_UUID,		2,	/* transfer */
142 	0,				/* no bind_instance_size */
143 	0,				/* no bind_req() */
144 	0,				/* no unbind_and_close() */
145 	0,				/* use generic_call_stub() */
146 	&TYPEINFO(winreg_interface),	/* interface ti */
147 	winreg_stub_table		/* stub_table */
148 };
149 
150 static char winreg_sysname[SYS_NMLN];
151 
152 /*
153  * winreg_initialize
154  *
155  * Initialize and register the WINREG RPC interface with the RPC runtime
156  * library. It must be called in order to use either the client side
157  * or the server side functions.
158  */
159 void
160 winreg_initialize(void)
161 {
162 	winreg_subkey_t *key;
163 	struct utsname name;
164 	char *sysname;
165 	int i;
166 
167 	list_create(&winreg_keylist.kl_list, sizeof (winreg_subkey_t),
168 	    offsetof(winreg_subkey_t, sk_lnd));
169 	winreg_keylist.kl_count = 0;
170 
171 	for (i = 0; i < sizeof (winreg_keys)/sizeof (winreg_keys[0]); ++i) {
172 		if ((key = malloc(sizeof (winreg_subkey_t))) != NULL) {
173 			bzero(key, sizeof (winreg_subkey_t));
174 			(void) strlcpy(key->sk_name, winreg_keys[i],
175 			    MAXPATHLEN);
176 			key->sk_predefined = B_TRUE;
177 			list_insert_tail(&winreg_keylist.kl_list, key);
178 			++winreg_keylist.kl_count;
179 		}
180 	}
181 
182 	if (uname(&name) < 0)
183 		sysname = "Solaris";
184 	else
185 		sysname = name.sysname;
186 
187 	(void) strlcpy(winreg_sysname, sysname, SYS_NMLN);
188 	(void) ndr_svc_register(&winreg_service);
189 }
190 
191 /*
192  * winreg_s_OpenHK
193  *
194  * Stub.
195  */
196 static int
197 winreg_s_OpenHK(void *arg, ndr_xa_t *mxa)
198 {
199 	struct winreg_OpenHKCR *param = arg;
200 	ndr_hdid_t *id;
201 
202 	if ((id = ndr_hdalloc(mxa, &winreg_hk)) == NULL) {
203 		bzero(&param->handle, sizeof (winreg_handle_t));
204 		param->status = ERROR_ACCESS_DENIED;
205 	} else {
206 		bcopy(id, &param->handle, sizeof (winreg_handle_t));
207 		param->status = ERROR_SUCCESS;
208 	}
209 
210 	return (NDR_DRC_OK);
211 }
212 
213 /*
214  * winreg_s_OpenHKLM
215  *
216  * This is a request to open the HKLM and get a handle.
217  * The client should treat the handle as an opaque object.
218  *
219  * Status:
220  *	ERROR_SUCCESS		Valid handle returned.
221  *	ERROR_ACCESS_DENIED	Unable to allocate a handle.
222  */
223 static int
224 winreg_s_OpenHKLM(void *arg, ndr_xa_t *mxa)
225 {
226 	struct winreg_OpenHKLM *param = arg;
227 	ndr_hdid_t *id;
228 
229 	if ((id = ndr_hdalloc(mxa, &winreg_hklm)) == NULL) {
230 		bzero(&param->handle, sizeof (winreg_handle_t));
231 		param->status = ERROR_ACCESS_DENIED;
232 	} else {
233 		bcopy(id, &param->handle, sizeof (winreg_handle_t));
234 		param->status = ERROR_SUCCESS;
235 	}
236 
237 	return (NDR_DRC_OK);
238 }
239 
240 /*
241  * winreg_s_OpenHKUsers
242  *
243  * This is a request to get a HKUsers handle. I'm not sure we are
244  * ready to fully support this interface yet, mostly due to the need
245  * to support subsequent requests, but we may support enough now. It
246  * seems okay with regedt32.
247  */
248 static int
249 winreg_s_OpenHKUsers(void *arg, ndr_xa_t *mxa)
250 {
251 	struct winreg_OpenHKUsers *param = arg;
252 	ndr_hdid_t *id;
253 
254 	if ((id = ndr_hdalloc(mxa, &winreg_hkuser)) == NULL) {
255 		bzero(&param->handle, sizeof (winreg_handle_t));
256 		param->status = ERROR_ACCESS_DENIED;
257 	} else {
258 		bcopy(id, &param->handle, sizeof (winreg_handle_t));
259 		param->status = ERROR_SUCCESS;
260 	}
261 
262 	return (NDR_DRC_OK);
263 }
264 
265 /*
266  * winreg_s_Close
267  *
268  * This is a request to close the WINREG interface specified by the
269  * handle. We don't track handles (yet), so just zero out the handle
270  * and return NDR_DRC_OK. Setting the handle to zero appears to be
271  * standard behaviour.
272  */
273 static int
274 winreg_s_Close(void *arg, ndr_xa_t *mxa)
275 {
276 	struct winreg_Close *param = arg;
277 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
278 
279 	ndr_hdfree(mxa, id);
280 
281 	bzero(&param->result_handle, sizeof (winreg_handle_t));
282 	param->status = ERROR_SUCCESS;
283 	return (NDR_DRC_OK);
284 }
285 
286 /*
287  * winreg_s_CreateKey
288  */
289 static int
290 winreg_s_CreateKey(void *arg, ndr_xa_t *mxa)
291 {
292 	struct winreg_CreateKey *param = arg;
293 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
294 	ndr_handle_t *hd;
295 	winreg_subkey_t *key;
296 	char *subkey;
297 	DWORD *action;
298 
299 	subkey = (char *)param->subkey.str;
300 
301 	if (!ndr_is_admin(mxa) || (subkey == NULL)) {
302 		bzero(param, sizeof (struct winreg_CreateKey));
303 		param->status = ERROR_ACCESS_DENIED;
304 		return (NDR_DRC_OK);
305 	}
306 
307 	hd = ndr_hdlookup(mxa, id);
308 	if (hd == NULL) {
309 		bzero(param, sizeof (struct winreg_CreateKey));
310 		param->status = ERROR_INVALID_HANDLE;
311 		return (NDR_DRC_OK);
312 	}
313 
314 	if ((action = NDR_NEW(mxa, DWORD)) == NULL) {
315 		bzero(param, sizeof (struct winreg_CreateKey));
316 		param->status = ERROR_NOT_ENOUGH_MEMORY;
317 		return (NDR_DRC_OK);
318 	}
319 
320 	if (list_is_empty(&winreg_keylist.kl_list))
321 		goto new_key;
322 
323 	/*
324 	 * Check for an existing key.
325 	 */
326 	key = list_head(&winreg_keylist.kl_list);
327 	do {
328 		if (strcasecmp(subkey, key->sk_name) == 0) {
329 			bcopy(&key->sk_handle, &param->result_handle,
330 			    sizeof (winreg_handle_t));
331 			*action = WINREG_ACTION_EXISTING_KEY;
332 			param->action = action;
333 			param->status = ERROR_SUCCESS;
334 			return (NDR_DRC_OK);
335 		}
336 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
337 
338 new_key:
339 	/*
340 	 * Create a new key.
341 	 */
342 	id = ndr_hdalloc(mxa, &winreg_hkkey);
343 	key = malloc(sizeof (winreg_subkey_t));
344 
345 	if ((id == NULL) || (key == NULL)) {
346 		bzero(param, sizeof (struct winreg_CreateKey));
347 		param->status = ERROR_NOT_ENOUGH_MEMORY;
348 		return (NDR_DRC_OK);
349 	}
350 
351 	bcopy(id, &key->sk_handle, sizeof (ndr_hdid_t));
352 	(void) strlcpy(key->sk_name, subkey, MAXPATHLEN);
353 	key->sk_predefined = B_FALSE;
354 	list_insert_tail(&winreg_keylist.kl_list, key);
355 	++winreg_keylist.kl_count;
356 
357 	bcopy(id, &param->result_handle, sizeof (winreg_handle_t));
358 	*action = WINREG_ACTION_NEW_KEY;
359 	param->action = action;
360 	param->status = ERROR_SUCCESS;
361 	return (NDR_DRC_OK);
362 }
363 
364 /*
365  * winreg_s_DeleteKey
366  */
367 static int
368 winreg_s_DeleteKey(void *arg, ndr_xa_t *mxa)
369 {
370 	struct winreg_DeleteKey *param = arg;
371 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
372 	winreg_subkey_t *key;
373 	char *subkey;
374 
375 	subkey = (char *)param->subkey.str;
376 
377 	if (!ndr_is_admin(mxa) || (subkey == NULL)) {
378 		param->status = ERROR_ACCESS_DENIED;
379 		return (NDR_DRC_OK);
380 	}
381 
382 	if ((ndr_hdlookup(mxa, id) == NULL) ||
383 	    list_is_empty(&winreg_keylist.kl_list) ||
384 	    winreg_key_has_subkey(subkey)) {
385 		param->status = ERROR_ACCESS_DENIED;
386 		return (NDR_DRC_OK);
387 	}
388 
389 	key = list_head(&winreg_keylist.kl_list);
390 	do {
391 		if (strcasecmp(subkey, key->sk_name) == 0) {
392 			if (key->sk_predefined == B_TRUE) {
393 				/* Predefined keys cannot be deleted */
394 				break;
395 			}
396 
397 			list_remove(&winreg_keylist.kl_list, key);
398 			--winreg_keylist.kl_count;
399 			ndr_hdfree(mxa, &key->sk_handle);
400 			free(key);
401 			param->status = ERROR_SUCCESS;
402 			return (NDR_DRC_OK);
403 		}
404 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
405 
406 	param->status = ERROR_ACCESS_DENIED;
407 	return (NDR_DRC_OK);
408 }
409 
410 static boolean_t
411 winreg_key_has_subkey(const char *subkey)
412 {
413 	winreg_subkey_t *key;
414 	int keylen;
415 
416 	if (list_is_empty(&winreg_keylist.kl_list))
417 		return (B_FALSE);
418 
419 	keylen = strlen(subkey);
420 
421 	key = list_head(&winreg_keylist.kl_list);
422 	do {
423 		if (strncasecmp(subkey, key->sk_name, keylen) == 0) {
424 			/*
425 			 * Potential match.  If sk_name is longer than
426 			 * subkey, then sk_name is a subkey of our key.
427 			 */
428 			if (keylen < strlen(key->sk_name))
429 				return (B_TRUE);
430 		}
431 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
432 
433 	return (B_FALSE);
434 }
435 
436 /*
437  * winreg_subkey_get_relative_name
438  *
439  * Each key contains one or more child keys, each called a subkey.
440  * For any specified key, its name MUST be unique for any other subkeys that
441  * have the same parent key.
442  *
443  * To accurately identify a given subkey within the key namespace, its fully
444  * qualified name (FQN) is used. The FQN MUST consist of the name of the subkey
445  * and the name of all of its parent keys all the way to the root of the tree.
446  *
447  * The "\" character MUST be used as a hierarchy separator to identify each key
448  * in the FQN and therefore MUST not be used in the name of a single key.
449  * For example, the subkey "MountedDevices" belongs to the subtree
450  * HKEY_LOCAL_MACHINE, as shown in the following example.
451  *
452  *	HKEY_LOCAL_MACHINE -> SYSTEM -> MountedDevices
453  *
454  * The FQN for MountedDevices is HKEY_LOCAL_MACHINE\SYSTEM\MountedDevices.
455  * The relative name of the subkey is "MountedDevices". The relative name
456  * MUST be used only for operations that are performed on its immediate parent
457  * key (SYSTEM in the previous example).
458  */
459 static char *
460 winreg_subkey_get_relative_name(const char *subkey)
461 {
462 	winreg_subkey_t *key;
463 	char *value;
464 
465 	if (subkey == NULL)
466 		return (NULL);
467 
468 	if (list_is_empty(&winreg_keylist.kl_list))
469 		return (NULL);
470 
471 	key = list_head(&winreg_keylist.kl_list);
472 	do {
473 		if (strcasecmp(subkey, key->sk_name) == 0) {
474 			value = strrchr(key->sk_name, '\\');
475 			if (value != NULL)
476 				return (++value);
477 		}
478 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
479 
480 	return (NULL);
481 }
482 
483 /*
484  * winreg_s_DeleteValue
485  */
486 /*ARGSUSED*/
487 static int
488 winreg_s_DeleteValue(void *arg, ndr_xa_t *mxa)
489 {
490 	struct winreg_DeleteValue *param = arg;
491 
492 	param->status = ERROR_ACCESS_DENIED;
493 	return (NDR_DRC_OK);
494 }
495 
496 /*
497  * winreg_s_EnumKey
498  */
499 static int
500 winreg_s_EnumKey(void *arg, ndr_xa_t *mxa)
501 {
502 	struct winreg_EnumKey *param = arg;
503 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
504 	winreg_string_t	*name, *class;
505 	char *value, *namep = NULL, *classp = NULL;
506 	int slen = 0;
507 
508 	if (ndr_hdlookup(mxa, id) == NULL) {
509 		bzero(param, sizeof (struct winreg_EnumKey));
510 		param->status = ERROR_NO_MORE_ITEMS;
511 		return (NDR_DRC_OK);
512 	}
513 
514 	if (param->index > 0) {
515 		bzero(param, sizeof (struct winreg_EnumKey));
516 		param->status = ERROR_NO_MORE_ITEMS;
517 		return (NDR_DRC_OK);
518 	}
519 
520 	name = (winreg_string_t	*)&param->name_in;
521 	class = (winreg_string_t *)&param->class_in;
522 	if (name->length != 0)
523 		namep = (char *)name->str;
524 
525 	if (class->length != 0)
526 		classp = (char *)class->str;
527 
528 	value = winreg_lookup_eventlog_registry(namep, classp);
529 	if (value == NULL) {
530 		bzero(param, sizeof (struct winreg_EnumKey));
531 		param->status = ERROR_CANTREAD;
532 		return (NDR_DRC_OK);
533 	}
534 
535 	slen = mts_wcequiv_strlen(value) + sizeof (mts_wchar_t);
536 	param->name_out.length = slen;
537 	param->name_out.allosize = slen;
538 	if ((param->name_out.str = NDR_STRDUP(mxa, value)) == NULL) {
539 		bzero(param, sizeof (struct winreg_EnumKey));
540 		param->status = ERROR_NOT_ENOUGH_MEMORY;
541 		return (NDR_DRC_OK);
542 	}
543 
544 	param->status = ERROR_SUCCESS;
545 	return (NDR_DRC_OK);
546 }
547 
548 /*
549  * winreg_s_EnumValue
550  */
551 static int
552 winreg_s_EnumValue(void *arg, ndr_xa_t *mxa)
553 {
554 	struct winreg_EnumValue *param = arg;
555 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
556 
557 	if (ndr_hdlookup(mxa, id) == NULL) {
558 		bzero(param, sizeof (struct winreg_EnumValue));
559 		param->status = ERROR_NO_MORE_ITEMS;
560 		return (NDR_DRC_OK);
561 	}
562 
563 	bzero(param, sizeof (struct winreg_EnumValue));
564 	param->status = ERROR_NO_MORE_ITEMS;
565 	return (NDR_DRC_OK);
566 }
567 
568 /*
569  * winreg_s_FlushKey
570  *
571  * Flush the attributes associated with the specified open key to disk.
572  */
573 static int
574 winreg_s_FlushKey(void *arg, ndr_xa_t *mxa)
575 {
576 	struct winreg_FlushKey *param = arg;
577 	ndr_hdid_t *id = (ndr_hdid_t *)&param->handle;
578 
579 	if (ndr_hdlookup(mxa, id) == NULL)
580 		param->status = ERROR_INVALID_HANDLE;
581 	else
582 		param->status = ERROR_SUCCESS;
583 
584 	return (NDR_DRC_OK);
585 }
586 
587 /*
588  * winreg_s_GetKeySec
589  */
590 /*ARGSUSED*/
591 static int
592 winreg_s_GetKeySec(void *arg, ndr_xa_t *mxa)
593 {
594 	struct winreg_GetKeySec *param = arg;
595 
596 	bzero(param, sizeof (struct winreg_GetKeySec));
597 	param->status = ERROR_ACCESS_DENIED;
598 	return (NDR_DRC_OK);
599 }
600 
601 /*
602  * winreg_s_NotifyChange
603  */
604 static int
605 winreg_s_NotifyChange(void *arg, ndr_xa_t *mxa)
606 {
607 	struct winreg_NotifyChange *param = arg;
608 
609 	if (ndr_is_admin(mxa))
610 		param->status = ERROR_SUCCESS;
611 	else
612 		param->status = ERROR_ACCESS_DENIED;
613 
614 	return (NDR_DRC_OK);
615 }
616 
617 /*
618  * winreg_s_OpenKey
619  *
620  * This is a request to open a windows registry key.
621  * If we recognize the key, we return a handle.
622  *
623  * Returns:
624  *	ERROR_SUCCESS		Valid handle returned.
625  *	ERROR_FILE_NOT_FOUND	No key or unable to allocate a handle.
626  */
627 static int
628 winreg_s_OpenKey(void *arg, ndr_xa_t *mxa)
629 {
630 	struct winreg_OpenKey *param = arg;
631 	char *subkey = (char *)param->name.str;
632 	ndr_hdid_t *id = NULL;
633 	winreg_subkey_t *key;
634 
635 	if (list_is_empty(&winreg_keylist.kl_list)) {
636 		bzero(&param->result_handle, sizeof (winreg_handle_t));
637 		param->status = ERROR_FILE_NOT_FOUND;
638 		return (NDR_DRC_OK);
639 	}
640 
641 	key = list_head(&winreg_keylist.kl_list);
642 	do {
643 		if (strcasecmp(subkey, key->sk_name) == 0) {
644 			if (key->sk_predefined == B_TRUE)
645 				id = ndr_hdalloc(mxa, &winreg_hkkey);
646 			else
647 				id = &key->sk_handle;
648 
649 			if (id == NULL)
650 				break;
651 
652 			bcopy(id, &param->result_handle,
653 			    sizeof (winreg_handle_t));
654 			param->status = ERROR_SUCCESS;
655 			return (NDR_DRC_OK);
656 		}
657 	} while ((key = list_next(&winreg_keylist.kl_list, key)) != NULL);
658 
659 	bzero(&param->result_handle, sizeof (winreg_handle_t));
660 	param->status = ERROR_FILE_NOT_FOUND;
661 	return (NDR_DRC_OK);
662 }
663 
664 /*
665  * winreg_s_QueryKey
666  */
667 /*ARGSUSED*/
668 static int
669 winreg_s_QueryKey(void *arg, ndr_xa_t *mxa)
670 {
671 	struct winreg_QueryKey *param = arg;
672 	int rc;
673 	winreg_string_t	*name;
674 
675 	name = (winreg_string_t	*)&param->name;
676 	bzero(param, sizeof (struct winreg_QueryKey));
677 	if ((name = NDR_NEW(mxa, winreg_string_t)) != NULL)
678 		rc = NDR_MSTRING(mxa, "", (ndr_mstring_t *)name);
679 
680 	if ((name == NULL) || (rc != 0)) {
681 		bzero(param, sizeof (struct winreg_QueryKey));
682 		param->status = ERROR_NOT_ENOUGH_MEMORY;
683 		return (NDR_DRC_OK);
684 	}
685 
686 	param->status = ERROR_SUCCESS;
687 	return (NDR_DRC_OK);
688 }
689 
690 /*
691  * winreg_s_QueryValue
692  *
693  * This is a request to get the value associated with a specified name.
694  *
695  * Returns:
696  *	ERROR_SUCCESS		Value returned.
697  *	ERROR_FILE_NOT_FOUND	PrimaryModule is not supported.
698  *	ERROR_CANTREAD          No such name or memory problem.
699  */
700 static int
701 winreg_s_QueryValue(void *arg, ndr_xa_t *mxa)
702 {
703 	struct winreg_QueryValue *param = arg;
704 	struct winreg_value *pv;
705 	char *name;
706 	char *value;
707 	DWORD slen;
708 	DWORD msize;
709 
710 	name = (char *)param->value_name.str;
711 
712 	if (strcasecmp(name, "PrimaryModule") == 0) {
713 		param->status = ERROR_FILE_NOT_FOUND;
714 		return (NDR_DRC_OK);
715 	}
716 
717 	if ((value = winreg_lookup_value(name)) == NULL) {
718 		param->status = ERROR_CANTREAD;
719 		return (NDR_DRC_OK);
720 	}
721 
722 	slen = mts_wcequiv_strlen(value) + sizeof (mts_wchar_t);
723 	msize = sizeof (struct winreg_value) + slen;
724 
725 	param->value = (struct winreg_value *)NDR_MALLOC(mxa, msize);
726 	param->type = NDR_NEW(mxa, DWORD);
727 	param->value_size = NDR_NEW(mxa, DWORD);
728 	param->value_size_total = NDR_NEW(mxa, DWORD);
729 
730 	if (param->value == NULL || param->type == NULL ||
731 	    param->value_size == NULL || param->value_size_total == NULL) {
732 		param->status = ERROR_CANTREAD;
733 		return (NDR_DRC_OK);
734 	}
735 
736 	bzero(param->value, msize);
737 	pv = param->value;
738 	pv->vc_first_is = 0;
739 	pv->vc_length_is = slen;
740 	/*LINTED E_BAD_PTR_CAST_ALIGN*/
741 	(void) ndr_mbstowcs(NULL, (mts_wchar_t *)pv->value, value, slen);
742 
743 	*param->type = 1;
744 	*param->value_size = slen;
745 	*param->value_size_total = slen;
746 
747 	param->status = ERROR_SUCCESS;
748 	return (NDR_DRC_OK);
749 }
750 
751 /*
752  * Lookup a name in the registry and return the associated value.
753  * Our registry is a case-insensitive, name-value pair table.
754  *
755  * Windows ProductType: WinNT, ServerNT, LanmanNT.
756  *	Windows NT4.0 workstation: WinNT
757  *	Windows NT4.0 server:      ServerNT
758  *
759  * If LanmanNT is used here, Windows 2000 sends LsarQueryInfoPolicy
760  * with info level 6, which we don't support.  If we use ServerNT
761  * (as reported by NT4.0 Server) Windows 2000 send requests for
762  * levels 3 and 5, which are support.
763  *
764  * On success, returns a pointer to the value.  Otherwise returns
765  * a null pointer.
766  */
767 static char *
768 winreg_lookup_value(const char *name)
769 {
770 	static struct registry {
771 		char *name;
772 		char *value;
773 	} registry[] = {
774 		{ "ProductType", "ServerNT" },
775 		{ "Sources",	 NULL }	/* product name */
776 	};
777 
778 	int i;
779 
780 	for (i = 0; i < sizeof (registry)/sizeof (registry[0]); ++i) {
781 		if (strcasecmp(registry[i].name, name) == 0) {
782 			if (registry[i].value == NULL)
783 				return (winreg_sysname);
784 			else
785 				return (registry[i].value);
786 		}
787 	}
788 
789 	return (NULL);
790 }
791 
792 /*
793  * winreg_lookup_eventlog_registry
794  *
795  * Return the subkey of the specified EventLog key. Decoding of
796  * class paramater not yet supported.
797  */
798 /*ARGSUSED*/
799 static char *
800 winreg_lookup_eventlog_registry(char *name, char *class)
801 {
802 	if (name == NULL)
803 		return (winreg_subkey_get_relative_name(WINREG_LOGR_SYSTEMKEY));
804 
805 	return (winreg_subkey_get_relative_name(name));
806 }
807 
808 /*
809  * winreg_s_SetKeySec
810  */
811 /*ARGSUSED*/
812 static int
813 winreg_s_SetKeySec(void *arg, ndr_xa_t *mxa)
814 {
815 	struct winreg_SetKeySec *param = arg;
816 
817 	param->status = ERROR_ACCESS_DENIED;
818 	return (NDR_DRC_OK);
819 }
820 
821 /*
822  * winreg_s_CreateValue
823  */
824 /*ARGSUSED*/
825 static int
826 winreg_s_CreateValue(void *arg, ndr_xa_t *mxa)
827 {
828 	struct winreg_CreateValue *param = arg;
829 
830 	param->status = ERROR_ACCESS_DENIED;
831 	return (NDR_DRC_OK);
832 }
833 
834 /*
835  * winreg_s_Shutdown
836  *
837  * Attempt to shutdown or reboot the system: access denied.
838  */
839 /*ARGSUSED*/
840 static int
841 winreg_s_Shutdown(void *arg, ndr_xa_t *mxa)
842 {
843 	struct winreg_Shutdown *param = arg;
844 
845 	param->status = ERROR_ACCESS_DENIED;
846 	return (NDR_DRC_OK);
847 }
848 
849 /*
850  * winreg_s_AbortShutdown
851  *
852  * Abort a shutdown request.
853  */
854 static int
855 winreg_s_AbortShutdown(void *arg, ndr_xa_t *mxa)
856 {
857 	struct winreg_AbortShutdown *param = arg;
858 
859 	if (ndr_is_admin(mxa))
860 		param->status = ERROR_SUCCESS;
861 	else
862 		param->status = ERROR_ACCESS_DENIED;
863 
864 	return (NDR_DRC_OK);
865 }
866 
867 /*
868  * winreg_s_GetVersion
869  *
870  * Return the windows registry version.  The current version is 5.
871  * This call is usually made prior to enumerating or querying registry
872  * keys or values.
873  */
874 /*ARGSUSED*/
875 static int
876 winreg_s_GetVersion(void *arg, ndr_xa_t *mxa)
877 {
878 	struct winreg_GetVersion *param = arg;
879 
880 	param->version = 5;
881 	param->status = ERROR_SUCCESS;
882 	return (NDR_DRC_OK);
883 }
884