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