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