1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
  26  * Copyright 2018 RackTop Systems.
  27  */
  28 
  29 #include <strings.h>
  30 #include <smbsrv/libsmb.h>
  31 
  32 extern int smb_pwd_num(void);
  33 extern int smb_lgrp_numbydomain(smb_domain_type_t, int *);
  34 
  35 static uint32_t smb_sam_lookup_user(char *, smb_sid_t **);
  36 static uint32_t smb_sam_lookup_group(char *, smb_sid_t **);
  37 
  38 /*
  39  * Local well-known accounts data structure table and prototypes
  40  */
  41 typedef struct smb_lwka {
  42         uint32_t        lwka_rid;
  43         char            *lwka_name;
  44         uint16_t        lwka_type;
  45 } smb_lwka_t;
  46 
  47 static smb_lwka_t lwka_tbl[] = {
  48         { 500, "Administrator", SidTypeUser },
  49         { 501, "Guest", SidTypeUser },
  50         { 502, "KRBTGT", SidTypeUser },
  51         { 512, "Domain Admins", SidTypeGroup },
  52         { 513, "Domain Users", SidTypeGroup },
  53         { 514, "Domain Guests", SidTypeGroup },
  54         { 516, "Domain Controllers", SidTypeGroup },
  55         { 517, "Cert Publishers", SidTypeGroup },
  56         { 518, "Schema Admins", SidTypeGroup },
  57         { 519, "Enterprise Admins", SidTypeGroup },
  58         { 520, "Global Policy Creator Owners", SidTypeGroup },
  59         { 533, "RAS and IAS Servers", SidTypeGroup }
  60 };
  61 
  62 #define SMB_LWKA_NUM    (sizeof (lwka_tbl)/sizeof (lwka_tbl[0]))
  63 
  64 static smb_lwka_t *smb_lwka_lookup_name(char *);
  65 static smb_lwka_t *smb_lwka_lookup_sid(smb_sid_t *);
  66 
  67 /*
  68  * Looks up the given name in local account databases:
  69  *
  70  * SMB Local users are looked up in /var/smb/smbpasswd
  71  * SMB Local groups are looked up in /var/smb/smbgroup.db
  72  *
  73  * If the account is found, its information is populated
  74  * in the passed smb_account_t structure. Caller must free
  75  * allocated memories by calling smb_account_free() upon
  76  * successful return.
  77  *
  78  * The type of account is specified by 'type', which can be user,
  79  * alias (local group) or unknown. If the caller doesn't know
  80  * whether the name is a user or group name then SidTypeUnknown
  81  * should be passed.
  82  *
  83  * If a local user and group have the same name, the user will
  84  * always be picked. Note that this situation cannot happen on
  85  * Windows systems.
  86  *
  87  * If a SMB local user/group is found but it turns out that
  88  * it'll be mapped to a domain user/group the lookup is considered
  89  * failed and NT_STATUS_NONE_MAPPED is returned.
  90  *
  91  * Return status:
  92  *
  93  *   NT_STATUS_NOT_FOUND        This is not a local account
  94  *   NT_STATUS_NONE_MAPPED      It's a local account but cannot be
  95  *                              translated.
  96  *   other error status codes.
  97  */
  98 uint32_t
  99 smb_sam_lookup_name(char *domain, char *name, uint16_t type,
 100     smb_account_t *account)
 101 {
 102         smb_domain_t di;
 103         smb_sid_t *sid;
 104         uint32_t status;
 105         smb_lwka_t *lwka;
 106 
 107         bzero(account, sizeof (smb_account_t));
 108 
 109         if (domain != NULL) {
 110                 if (!smb_domain_lookup_name(domain, &di) ||
 111                     (di.di_type != SMB_DOMAIN_LOCAL))
 112                         return (NT_STATUS_NOT_FOUND);
 113 
 114                 /* Only Netbios hostname is accepted */
 115                 if (smb_strcasecmp(domain, di.di_nbname, 0) != 0)
 116                         return (NT_STATUS_NONE_MAPPED);
 117         } else {
 118                 if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
 119                         return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
 120         }
 121 
 122         if (smb_strcasecmp(name, di.di_nbname, 0) == 0) {
 123                 /* This is the local domain name */
 124                 account->a_type = SidTypeDomain;
 125                 account->a_name = strdup("");
 126                 account->a_domain = strdup(di.di_nbname);
 127                 account->a_sid = smb_sid_dup(di.di_binsid);
 128                 account->a_domsid = smb_sid_dup(di.di_binsid);
 129                 account->a_rid = (uint32_t)-1;
 130 
 131                 if (!smb_account_validate(account)) {
 132                         smb_account_free(account);
 133                         return (NT_STATUS_NO_MEMORY);
 134                 }
 135 
 136                 return (NT_STATUS_SUCCESS);
 137         }
 138 
 139         if ((lwka = smb_lwka_lookup_name(name)) != NULL) {
 140                 sid = smb_sid_splice(di.di_binsid, lwka->lwka_rid);
 141                 type = lwka->lwka_type;
 142         } else {
 143                 switch (type) {
 144                 case SidTypeUser:
 145                         status = smb_sam_lookup_user(name, &sid);
 146                         if (status != NT_STATUS_SUCCESS)
 147                                 return (status);
 148                         break;
 149 
 150                 case SidTypeAlias:
 151                         status = smb_sam_lookup_group(name, &sid);
 152                         if (status != NT_STATUS_SUCCESS)
 153                                 return (status);
 154                         break;
 155 
 156                 case SidTypeUnknown:
 157                         type = SidTypeUser;
 158                         status = smb_sam_lookup_user(name, &sid);
 159                         if (status == NT_STATUS_SUCCESS)
 160                                 break;
 161 
 162                         if (status == NT_STATUS_NONE_MAPPED)
 163                                 return (status);
 164 
 165                         type = SidTypeAlias;
 166                         status = smb_sam_lookup_group(name, &sid);
 167                         if (status != NT_STATUS_SUCCESS)
 168                                 return (status);
 169                         break;
 170 
 171                 default:
 172                         return (NT_STATUS_INVALID_PARAMETER);
 173                 }
 174         }
 175 
 176         account->a_name = strdup(name);
 177         account->a_sid = sid;
 178         account->a_domain = strdup(di.di_nbname);
 179         account->a_domsid = smb_sid_split(sid, &account->a_rid);
 180         account->a_type = type;
 181 
 182         if (!smb_account_validate(account)) {
 183                 smb_account_free(account);
 184                 return (NT_STATUS_NO_MEMORY);
 185         }
 186 
 187         return (NT_STATUS_SUCCESS);
 188 }
 189 
 190 /*
 191  * Looks up the given SID in local account databases:
 192  *
 193  * SMB Local users are looked up in /var/smb/smbpasswd
 194  * SMB Local groups are looked up in /var/smb/smbgroup.db
 195  *
 196  * If the account is found, its information is populated
 197  * in the passed smb_account_t structure. Caller must free
 198  * allocated memories by calling smb_account_free() upon
 199  * successful return.
 200  *
 201  * Return status:
 202  *
 203  *   NT_STATUS_NOT_FOUND        This is not a local account
 204  *   NT_STATUS_NONE_MAPPED      It's a local account but cannot be
 205  *                              translated.
 206  *   other error status codes.
 207  */
 208 uint32_t
 209 smb_sam_lookup_sid(smb_sid_t *sid, smb_account_t *account)
 210 {
 211         char hostname[MAXHOSTNAMELEN];
 212         smb_passwd_t smbpw;
 213         smb_group_t grp;
 214         smb_lwka_t *lwka;
 215         smb_domain_t di;
 216         uint32_t rid;
 217         uid_t id;
 218         int id_type;
 219         int rc;
 220 
 221         bzero(account, sizeof (smb_account_t));
 222 
 223         if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
 224                 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
 225 
 226         if (smb_sid_cmp(sid, di.di_binsid)) {
 227                 /* This is the local domain SID */
 228                 account->a_type = SidTypeDomain;
 229                 account->a_name = strdup("");
 230                 account->a_domain = strdup(di.di_nbname);
 231                 account->a_sid = smb_sid_dup(sid);
 232                 account->a_domsid = smb_sid_dup(sid);
 233                 account->a_rid = (uint32_t)-1;
 234 
 235                 if (!smb_account_validate(account)) {
 236                         smb_account_free(account);
 237                         return (NT_STATUS_NO_MEMORY);
 238                 }
 239 
 240                 return (NT_STATUS_SUCCESS);
 241         }
 242 
 243         if (!smb_sid_indomain(di.di_binsid, sid)) {
 244                 /* This is not a local SID */
 245                 return (NT_STATUS_NOT_FOUND);
 246         }
 247 
 248         if ((lwka = smb_lwka_lookup_sid(sid)) != NULL) {
 249                 account->a_type = lwka->lwka_type;
 250                 account->a_name = strdup(lwka->lwka_name);
 251         } else {
 252                 id_type = SMB_IDMAP_UNKNOWN;
 253                 if (smb_idmap_getid(sid, &id, &id_type) != IDMAP_SUCCESS)
 254                         return (NT_STATUS_NONE_MAPPED);
 255 
 256                 switch (id_type) {
 257                 case SMB_IDMAP_USER:
 258                         account->a_type = SidTypeUser;
 259                         if (smb_pwd_getpwuid(id, &smbpw) == NULL)
 260                                 return (NT_STATUS_NO_SUCH_USER);
 261 
 262                         account->a_name = strdup(smbpw.pw_name);
 263                         account->a_flags = smbpw.pw_flags;
 264                         break;
 265 
 266                 case SMB_IDMAP_GROUP:
 267                         account->a_type = SidTypeAlias;
 268                         (void) smb_sid_getrid(sid, &rid);
 269                         rc = smb_lgrp_getbyrid(rid, SMB_DOMAIN_LOCAL, &grp);
 270                         if (rc != SMB_LGRP_SUCCESS)
 271                                 return (NT_STATUS_NO_SUCH_ALIAS);
 272 
 273                         account->a_name = strdup(grp.sg_name);
 274                         smb_lgrp_free(&grp);
 275                         break;
 276 
 277                 default:
 278                         return (NT_STATUS_NONE_MAPPED);
 279                 }
 280         }
 281 
 282         if (smb_getnetbiosname(hostname, MAXHOSTNAMELEN) == 0)
 283                 account->a_domain = strdup(hostname);
 284         account->a_sid = smb_sid_dup(sid);
 285         account->a_domsid = smb_sid_split(sid, &account->a_rid);
 286 
 287         if (!smb_account_validate(account)) {
 288                 smb_account_free(account);
 289                 return (NT_STATUS_NO_MEMORY);
 290         }
 291 
 292         return (NT_STATUS_SUCCESS);
 293 }
 294 
 295 /*
 296  * Returns number of SMB users, i.e. users who have entry
 297  * in /var/smb/smbpasswd
 298  */
 299 int
 300 smb_sam_usr_cnt(void)
 301 {
 302         return (smb_pwd_num());
 303 }
 304 
 305 /*
 306  * Updates a list of groups in which the given user is a member
 307  * by adding any local (SAM) groups.
 308  *
 309  * We are a member of local groups where the local group
 310  * contains either the user's primary SID, or any of their
 311  * other SIDs such as from domain groups, SID history, etc.
 312  * We can have indirect membership via domain groups.
 313  */
 314 uint32_t
 315 smb_sam_usr_groups(smb_sid_t *user_sid, smb_ids_t *gids)
 316 {
 317         smb_ids_t new_gids;
 318         smb_id_t *ids, *new_ids;
 319         smb_giter_t gi;
 320         smb_group_t lgrp;
 321         int i, gcnt, total_cnt;
 322         uint32_t ret;
 323         boolean_t member;
 324 
 325         /*
 326          * First pass: count groups to be added (gcnt)
 327          */
 328         gcnt = 0;
 329         if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
 330                 return (NT_STATUS_INTERNAL_ERROR);
 331 
 332         while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
 333                 member = B_FALSE;
 334                 if (smb_lgrp_is_member(&lgrp, user_sid))
 335                         member = B_TRUE;
 336                 else for (i = 0, ids = gids->i_ids;
 337                     i < gids->i_cnt; i++, ids++) {
 338                         if (smb_lgrp_is_member(&lgrp, ids->i_sid)) {
 339                                 member = B_TRUE;
 340                                 break;
 341                         }
 342                 }
 343                 /* Careful: only count lgrp once */
 344                 if (member)
 345                         gcnt++;
 346                 smb_lgrp_free(&lgrp);
 347         }
 348         smb_lgrp_iterclose(&gi);
 349 
 350         if (gcnt == 0)
 351                 return (NT_STATUS_SUCCESS);
 352 
 353         /*
 354          * Second pass: add to groups list.
 355          * Do not modify gcnt after here.
 356          */
 357         if (smb_lgrp_iteropen(&gi) != SMB_LGRP_SUCCESS)
 358                 return (NT_STATUS_INTERNAL_ERROR);
 359 
 360         /*
 361          * Expand the list (copy to a new, larger one)
 362          * Note: were're copying pointers from the old
 363          * array to the new (larger) array, and then
 364          * adding new pointers after what we copied.
 365          */
 366         ret = 0;
 367         new_gids.i_cnt = gids->i_cnt;
 368         total_cnt = gids->i_cnt + gcnt;
 369         new_gids.i_ids = malloc(total_cnt * sizeof (smb_id_t));
 370         if (new_gids.i_ids == NULL) {
 371                 ret = NT_STATUS_NO_MEMORY;
 372                 goto out;
 373         }
 374         (void) memcpy(new_gids.i_ids, gids->i_ids,
 375             gids->i_cnt * sizeof (smb_id_t));
 376         new_ids = new_gids.i_ids + gids->i_cnt;
 377         (void) memset(new_ids, 0, gcnt * sizeof (smb_id_t));
 378 
 379         /*
 380          * Add group SIDs starting at the end of the
 381          * previous list.  (new_ids)
 382          */
 383         while (smb_lgrp_iterate(&gi, &lgrp) == SMB_LGRP_SUCCESS) {
 384                 member = B_FALSE;
 385                 if (smb_lgrp_is_member(&lgrp, user_sid))
 386                         member = B_TRUE;
 387                 else for (i = 0, ids = gids->i_ids;
 388                     i < gids->i_cnt; i++, ids++) {
 389                         if (smb_lgrp_is_member(&lgrp, ids->i_sid)) {
 390                                 member = B_TRUE;
 391                                 break;
 392                         }
 393                 }
 394                 if (member && (new_gids.i_cnt < (gids->i_cnt + gcnt))) {
 395                         new_ids->i_sid = smb_sid_dup(lgrp.sg_id.gs_sid);
 396                         if (new_ids->i_sid == NULL) {
 397                                 smb_lgrp_free(&lgrp);
 398                                 ret = NT_STATUS_NO_MEMORY;
 399                                 goto out;
 400                         }
 401                         new_ids->i_attrs = lgrp.sg_attr;
 402                         new_ids++;
 403                         new_gids.i_cnt++;
 404                 }
 405                 smb_lgrp_free(&lgrp);
 406         }
 407 
 408 out:
 409         smb_lgrp_iterclose(&gi);
 410 
 411         if (ret != 0) {
 412                 if (new_gids.i_ids != NULL) {
 413                         /*
 414                          * Free only the new sids we added.
 415                          * The old ones were copied ptrs.
 416                          */
 417                         ids = new_gids.i_ids + gids->i_cnt;
 418                         for (i = 0; i < gcnt; i++, ids++) {
 419                                 smb_sid_free(ids->i_sid);
 420                         }
 421                         free(new_gids.i_ids);
 422                 }
 423                 return (ret);
 424         }
 425 
 426         /*
 427          * Success! Update passed gids and
 428          * free the old array.
 429          */
 430         free(gids->i_ids);
 431         *gids = new_gids;
 432 
 433         return (NT_STATUS_SUCCESS);
 434 }
 435 
 436 /*
 437  * Returns the number of built-in or local groups stored
 438  * in /var/smb/smbgroup.db
 439  */
 440 int
 441 smb_sam_grp_cnt(smb_domain_type_t dtype)
 442 {
 443         int grpcnt;
 444         int rc;
 445 
 446         switch (dtype) {
 447         case SMB_DOMAIN_BUILTIN:
 448                 rc = smb_lgrp_numbydomain(SMB_DOMAIN_BUILTIN, &grpcnt);
 449                 break;
 450 
 451         case SMB_DOMAIN_LOCAL:
 452                 rc = smb_lgrp_numbydomain(SMB_DOMAIN_LOCAL, &grpcnt);
 453                 break;
 454 
 455         default:
 456                 rc = SMB_LGRP_INVALID_ARG;
 457         }
 458 
 459         return ((rc == SMB_LGRP_SUCCESS) ? grpcnt : 0);
 460 }
 461 
 462 /*
 463  * Determines whether the given SID is a member of the group
 464  * specified by gname.
 465  */
 466 boolean_t
 467 smb_sam_grp_ismember(const char *gname, smb_sid_t *sid)
 468 {
 469         smb_group_t grp;
 470         boolean_t ismember = B_FALSE;
 471 
 472         if (smb_lgrp_getbyname((char *)gname, &grp) == SMB_LGRP_SUCCESS) {
 473                 ismember = smb_lgrp_is_member(&grp, sid);
 474                 smb_lgrp_free(&grp);
 475         }
 476 
 477         return (ismember);
 478 }
 479 
 480 /*
 481  * Frees memories allocated for the passed account fields.
 482  * Initializes @account after all.
 483  */
 484 void
 485 smb_account_free(smb_account_t *account)
 486 {
 487         free(account->a_name);
 488         free(account->a_domain);
 489         smb_sid_free(account->a_sid);
 490         smb_sid_free(account->a_domsid);
 491 
 492         bzero(account, sizeof (smb_account_t));
 493 }
 494 
 495 /*
 496  * Validates the given account.
 497  */
 498 boolean_t
 499 smb_account_validate(smb_account_t *account)
 500 {
 501         return ((account->a_name != NULL) && (account->a_sid != NULL) &&
 502             (account->a_domain != NULL) && (account->a_domsid != NULL));
 503 }
 504 
 505 /*
 506  * Lookup local SMB user account database (/var/smb/smbpasswd)
 507  * if there's a match query its SID from idmap service and make
 508  * sure the SID is a local SID.
 509  *
 510  * The memory for the returned SID must be freed by the caller.
 511  */
 512 static uint32_t
 513 smb_sam_lookup_user(char *name, smb_sid_t **sid)
 514 {
 515         smb_passwd_t smbpw;
 516 
 517         if (smb_pwd_getpwnam(name, &smbpw) == NULL)
 518                 return (NT_STATUS_NO_SUCH_USER);
 519 
 520         if (smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, sid)
 521             != IDMAP_SUCCESS)
 522                 return (NT_STATUS_NONE_MAPPED);
 523 
 524         if (!smb_sid_islocal(*sid)) {
 525                 smb_sid_free(*sid);
 526                 return (NT_STATUS_NONE_MAPPED);
 527         }
 528 
 529         return (NT_STATUS_SUCCESS);
 530 }
 531 
 532 /*
 533  * Lookup local SMB group account database (/var/smb/smbgroup.db)
 534  * The memory for the returned SID must be freed by the caller.
 535  */
 536 static uint32_t
 537 smb_sam_lookup_group(char *name, smb_sid_t **sid)
 538 {
 539         smb_group_t grp;
 540 
 541         if (smb_lgrp_getbyname(name, &grp) != SMB_LGRP_SUCCESS)
 542                 return (NT_STATUS_NO_SUCH_ALIAS);
 543 
 544         *sid = smb_sid_dup(grp.sg_id.gs_sid);
 545         smb_lgrp_free(&grp);
 546 
 547         return ((*sid == NULL) ? NT_STATUS_NO_MEMORY : NT_STATUS_SUCCESS);
 548 }
 549 
 550 static smb_lwka_t *
 551 smb_lwka_lookup_name(char *name)
 552 {
 553         int i;
 554 
 555         for (i = 0; i < SMB_LWKA_NUM; i++) {
 556                 if (smb_strcasecmp(name, lwka_tbl[i].lwka_name, 0) == 0)
 557                         return (&lwka_tbl[i]);
 558         }
 559 
 560         return (NULL);
 561 }
 562 
 563 static smb_lwka_t *
 564 smb_lwka_lookup_sid(smb_sid_t *sid)
 565 {
 566         uint32_t rid;
 567         int i;
 568 
 569         (void) smb_sid_getrid(sid, &rid);
 570         if (rid > 999)
 571                 return (NULL);
 572 
 573         for (i = 0; i < SMB_LWKA_NUM; i++) {
 574                 if (rid == lwka_tbl[i].lwka_rid)
 575                         return (&lwka_tbl[i]);
 576         }
 577 
 578         return (NULL);
 579 }
 580 
 581 /*
 582  * smb_sid_islocal
 583  *
 584  * Check a SID to see if it belongs to the local domain.
 585  */
 586 boolean_t
 587 smb_sid_islocal(smb_sid_t *sid)
 588 {
 589         smb_domain_t di;
 590         boolean_t islocal = B_FALSE;
 591 
 592         if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
 593                 islocal = smb_sid_indomain(di.di_binsid, sid);
 594 
 595         return (islocal);
 596 }
 597 
 598 void
 599 smb_ids_free(smb_ids_t *ids)
 600 {
 601         smb_id_t *id;
 602         int i;
 603 
 604         if ((ids != NULL) && (ids->i_ids != NULL)) {
 605                 id = ids->i_ids;
 606                 for (i = 0; i < ids->i_cnt; i++, id++)
 607                         smb_sid_free(id->i_sid);
 608 
 609                 free(ids->i_ids);
 610         }
 611 }