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
 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
 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
 256 winreg_s_OpenHKCR(void *arg, ndr_xa_t *mxa)
 257 {
 258         return (winreg_s_OpenHK(arg, mxa, "HKCR"));
 259 }
 260 
 261 static int
 262 winreg_s_OpenHKCU(void *arg, ndr_xa_t *mxa)
 263 {
 264         return (winreg_s_OpenHK(arg, mxa, "HKCU"));
 265 }
 266 
 267 static int
 268 winreg_s_OpenHKLM(void *arg, ndr_xa_t *mxa)
 269 {
 270         return (winreg_s_OpenHK(arg, mxa, "HKLM"));
 271 }
 272 
 273 static int
 274 winreg_s_OpenHKPD(void *arg, ndr_xa_t *mxa)
 275 {
 276         return (winreg_s_OpenHK(arg, mxa, "HKPD"));
 277 }
 278 
 279 static int
 280 winreg_s_OpenHKU(void *arg, ndr_xa_t *mxa)
 281 {
 282         return (winreg_s_OpenHK(arg, mxa, "HKU"));
 283 }
 284 
 285 static int
 286 winreg_s_OpenHKCC(void *arg, ndr_xa_t *mxa)
 287 {
 288         return (winreg_s_OpenHK(arg, mxa, "HKCC"));
 289 }
 290 
 291 static int
 292 winreg_s_OpenHKDD(void *arg, ndr_xa_t *mxa)
 293 {
 294         return (winreg_s_OpenHK(arg, mxa, "HKDD"));
 295 }
 296 
 297 static int
 298 winreg_s_OpenHKPT(void *arg, ndr_xa_t *mxa)
 299 {
 300         return (winreg_s_OpenHK(arg, mxa, "HKPT"));
 301 }
 302 
 303 static int
 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
 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
 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 *
 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
 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
 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
 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
 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 *
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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
 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 *
 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
 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
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
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
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
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 }