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) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2019 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 /*
  28  * This module provides the high level interface to the LSA RPC functions.
  29  */
  30 
  31 #include <strings.h>
  32 #include <unistd.h>
  33 
  34 #include <smbsrv/libsmb.h>
  35 #include <smbsrv/libmlsvc.h>
  36 #include <smbsrv/smbinfo.h>
  37 #include <smbsrv/smb_token.h>
  38 
  39 #include <lsalib.h>
  40 
  41 static uint32_t lsa_lookup_name_int(char *, uint16_t, smb_account_t *,
  42     boolean_t);
  43 static uint32_t lsa_lookup_sid_int(smb_sid_t *, smb_account_t *, boolean_t);
  44 
  45 static uint32_t lsa_lookup_name_builtin(char *, char *, smb_account_t *);
  46 static uint32_t lsa_lookup_name_domain(char *, smb_account_t *);
  47 
  48 static uint32_t lsa_lookup_sid_builtin(smb_sid_t *, smb_account_t *);
  49 static uint32_t lsa_lookup_sid_domain(smb_sid_t *, smb_account_t *);
  50 
  51 static uint32_t lsa_list_accounts(mlsvc_handle_t *);
  52 static uint32_t lsa_map_status(uint32_t);
  53 
  54 /*
  55  * Lookup the given account and returns the account information
  56  * in the passed smb_account_t structure.
  57  *
  58  * The lookup is performed in the following order:
  59  *    well known accounts
  60  *    local accounts
  61  *    domain accounts
  62  *
  63  * If it's established the given account is well know or local
  64  * but the lookup fails for some reason, the next step(s) won't be
  65  * performed.
  66  *
  67  * If the name is a domain account, it may refer to a user, group or
  68  * alias. If it is a local account, its type should be specified
  69  * in the sid_type parameter. In case the account type is unknown
  70  * sid_type should be set to SidTypeUnknown.
  71  *
  72  * account argument could be either [domain\]name or [domain/]name.
  73  *
  74  * Return status:
  75  *
  76  *   NT_STATUS_SUCCESS          Account is successfully translated
  77  *   NT_STATUS_NONE_MAPPED      Couldn't translate the account
  78  */
  79 uint32_t
  80 lsa_lookup_name(char *account, uint16_t type, smb_account_t *info)
  81 {
  82         return (lsa_lookup_name_int(account, type, info, B_TRUE));
  83 }
  84 
  85 /* Variant that avoids the call out to AD. */
  86 uint32_t
  87 lsa_lookup_lname(char *account, uint16_t type, smb_account_t *info)
  88 {
  89         return (lsa_lookup_name_int(account, type, info, B_FALSE));
  90 }
  91 
  92 uint32_t
  93 lsa_lookup_name_int(char *account, uint16_t type, smb_account_t *info,
  94     boolean_t try_ad)
  95 {
  96         char nambuf[SMB_USERNAME_MAXLEN];
  97         char dombuf[SMB_PI_MAX_DOMAIN];
  98         char *name, *domain;
  99         uint32_t status;
 100         char *slash;
 101 
 102         if (account == NULL)
 103                 return (NT_STATUS_NONE_MAPPED);
 104 
 105         (void) strsubst(account, '/', '\\');
 106         (void) strcanon(account, "\\");
 107         /* \john -> john */
 108         account += strspn(account, "\\");
 109 
 110         if ((slash = strchr(account, '\\')) != NULL) {
 111                 *slash = '\0';
 112                 (void) strlcpy(dombuf, account, sizeof (dombuf));
 113                 (void) strlcpy(nambuf, slash + 1, sizeof (nambuf));
 114                 *slash = '\\';
 115                 name = nambuf;
 116                 domain = dombuf;
 117         } else {
 118                 name = account;
 119                 domain = NULL;
 120         }
 121 
 122         status = lsa_lookup_name_builtin(domain, name, info);
 123         if (status == NT_STATUS_NOT_FOUND) {
 124                 status = smb_sam_lookup_name(domain, name, type, info);
 125                 if (status == NT_STATUS_SUCCESS)
 126                         return (status);
 127 
 128                 if (try_ad && ((domain == NULL) ||
 129                     (status == NT_STATUS_NOT_FOUND))) {
 130                         status = lsa_lookup_name_domain(account, info);
 131                 }
 132         }
 133 
 134         return ((status == NT_STATUS_SUCCESS) ? status : NT_STATUS_NONE_MAPPED);
 135 }
 136 
 137 uint32_t
 138 lsa_lookup_sid(smb_sid_t *sid, smb_account_t *info)
 139 {
 140         return (lsa_lookup_sid_int(sid, info, B_TRUE));
 141 }
 142 
 143 /* Variant that avoids the call out to AD. */
 144 uint32_t
 145 lsa_lookup_lsid(smb_sid_t *sid, smb_account_t *info)
 146 {
 147         return (lsa_lookup_sid_int(sid, info, B_FALSE));
 148 }
 149 
 150 static uint32_t
 151 lsa_lookup_sid_int(smb_sid_t *sid, smb_account_t *info, boolean_t try_ad)
 152 {
 153         uint32_t status;
 154 
 155         if (!smb_sid_isvalid(sid))
 156                 return (NT_STATUS_INVALID_SID);
 157 
 158         status = lsa_lookup_sid_builtin(sid, info);
 159         if (status == NT_STATUS_NOT_FOUND) {
 160                 status = smb_sam_lookup_sid(sid, info);
 161                 if (try_ad && status == NT_STATUS_NOT_FOUND) {
 162                         status = lsa_lookup_sid_domain(sid, info);
 163                 }
 164         }
 165 
 166         return ((status == NT_STATUS_SUCCESS) ? status : NT_STATUS_NONE_MAPPED);
 167 }
 168 
 169 /*
 170  * Obtains the primary domain SID and name from the specified server
 171  * (domain controller).
 172  *
 173  * The requested information will be returned via 'info' argument.
 174  *
 175  * Returns NT status codes. (Raw, not LSA-ized)
 176  */
 177 DWORD
 178 lsa_query_primary_domain_info(char *server, char *domain,
 179     smb_domain_t *info)
 180 {
 181         mlsvc_handle_t domain_handle;
 182         char user[SMB_USERNAME_MAXLEN];
 183         DWORD status;
 184 
 185         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 186 
 187         status = lsar_open(server, domain, user, &domain_handle);
 188         if (status != 0)
 189                 return (status);
 190 
 191         status = lsar_query_info_policy(&domain_handle,
 192             MSLSA_POLICY_PRIMARY_DOMAIN_INFO, info);
 193 
 194         (void) lsar_close(&domain_handle);
 195         return (status);
 196 }
 197 
 198 /*
 199  * Obtains the account domain SID and name from the current server
 200  * (domain controller).
 201  *
 202  * The requested information will be returned via 'info' argument.
 203  *
 204  * Returns NT status codes. (Raw, not LSA-ized)
 205  */
 206 DWORD
 207 lsa_query_account_domain_info(char *server, char *domain,
 208     smb_domain_t *info)
 209 {
 210         mlsvc_handle_t domain_handle;
 211         char user[SMB_USERNAME_MAXLEN];
 212         DWORD status;
 213 
 214         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 215 
 216         status = lsar_open(server, domain, user, &domain_handle);
 217         if (status != 0)
 218                 return (status);
 219 
 220         status = lsar_query_info_policy(&domain_handle,
 221             MSLSA_POLICY_ACCOUNT_DOMAIN_INFO, info);
 222 
 223         (void) lsar_close(&domain_handle);
 224         return (status);
 225 }
 226 
 227 /*
 228  * lsa_query_dns_domain_info
 229  *
 230  * Obtains the DNS domain info from the specified server
 231  * (domain controller).
 232  *
 233  * The requested information will be returned via 'info' argument.
 234  *
 235  * Returns NT status codes. (Raw, not LSA-ized)
 236  */
 237 DWORD
 238 lsa_query_dns_domain_info(char *server, char *domain, smb_domain_t *info)
 239 {
 240         mlsvc_handle_t domain_handle;
 241         char user[SMB_USERNAME_MAXLEN];
 242         DWORD status;
 243 
 244         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 245 
 246         status = lsar_open(server, domain, user, &domain_handle);
 247         if (status != 0)
 248                 return (status);
 249 
 250         status = lsar_query_info_policy(&domain_handle,
 251             MSLSA_POLICY_DNS_DOMAIN_INFO, info);
 252 
 253         (void) lsar_close(&domain_handle);
 254         return (status);
 255 }
 256 
 257 /*
 258  * Enumerate the trusted domains of  primary domain.
 259  * This is the basic enumaration call which only returns the
 260  * NetBIOS name of the domain and its SID.
 261  *
 262  * The requested information will be returned via 'info' argument.
 263  *
 264  * Returns NT status codes.  (Raw, not LSA-ized)
 265  */
 266 DWORD
 267 lsa_enum_trusted_domains(char *server, char *domain,
 268     smb_trusted_domains_t *info)
 269 {
 270         mlsvc_handle_t domain_handle;
 271         char user[SMB_USERNAME_MAXLEN];
 272         DWORD enum_context;
 273         DWORD status;
 274 
 275         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 276 
 277         status = lsar_open(server, domain, user, &domain_handle);
 278         if (status != 0)
 279                 return (status);
 280 
 281         enum_context = 0;
 282 
 283         status = lsar_enum_trusted_domains(&domain_handle, &enum_context, info);
 284         if (status == NT_STATUS_NO_MORE_ENTRIES) {
 285                 /*
 286                  * STATUS_NO_MORE_ENTRIES indicates that we
 287                  * have all of the available information.
 288                  */
 289                 status = NT_STATUS_SUCCESS;
 290         }
 291 
 292         (void) lsar_close(&domain_handle);
 293         return (status);
 294 }
 295 
 296 /*
 297  * Enumerate the trusted domains of the primary domain.
 298  * This is the extended enumaration call which besides
 299  * NetBIOS name of the domain and its SID, it will return
 300  * the FQDN plus some trust information which is not used.
 301  *
 302  * The requested information will be returned via 'info' argument.
 303  *
 304  * Returns NT status codes. (Raw, not LSA-ized)
 305  */
 306 DWORD
 307 lsa_enum_trusted_domains_ex(char *server, char *domain,
 308     smb_trusted_domains_t *info)
 309 {
 310         mlsvc_handle_t domain_handle;
 311         char user[SMB_USERNAME_MAXLEN];
 312         DWORD enum_context;
 313         DWORD status;
 314 
 315         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 316 
 317         status = lsar_open(server, domain, user, &domain_handle);
 318         if (status != 0)
 319                 return (status);
 320 
 321         enum_context = 0;
 322 
 323         status = lsar_enum_trusted_domains_ex(&domain_handle, &enum_context,
 324             info);
 325         if (status == NT_STATUS_NO_MORE_ENTRIES) {
 326                 /*
 327                  * STATUS_NO_MORE_ENTRIES indicates that we
 328                  * have all of the available information.
 329                  */
 330                 status = NT_STATUS_SUCCESS;
 331         }
 332 
 333         (void) lsar_close(&domain_handle);
 334         return (status);
 335 }
 336 
 337 /*
 338  * Lookup well known accounts table
 339  *
 340  * Return status:
 341  *
 342  *   NT_STATUS_SUCCESS          Account is translated successfully
 343  *   NT_STATUS_NOT_FOUND        This is not a well known account
 344  *   NT_STATUS_NONE_MAPPED      Account is found but domains don't match
 345  *   NT_STATUS_NO_MEMORY        Memory shortage
 346  *   NT_STATUS_INTERNAL_ERROR   Internal error/unexpected failure
 347  */
 348 static uint32_t
 349 lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info)
 350 {
 351         smb_wka_t *wka;
 352         char *wkadom;
 353 
 354         bzero(info, sizeof (smb_account_t));
 355 
 356         if ((wka = smb_wka_lookup_name(name)) == NULL)
 357                 return (NT_STATUS_NOT_FOUND);
 358 
 359         if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
 360                 return (NT_STATUS_INTERNAL_ERROR);
 361 
 362         if ((domain != NULL) && (smb_strcasecmp(domain, wkadom, 0) != 0))
 363                 return (NT_STATUS_NONE_MAPPED);
 364 
 365         info->a_name = strdup(name);
 366         info->a_sid = smb_sid_dup(wka->wka_binsid);
 367         info->a_domain = strdup(wkadom);
 368         info->a_domsid = smb_sid_split(wka->wka_binsid, &info->a_rid);
 369         info->a_type = wka->wka_type;
 370 
 371         if (!smb_account_validate(info)) {
 372                 smb_account_free(info);
 373                 return (NT_STATUS_NO_MEMORY);
 374         }
 375 
 376         return (NT_STATUS_SUCCESS);
 377 }
 378 
 379 /*
 380  * Lookup a domain account by its name.
 381  *
 382  * The information is returned in the user_info structure.
 383  * The caller is responsible for allocating and releasing
 384  * this structure.
 385  *
 386  * Returns NT status codes. (LSA-ized)
 387  */
 388 static uint32_t
 389 lsa_lookup_name_domain(char *account_name, smb_account_t *info)
 390 {
 391         mlsvc_handle_t domain_handle;
 392         smb_domainex_t dinfo;
 393         char user[SMB_USERNAME_MAXLEN];
 394         uint32_t status;
 395 
 396         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 397 
 398         if (!smb_domain_getinfo(&dinfo))
 399                 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
 400 
 401         status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
 402             user, &domain_handle);
 403         if (status != 0)
 404                 return (lsa_map_status(status));
 405 
 406         status = lsar_lookup_names(&domain_handle, account_name, info);
 407 
 408         (void) lsar_close(&domain_handle);
 409         return (status);
 410 }
 411 
 412 /*
 413  * lsa_lookup_privs
 414  *
 415  * Request the privileges associated with the specified account. In
 416  * order to get the privileges, we first have to lookup the name on
 417  * the specified domain controller and obtain the appropriate SID.
 418  * The SID can then be used to open the account and obtain the
 419  * account privileges. The results from both the name lookup and the
 420  * privileges are returned in the user_info structure. The caller is
 421  * responsible for allocating and releasing this structure.
 422  *
 423  * Returns NT status codes. (LSA-ized)
 424  */
 425 /*ARGSUSED*/
 426 DWORD
 427 lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo)
 428 {
 429         mlsvc_handle_t domain_handle;
 430         smb_domainex_t dinfo;
 431         char user[SMB_USERNAME_MAXLEN];
 432         DWORD status;
 433 
 434         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 435 
 436         if (!smb_domain_getinfo(&dinfo))
 437                 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
 438 
 439         status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
 440             user, &domain_handle);
 441         if (status != 0)
 442                 return (lsa_map_status(status));
 443 
 444         status = lsa_list_accounts(&domain_handle);
 445         (void) lsar_close(&domain_handle);
 446         return (status);
 447 }
 448 
 449 /*
 450  * lsa_list_privs
 451  *
 452  * List the privileges supported by the specified server.
 453  * This function is only intended for diagnostics.
 454  *
 455  * Returns NT status codes. (LSA-ized)
 456  */
 457 DWORD
 458 lsa_list_privs(char *server, char *domain)
 459 {
 460         static char name[128];
 461         static struct ms_luid luid;
 462         mlsvc_handle_t domain_handle;
 463         char user[SMB_USERNAME_MAXLEN];
 464         DWORD status;
 465         int rc;
 466         int i;
 467 
 468         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 469 
 470         status = lsar_open(server, domain, user, &domain_handle);
 471         if (status != 0)
 472                 return (lsa_map_status(status));
 473 
 474         for (i = 0; i < 30; ++i) {
 475                 luid.low_part = i;
 476                 rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128);
 477                 if (rc != 0)
 478                         continue;
 479 
 480                 (void) lsar_lookup_priv_value(&domain_handle, name, &luid);
 481                 (void) lsar_lookup_priv_display_name(&domain_handle, name,
 482                     name, 128);
 483         }
 484 
 485         (void) lsar_close(&domain_handle);
 486         return (NT_STATUS_SUCCESS);
 487 }
 488 
 489 /*
 490  * lsa_list_accounts
 491  *
 492  * This function can be used to list the accounts in the specified
 493  * domain. For now the SIDs are just listed in the system log.
 494  *
 495  * Returns NT status
 496  */
 497 static DWORD
 498 lsa_list_accounts(mlsvc_handle_t *domain_handle)
 499 {
 500         mlsvc_handle_t account_handle;
 501         struct mslsa_EnumAccountBuf accounts;
 502         struct mslsa_sid *sid;
 503         smb_account_t ainfo;
 504         DWORD enum_context = 0;
 505         DWORD status;
 506         int i;
 507 
 508         bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf));
 509 
 510         do {
 511                 status = lsar_enum_accounts(domain_handle, &enum_context,
 512                     &accounts);
 513                 if (status != 0)
 514                         return (status);
 515 
 516                 for (i = 0; i < accounts.entries_read; ++i) {
 517                         sid = accounts.info[i].sid;
 518 
 519                         if (lsar_open_account(domain_handle, sid,
 520                             &account_handle) == 0) {
 521                                 (void) lsar_enum_privs_account(&account_handle,
 522                                     &ainfo);
 523                                 (void) lsar_close(&account_handle);
 524                         }
 525 
 526                         free(accounts.info[i].sid);
 527                 }
 528 
 529                 if (accounts.info)
 530                         free(accounts.info);
 531         } while (status == 0 && accounts.entries_read != 0);
 532 
 533         return (0);
 534 }
 535 
 536 /*
 537  * Lookup well known accounts table for the given SID
 538  *
 539  * Return status:
 540  *
 541  *   NT_STATUS_SUCCESS          Account is translated successfully
 542  *   NT_STATUS_NOT_FOUND        This is not a well known account
 543  *   NT_STATUS_NO_MEMORY        Memory shortage
 544  *   NT_STATUS_INTERNAL_ERROR   Internal error/unexpected failure
 545  */
 546 static uint32_t
 547 lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo)
 548 {
 549         smb_wka_t *wka;
 550         char *wkadom;
 551 
 552         bzero(ainfo, sizeof (smb_account_t));
 553 
 554         if ((wka = smb_wka_lookup_sid(sid)) == NULL)
 555                 return (NT_STATUS_NOT_FOUND);
 556 
 557         if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
 558                 return (NT_STATUS_INTERNAL_ERROR);
 559 
 560         ainfo->a_name = strdup(wka->wka_name);
 561         ainfo->a_sid = smb_sid_dup(wka->wka_binsid);
 562         ainfo->a_domain = strdup(wkadom);
 563         ainfo->a_domsid = smb_sid_split(ainfo->a_sid, &ainfo->a_rid);
 564         ainfo->a_type = wka->wka_type;
 565 
 566         if (!smb_account_validate(ainfo)) {
 567                 smb_account_free(ainfo);
 568                 return (NT_STATUS_NO_MEMORY);
 569         }
 570 
 571         return (NT_STATUS_SUCCESS);
 572 }
 573 
 574 /*
 575  * Lookup a domain account by its SID.
 576  *
 577  * The information is returned in the user_info structure.
 578  * The caller is responsible for allocating and releasing
 579  * this structure.
 580  *
 581  * Returns NT status codes. (LSA-ized)
 582  */
 583 static uint32_t
 584 lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo)
 585 {
 586         mlsvc_handle_t domain_handle;
 587         smb_domainex_t dinfo;
 588         char user[SMB_USERNAME_MAXLEN];
 589         uint32_t status;
 590 
 591         smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
 592 
 593         if (!smb_domain_getinfo(&dinfo))
 594                 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
 595 
 596         status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
 597             user, &domain_handle);
 598         if (status != 0)
 599                 return (lsa_map_status(status));
 600 
 601         status = lsar_lookup_sids(&domain_handle, sid, ainfo);
 602 
 603         (void) lsar_close(&domain_handle);
 604         return (status);
 605 }
 606 
 607 /*
 608  * Most functions that call the local security authority expect
 609  * only a limited set of status returns.  This function maps the
 610  * status we get from talking to our domain controller into one
 611  * that LSA functions can return.  Most common errors become:
 612  * NT_STATUS_CANT_ACCESS_DOMAIN_INFO (when no DC etc.)
 613  */
 614 static uint32_t
 615 lsa_map_status(uint32_t status)
 616 {
 617         switch (status) {
 618         case NT_STATUS_SUCCESS:
 619                 break;
 620         case NT_STATUS_INVALID_PARAMETER:       /* rpc bind */
 621                 break;
 622         case NT_STATUS_NO_MEMORY:
 623                 break;
 624         case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
 625         case NT_STATUS_BAD_NETWORK_PATH:        /* get server addr */
 626         case NT_STATUS_NETWORK_ACCESS_DENIED:   /* authentication */
 627         case NT_STATUS_BAD_NETWORK_NAME:        /* tree connect */
 628         case NT_STATUS_ACCESS_DENIED:           /* open pipe */
 629                 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
 630                 break;
 631         default:
 632                 status = NT_STATUS_UNSUCCESSFUL;
 633                 break;
 634         }
 635         return (status);
 636 }