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