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