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