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