winreg_svc.c revision 12508:edb7861a1533
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 */
60static 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
75static char *winreg_eventlog = "System\\CurrentControlSet\\Services\\Eventlog";
76
77static char *winreg_log[] = {
78	"Application",
79	"Security",
80	"System",
81	"smbd",
82	"smbrdr"
83};
84
85typedef 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
92typedef struct winreg_keylist {
93	list_t kl_list;
94	int kl_count;
95} winreg_keylist_t;
96
97static winreg_keylist_t winreg_keylist;
98static mutex_t winreg_mutex;
99
100static void winreg_add_predefined(const char *);
101static ndr_hdid_t *winreg_alloc_id(ndr_xa_t *, const char *);
102static void winreg_dealloc_id(ndr_xa_t *, ndr_hdid_t *);
103static boolean_t winreg_key_has_subkey(const char *);
104static char *winreg_enum_subkey(ndr_xa_t *, const char *, uint32_t);
105static char *winreg_lookup_value(const char *);
106static uint32_t winreg_sd_format(smb_sd_t *);
107uint32_t srvsvc_sd_set_relative(smb_sd_t *, uint8_t *);
108
109static int winreg_s_OpenHKCR(void *, ndr_xa_t *);
110static int winreg_s_OpenHKCU(void *, ndr_xa_t *);
111static int winreg_s_OpenHKLM(void *, ndr_xa_t *);
112static int winreg_s_OpenHKPD(void *, ndr_xa_t *);
113static int winreg_s_OpenHKU(void *, ndr_xa_t *);
114static int winreg_s_OpenHKCC(void *, ndr_xa_t *);
115static int winreg_s_OpenHKDD(void *, ndr_xa_t *);
116static int winreg_s_OpenHKPT(void *, ndr_xa_t *);
117static int winreg_s_OpenHKPN(void *, ndr_xa_t *);
118static int winreg_s_OpenHK(void *, ndr_xa_t *, const char *);
119static int winreg_s_Close(void *, ndr_xa_t *);
120static int winreg_s_CreateKey(void *, ndr_xa_t *);
121static int winreg_s_DeleteKey(void *, ndr_xa_t *);
122static int winreg_s_DeleteValue(void *, ndr_xa_t *);
123static int winreg_s_EnumKey(void *, ndr_xa_t *);
124static int winreg_s_EnumValue(void *, ndr_xa_t *);
125static int winreg_s_FlushKey(void *, ndr_xa_t *);
126static int winreg_s_GetKeySec(void *, ndr_xa_t *);
127static int winreg_s_NotifyChange(void *, ndr_xa_t *);
128static int winreg_s_OpenKey(void *, ndr_xa_t *);
129static int winreg_s_QueryKey(void *, ndr_xa_t *);
130static int winreg_s_QueryValue(void *, ndr_xa_t *);
131static int winreg_s_SetKeySec(void *, ndr_xa_t *);
132static int winreg_s_CreateValue(void *, ndr_xa_t *);
133static int winreg_s_Shutdown(void *, ndr_xa_t *);
134static int winreg_s_AbortShutdown(void *, ndr_xa_t *);
135static int winreg_s_GetVersion(void *, ndr_xa_t *);
136
137static 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
167static 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
182static char winreg_sysname[SYS_NMLN];
183static 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 */
192void
193winreg_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
239static void
240winreg_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
254static int
255winreg_s_OpenHKCR(void *arg, ndr_xa_t *mxa)
256{
257	return (winreg_s_OpenHK(arg, mxa, "HKCR"));
258}
259
260static int
261winreg_s_OpenHKCU(void *arg, ndr_xa_t *mxa)
262{
263	return (winreg_s_OpenHK(arg, mxa, "HKCU"));
264}
265
266static int
267winreg_s_OpenHKLM(void *arg, ndr_xa_t *mxa)
268{
269	return (winreg_s_OpenHK(arg, mxa, "HKLM"));
270}
271
272static int
273winreg_s_OpenHKPD(void *arg, ndr_xa_t *mxa)
274{
275	return (winreg_s_OpenHK(arg, mxa, "HKPD"));
276}
277
278static int
279winreg_s_OpenHKU(void *arg, ndr_xa_t *mxa)
280{
281	return (winreg_s_OpenHK(arg, mxa, "HKU"));
282}
283
284static int
285winreg_s_OpenHKCC(void *arg, ndr_xa_t *mxa)
286{
287	return (winreg_s_OpenHK(arg, mxa, "HKCC"));
288}
289
290static int
291winreg_s_OpenHKDD(void *arg, ndr_xa_t *mxa)
292{
293	return (winreg_s_OpenHK(arg, mxa, "HKDD"));
294}
295
296static int
297winreg_s_OpenHKPT(void *arg, ndr_xa_t *mxa)
298{
299	return (winreg_s_OpenHK(arg, mxa, "HKPT"));
300}
301
302static int
303winreg_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 */
313static int
314winreg_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 */
341static int
342winreg_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
356static ndr_hdid_t *
357winreg_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
377static void
378winreg_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 */
393static int
394winreg_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
448new_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
474no_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 */
484static int
485winreg_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 */
536static boolean_t
537winreg_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 */
565static char *
566winreg_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*/
619static int
620winreg_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 */
631static int
632winreg_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 */
680static int
681winreg_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 */
702static int
703winreg_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 */
719static int
720winreg_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
754winreg_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
762static uint32_t
763winreg_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 */
787static int
788winreg_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 */
810static int
811winreg_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*/
865static int
866winreg_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 */
898static int
899winreg_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 */
965static char *
966winreg_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*/
993static int
994winreg_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*/
1006static int
1007winreg_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*/
1021static int
1022winreg_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 */
1035static int
1036winreg_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*/
1056static int
1057winreg_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