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