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