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  */
  27 
  28 #include <syslog.h>
  29 #include <stdlib.h>
  30 #include <unistd.h>
  31 #include <limits.h>
  32 #include <strings.h>
  33 #include <synch.h>
  34 #include <errno.h>
  35 #include <sys/types.h>
  36 #include <sys/stat.h>
  37 #include <sys/avl.h>
  38 #include <fcntl.h>
  39 #include <thread.h>
  40 #include <pwd.h>
  41 #include <dlfcn.h>
  42 #include <link.h>
  43 #include <assert.h>
  44 #include <smbsrv/libsmb.h>
  45 
  46 #define SMB_PASSWD      "/var/smb/smbpasswd"
  47 #define SMB_OPASSWD     "/var/smb/osmbpasswd"
  48 #define SMB_PASSTEMP    "/var/smb/ptmp"
  49 #define SMB_PASSLCK     "/var/smb/.pwd.lock"
  50 
  51 #define SMB_PWD_DISABLE "*DIS*"
  52 #define SMB_PWD_BUFSIZE 256
  53 
  54 #define S_WAITTIME      15
  55 
  56 typedef enum {
  57         SMB_PWD_NAME = 0,
  58         SMB_PWD_UID,
  59         SMB_PWD_LMHASH,
  60         SMB_PWD_NTHASH,
  61         SMB_PWD_NARG
  62 } smb_pwdarg_t;
  63 
  64 static struct flock flock = { 0, 0, 0, 0, 0, 0 };
  65 static pid_t lck_pid = 0;       /* process's pid at last lock */
  66 static thread_t lck_tid = 0;    /* thread that holds the lock */
  67 static int fildes = -1;
  68 static mutex_t lck_lock = DEFAULTMUTEX;
  69 static void *smb_pwd_hdl = NULL;
  70 
  71 static struct {
  72         smb_passwd_t *(*pwop_getpwnam)(const char *, smb_passwd_t *);
  73         smb_passwd_t *(*pwop_getpwuid)(uid_t, smb_passwd_t *);
  74         int (*pwop_setcntl)(const char *, int);
  75         int (*pwop_setpasswd)(const char *, const char *);
  76         int (*pwop_num)(void);
  77         int (*pwop_iteropen)(smb_pwditer_t *);
  78         smb_luser_t *(*pwop_iterate)(smb_pwditer_t *);
  79         void (*pwop_iterclose)(smb_pwditer_t *);
  80 } smb_pwd_ops;
  81 
  82 static int smb_pwd_lock(void);
  83 static int smb_pwd_unlock(void);
  84 static int smb_pwd_flck(void);
  85 static int smb_pwd_fulck(void);
  86 
  87 /*
  88  * buffer structure used by smb_pwd_fgetent/smb_pwd_fputent
  89  */
  90 typedef struct smb_pwbuf {
  91         char            pw_buf[SMB_PWD_BUFSIZE];
  92         smb_passwd_t    *pw_pwd;
  93 } smb_pwbuf_t;
  94 
  95 /*
  96  * flag values used with smb_pwd_fgetent
  97  */
  98 #define SMB_PWD_GETF_ALL        1       /* get all the account info */
  99 #define SMB_PWD_GETF_NOPWD      2       /* password is not needed */
 100 
 101 static smb_pwbuf_t *smb_pwd_fgetent(FILE *, smb_pwbuf_t *, uint32_t);
 102 static int smb_pwd_fputent(FILE *, const smb_pwbuf_t *);
 103 static int smb_pwd_chgpwent(smb_passwd_t *, const char *, int);
 104 static int smb_pwd_update(const char *, const char *, int);
 105 
 106 /*
 107  * Local Users Cache
 108  *
 109  * Simplifying assumptions
 110  *
 111  *      o smbpasswd is a service private file and shouldn't be edited manually
 112  *      o accounts are only added/modified via passwd and/or smbadm CLIs
 113  *      o accounts are not removed but disabled using smbadm CLI
 114  *      o editing smbpasswd manually might result in cache inconsistency
 115  *
 116  * Cache is created and populated upon service startup.
 117  * Cache is updated each time users list is requested if there's been
 118  * any change in smbpasswd file. The change criteria is smbpasswd's
 119  * modification timestamp.
 120  */
 121 
 122 /*
 123  * User cache handle
 124  */
 125 typedef struct smb_uchandle {
 126         avl_tree_t      uc_cache;
 127         rwlock_t        uc_cache_lck;
 128         timestruc_t     uc_timestamp;
 129         uint32_t        uc_refcnt;
 130         uint32_t        uc_state;
 131         mutex_t         uc_mtx;
 132         cond_t          uc_cv;
 133 } smb_uchandle_t;
 134 
 135 #define SMB_UCHS_NOCACHE        0
 136 #define SMB_UCHS_CREATED        1
 137 #define SMB_UCHS_UPDATING       2
 138 #define SMB_UCHS_UPDATED        3
 139 #define SMB_UCHS_DESTROYING     4
 140 
 141 /*
 142  * User cache node
 143  */
 144 typedef struct smb_ucnode {
 145         smb_luser_t     cn_user;
 146         avl_node_t      cn_link;
 147 } smb_ucnode_t;
 148 
 149 static void smb_lucache_create(void);
 150 static void smb_lucache_destroy(void);
 151 static void smb_lucache_update(void);
 152 static int smb_lucache_num(void);
 153 static int smb_lucache_lock(void);
 154 static void smb_lucache_unlock(void);
 155 static int smb_lucache_do_update(void);
 156 static void smb_lucache_flush(void);
 157 
 158 static smb_uchandle_t smb_uch;
 159 
 160 /*
 161  * smb_pwd_init
 162  *
 163  * Initializes the cache if requested.
 164  * Checks to see if a password management utility library
 165  * is interposed. If yes then it'll initializes smb_pwd_ops
 166  * structure with function pointers from this library.
 167  */
 168 void
 169 smb_pwd_init(boolean_t create_cache)
 170 {
 171         if (create_cache) {
 172                 smb_lucache_create();
 173 #if 0
 174                 /*
 175                  * This pre-loading of the cache results in idmapd requests.
 176                  * With the change to allow idmapd to call into libsmb to
 177                  * map names and SIDs, this creates a circular startup
 178                  * dependency.  This call has been temporarily disabled to
 179                  * avoid this issue.  It can be enabled when the name/SID
 180                  * lookup can be done directly on the LSA service.
 181                  */
 182                 smb_lucache_update();
 183 #endif
 184         }
 185 
 186         smb_pwd_hdl = smb_dlopen();
 187         if (smb_pwd_hdl == NULL)
 188                 return;
 189 
 190         bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
 191 
 192         smb_pwd_ops.pwop_getpwnam =
 193             (smb_passwd_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_getpwnam");
 194 
 195         smb_pwd_ops.pwop_getpwuid =
 196             (smb_passwd_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_getpwuid");
 197 
 198         smb_pwd_ops.pwop_setcntl =
 199             (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_setcntl");
 200 
 201         smb_pwd_ops.pwop_setpasswd =
 202             (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_setpasswd");
 203 
 204         smb_pwd_ops.pwop_num =
 205             (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_num");
 206 
 207         smb_pwd_ops.pwop_iteropen =
 208             (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_iteropen");
 209 
 210         smb_pwd_ops.pwop_iterclose =
 211             (void (*)())dlsym(smb_pwd_hdl, "smb_pwd_iterclose");
 212 
 213         smb_pwd_ops.pwop_iterate =
 214             (smb_luser_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_iterate");
 215 
 216         if (smb_pwd_ops.pwop_getpwnam == NULL ||
 217             smb_pwd_ops.pwop_getpwuid == NULL ||
 218             smb_pwd_ops.pwop_setcntl == NULL ||
 219             smb_pwd_ops.pwop_setpasswd == NULL ||
 220             smb_pwd_ops.pwop_num == NULL ||
 221             smb_pwd_ops.pwop_iteropen == NULL ||
 222             smb_pwd_ops.pwop_iterclose == NULL ||
 223             smb_pwd_ops.pwop_iterate == NULL) {
 224                 smb_dlclose(smb_pwd_hdl);
 225                 smb_pwd_hdl = NULL;
 226 
 227                 /* If error or function(s) are missing, use original lib */
 228                 bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
 229         }
 230 }
 231 
 232 /*
 233  * smb_pwd_fini
 234  *
 235  * Destroys the cache.
 236  * Closes interposed library.
 237  */
 238 void
 239 smb_pwd_fini(void)
 240 {
 241         smb_lucache_destroy();
 242         smb_dlclose(smb_pwd_hdl);
 243         smb_pwd_hdl = NULL;
 244         bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
 245 }
 246 
 247 /*
 248  * smb_pwd_getpwnam
 249  *
 250  * Returns a smb password structure for the given user name.
 251  * smbpw is a pointer to a buffer allocated by the caller.
 252  *
 253  * Returns NULL upon failure.
 254  */
 255 smb_passwd_t *
 256 smb_pwd_getpwnam(const char *name, smb_passwd_t *smbpw)
 257 {
 258         boolean_t found = B_FALSE;
 259         smb_pwbuf_t pwbuf;
 260         FILE *fp;
 261         int err;
 262 
 263         if (smb_pwd_ops.pwop_getpwnam != NULL)
 264                 return (smb_pwd_ops.pwop_getpwnam(name, smbpw));
 265 
 266         err = smb_pwd_lock();
 267         if (err != SMB_PWE_SUCCESS) {
 268                 syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", err);
 269                 return (NULL);
 270         }
 271 
 272         if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
 273                 syslog(LOG_WARNING, "smb_pwdutil: open failed, %m");
 274                 (void) smb_pwd_unlock();
 275                 return (NULL);
 276         }
 277 
 278         pwbuf.pw_pwd = smbpw;
 279 
 280         while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
 281                 if (strcasecmp(name, smbpw->pw_name) == 0) {
 282                         found = B_TRUE;
 283                         break;
 284                 }
 285         }
 286 
 287         (void) fclose(fp);
 288         (void) smb_pwd_unlock();
 289 
 290         if (!found) {
 291                 bzero(smbpw, sizeof (smb_passwd_t));
 292                 return (NULL);
 293         }
 294 
 295         return (smbpw);
 296 }
 297 
 298 /*
 299  * smb_pwd_getpwuid
 300  *
 301  * Returns a smb password structure for the given UID
 302  * smbpw is a pointer to a buffer allocated by the caller.
 303  *
 304  * Returns NULL upon failure.
 305  */
 306 smb_passwd_t *
 307 smb_pwd_getpwuid(uid_t uid, smb_passwd_t *smbpw)
 308 {
 309         boolean_t found = B_FALSE;
 310         smb_pwbuf_t pwbuf;
 311         FILE *fp;
 312         int err;
 313 
 314         if (smb_pwd_ops.pwop_getpwuid != NULL)
 315                 return (smb_pwd_ops.pwop_getpwuid(uid, smbpw));
 316 
 317         err = smb_pwd_lock();
 318         if (err != SMB_PWE_SUCCESS) {
 319                 syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", err);
 320                 return (NULL);
 321         }
 322 
 323         if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
 324                 syslog(LOG_WARNING, "smb_pwdutil: open failed, %m");
 325                 (void) smb_pwd_unlock();
 326                 return (NULL);
 327         }
 328 
 329         pwbuf.pw_pwd = smbpw;
 330 
 331         while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
 332                 if (uid == smbpw->pw_uid) {
 333                         found = B_TRUE;
 334                         break;
 335                 }
 336         }
 337 
 338         (void) fclose(fp);
 339         (void) smb_pwd_unlock();
 340 
 341         if (!found) {
 342                 bzero(smbpw, sizeof (smb_passwd_t));
 343                 return (NULL);
 344         }
 345 
 346         return (smbpw);
 347 }
 348 
 349 /*
 350  * smb_pwd_setpasswd
 351  *
 352  * Update/add the given user to the smbpasswd file.
 353  */
 354 int
 355 smb_pwd_setpasswd(const char *name, const char *password)
 356 {
 357         if (smb_pwd_ops.pwop_setpasswd != NULL)
 358                 return (smb_pwd_ops.pwop_setpasswd(name, password));
 359 
 360         return (smb_pwd_update(name, password, 0));
 361 }
 362 
 363 /*
 364  * smb_pwd_setcntl
 365  *
 366  * Change the account state. This can be making the account
 367  * disable/enable or removing its LM hash.
 368  */
 369 int
 370 smb_pwd_setcntl(const char *name, int control)
 371 {
 372         if (smb_pwd_ops.pwop_setcntl != NULL)
 373                 return (smb_pwd_ops.pwop_setcntl(name, control));
 374 
 375         if (control == 0)
 376                 return (SMB_PWE_SUCCESS);
 377 
 378         return (smb_pwd_update(name, NULL, control));
 379 }
 380 
 381 /*
 382  * smb_pwd_num
 383  *
 384  * Returns the number of cached local users
 385  */
 386 int
 387 smb_pwd_num(void)
 388 {
 389         if (smb_pwd_ops.pwop_num != NULL)
 390                 return (smb_pwd_ops.pwop_num());
 391 
 392         smb_lucache_update();
 393 
 394         return (smb_lucache_num());
 395 }
 396 
 397 /*
 398  * smb_pwd_iteropen
 399  *
 400  * Initalizes the given iterator handle.
 401  * This handle will be used to iterate the users cache
 402  * by the caller. The cache will be locked for read and it
 403  * will remain locked until smb_pwd_iterclose() is called.
 404  */
 405 int
 406 smb_pwd_iteropen(smb_pwditer_t *iter)
 407 {
 408         if (iter == NULL)
 409                 return (SMB_PWE_INVALID_PARAM);
 410 
 411         if (smb_pwd_ops.pwop_iteropen != NULL)
 412                 return (smb_pwd_ops.pwop_iteropen(iter));
 413 
 414         iter->spi_next = NULL;
 415 
 416         smb_lucache_update();
 417 
 418         return (smb_lucache_lock());
 419 }
 420 
 421 /*
 422  * smb_pwd_iterate
 423  *
 424  * Scans through users cache using the given iterator
 425  */
 426 smb_luser_t *
 427 smb_pwd_iterate(smb_pwditer_t *iter)
 428 {
 429         smb_ucnode_t *ucnode;
 430 
 431         if (iter == NULL)
 432                 return (NULL);
 433 
 434         if (smb_pwd_ops.pwop_iterate != NULL)
 435                 return (smb_pwd_ops.pwop_iterate(iter));
 436 
 437         if (iter->spi_next == NULL)
 438                 ucnode = avl_first(&smb_uch.uc_cache);
 439         else
 440                 ucnode = AVL_NEXT(&smb_uch.uc_cache, iter->spi_next);
 441 
 442         if ((iter->spi_next = ucnode) != NULL)
 443                 return (&ucnode->cn_user);
 444 
 445         return (NULL);
 446 }
 447 
 448 /*
 449  * smb_pwd_iterclose
 450  *
 451  * Closes the given iterator. Effectively it only unlocks the cache
 452  */
 453 void
 454 smb_pwd_iterclose(smb_pwditer_t *iter)
 455 {
 456         if (smb_pwd_ops.pwop_iterclose != NULL) {
 457                 smb_pwd_ops.pwop_iterclose(iter);
 458                 return;
 459         }
 460 
 461         if (iter != NULL)
 462                 smb_lucache_unlock();
 463 }
 464 
 465 /*
 466  * smb_pwd_update
 467  *
 468  * Updates the password entry of the given user if the user already
 469  * has an entry, otherwise it'll add an entry for the user with
 470  * given password and control information.
 471  */
 472 static int
 473 smb_pwd_update(const char *name, const char *password, int control)
 474 {
 475         struct stat64 stbuf;
 476         FILE *src, *dst;
 477         int tempfd;
 478         int err = SMB_PWE_SUCCESS;
 479         smb_pwbuf_t pwbuf;
 480         smb_passwd_t smbpw;
 481         boolean_t newent = B_TRUE;
 482         boolean_t user_disable = B_FALSE;
 483         char uxbuf[1024];
 484         struct passwd uxpw;
 485         int64_t lm_level;
 486 
 487         err = smb_pwd_lock();
 488         if (err != SMB_PWE_SUCCESS)
 489                 return (err);
 490 
 491         if (stat64(SMB_PASSWD, &stbuf) < 0) {
 492                 err = SMB_PWE_STAT_FAILED;
 493                 goto passwd_exit;
 494         }
 495 
 496         if ((tempfd = open(SMB_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
 497                 err = SMB_PWE_OPEN_FAILED;
 498                 goto passwd_exit;
 499         }
 500 
 501         if ((dst = fdopen(tempfd, "wF")) == NULL) {
 502                 err = SMB_PWE_OPEN_FAILED;
 503                 goto passwd_exit;
 504         }
 505 
 506         if ((src = fopen(SMB_PASSWD, "rF")) == NULL) {
 507                 err = SMB_PWE_OPEN_FAILED;
 508                 (void) fclose(dst);
 509                 (void) unlink(SMB_PASSTEMP);
 510                 goto passwd_exit;
 511         }
 512 
 513         if (smb_config_getnum(SMB_CI_LM_LEVEL, &lm_level) != SMBD_SMF_OK)
 514                 lm_level = 4;
 515 
 516         if (lm_level >= 4)
 517                 control |= SMB_PWC_NOLM;
 518 
 519         pwbuf.pw_pwd = &smbpw;
 520 
 521         /*
 522          * copy old password entries to temporary file while replacing
 523          * the entry that matches "name"
 524          */
 525         while (smb_pwd_fgetent(src, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
 526                 if (strcmp(smbpw.pw_name, name) == 0) {
 527                         if ((control & SMB_PWC_DELETE) != 0) {
 528                                 /* exclude the entry from the new passwd file */
 529                                 newent = B_FALSE;
 530                                 err = SMB_PWE_SUCCESS;
 531                                 continue;
 532                         }
 533                         err = smb_pwd_chgpwent(&smbpw, password, control);
 534                         if (err == SMB_PWE_USER_DISABLE)
 535                                 user_disable = B_TRUE;
 536                         err = smb_pwd_fputent(dst, &pwbuf);
 537                         newent = B_FALSE;
 538                 } else {
 539                         err = smb_pwd_fputent(dst, &pwbuf);
 540                 }
 541 
 542                 if (err != SMB_PWE_SUCCESS) {
 543                         (void) fclose(src);
 544                         (void) fclose(dst);
 545                         goto passwd_exit;
 546                 }
 547         }
 548 
 549         if (newent) {
 550                 if (getpwnam_r(name, &uxpw, uxbuf, sizeof (uxbuf))) {
 551                         bzero(&smbpw, sizeof (smb_passwd_t));
 552                         (void) strlcpy(smbpw.pw_name, uxpw.pw_name,
 553                             sizeof (smbpw.pw_name));
 554                         smbpw.pw_uid = uxpw.pw_uid;
 555                         (void) smb_pwd_chgpwent(&smbpw, password, control);
 556                         err = smb_pwd_fputent(dst, &pwbuf);
 557                 } else {
 558                         err = SMB_PWE_USER_UNKNOWN;
 559                 }
 560 
 561                 if (err != SMB_PWE_SUCCESS) {
 562                         (void) fclose(src);
 563                         (void) fclose(dst);
 564                         goto passwd_exit;
 565                 }
 566         }
 567 
 568         (void) fclose(src);
 569         if (fclose(dst) != 0) {
 570                 err = SMB_PWE_CLOSE_FAILED;
 571                 goto passwd_exit; /* Don't trust the temporary file */
 572         }
 573 
 574         /* Rename temp to passwd */
 575         if (unlink(SMB_OPASSWD) && access(SMB_OPASSWD, 0) == 0) {
 576                 err = SMB_PWE_UPDATE_FAILED;
 577                 (void) unlink(SMB_PASSTEMP);
 578                 goto passwd_exit;
 579         }
 580 
 581         if (link(SMB_PASSWD, SMB_OPASSWD) == -1) {
 582                 err = SMB_PWE_UPDATE_FAILED;
 583                 (void) unlink(SMB_PASSTEMP);
 584                 goto passwd_exit;
 585         }
 586 
 587         if (rename(SMB_PASSTEMP, SMB_PASSWD) == -1) {
 588                 err = SMB_PWE_UPDATE_FAILED;
 589                 (void) unlink(SMB_PASSTEMP);
 590                 goto passwd_exit;
 591         }
 592 
 593         (void) chmod(SMB_PASSWD, 0400);
 594 
 595 passwd_exit:
 596         (void) smb_pwd_unlock();
 597         if ((err == SMB_PWE_SUCCESS) && user_disable)
 598                 err = SMB_PWE_USER_DISABLE;
 599 
 600         return (err);
 601 }
 602 
 603 /*
 604  * smb_pwd_fgetent
 605  *
 606  * Parse the buffer in the passed pwbuf and fill in the
 607  * smb password structure to point to the parsed information.
 608  * The entry format is:
 609  *
 610  *      <user-name>:<user-id>:<LM hash>:<NTLM hash>
 611  *
 612  * Returns a pointer to the passed pwbuf structure on success,
 613  * otherwise returns NULL.
 614  */
 615 static smb_pwbuf_t *
 616 smb_pwd_fgetent(FILE *fp, smb_pwbuf_t *pwbuf, uint32_t flags)
 617 {
 618         char *argv[SMB_PWD_NARG];
 619         char *pwentry;
 620         smb_passwd_t *pw;
 621         smb_pwdarg_t i;
 622         int lm_len, nt_len;
 623 
 624         pwentry = pwbuf->pw_buf;
 625         if (fgets(pwentry, SMB_PWD_BUFSIZE, fp) == NULL)
 626                 return (NULL);
 627         (void) trim_whitespace(pwentry);
 628 
 629         for (i = 0; i < SMB_PWD_NARG; ++i) {
 630                 if ((argv[i] = strsep((char **)&pwentry, ":")) == NULL)
 631                         return (NULL);
 632         }
 633 
 634         if ((*argv[SMB_PWD_NAME] == '\0') || (*argv[SMB_PWD_UID] == '\0'))
 635                 return (NULL);
 636 
 637         pw = pwbuf->pw_pwd;
 638         bzero(pw, sizeof (smb_passwd_t));
 639         pw->pw_uid = strtoul(argv[SMB_PWD_UID], 0, 10);
 640         (void) strlcpy(pw->pw_name, argv[SMB_PWD_NAME], sizeof (pw->pw_name));
 641 
 642         if (strcmp(argv[SMB_PWD_LMHASH], SMB_PWD_DISABLE) == 0) {
 643                 pw->pw_flags |= SMB_PWF_DISABLE;
 644                 if (flags != SMB_PWD_GETF_NOPWD) {
 645                         (void) strcpy((char *)pw->pw_lmhash, SMB_PWD_DISABLE);
 646                         (void) strcpy((char *)pw->pw_nthash, SMB_PWD_DISABLE);
 647                 }
 648                 return (pwbuf);
 649         }
 650 
 651         if (flags == SMB_PWD_GETF_NOPWD)
 652                 return (pwbuf);
 653 
 654         lm_len = strlen(argv[SMB_PWD_LMHASH]);
 655         if (lm_len == SMBAUTH_HEXHASH_SZ) {
 656                 (void) hextobin(argv[SMB_PWD_LMHASH], SMBAUTH_HEXHASH_SZ,
 657                     (char *)pw->pw_lmhash, SMBAUTH_HASH_SZ);
 658 
 659                 pw->pw_flags |= SMB_PWF_LM;
 660         } else if (lm_len != 0) {
 661                 return (NULL);
 662         }
 663 
 664         nt_len = strlen(argv[SMB_PWD_NTHASH]);
 665         if (nt_len == SMBAUTH_HEXHASH_SZ) {
 666                 (void) hextobin(argv[SMB_PWD_NTHASH], SMBAUTH_HEXHASH_SZ,
 667                     (char *)pw->pw_nthash, SMBAUTH_HASH_SZ);
 668 
 669                 pw->pw_flags |= SMB_PWF_NT;
 670         } else if (nt_len != 0) {
 671                 return (NULL);
 672         }
 673 
 674         return (pwbuf);
 675 }
 676 
 677 /*
 678  * smb_pwd_chgpwent
 679  *
 680  * Updates the given smb_passwd_t structure with given password and
 681  * control information.
 682  */
 683 static int
 684 smb_pwd_chgpwent(smb_passwd_t *smbpw, const char *password, int control)
 685 {
 686         if (control & SMB_PWC_DISABLE) {
 687                 /* disable the user */
 688                 smbpw->pw_flags |= SMB_PWF_DISABLE;
 689                 (void) strcpy((char *)smbpw->pw_lmhash, SMB_PWD_DISABLE);
 690                 (void) strcpy((char *)smbpw->pw_nthash, SMB_PWD_DISABLE);
 691                 smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
 692                 return (SMB_PWE_SUCCESS);
 693         }
 694 
 695         if ((control & SMB_PWC_ENABLE) && (smbpw->pw_flags & SMB_PWF_DISABLE)) {
 696                 /* enable the user if it's been disabled */
 697                 *smbpw->pw_lmhash = '\0';
 698                 *smbpw->pw_nthash = '\0';
 699                 smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
 700                 return (SMB_PWE_SUCCESS);
 701         }
 702 
 703         /* No password update if account is disabled */
 704         if (smbpw->pw_flags & SMB_PWF_DISABLE)
 705                 return (SMB_PWE_USER_DISABLE);
 706 
 707         /* This call was just to update the control flags */
 708         if (password == NULL)
 709                 return (SMB_PWE_SUCCESS);
 710 
 711         if (control & SMB_PWC_NOLM) {
 712                 /* LM hash should not be present */
 713                 smbpw->pw_flags &= ~SMB_PWF_LM;
 714                 *smbpw->pw_lmhash = '\0';
 715         } else {
 716                 smbpw->pw_flags |= SMB_PWF_LM;
 717                 (void) smb_auth_lm_hash(password, smbpw->pw_lmhash);
 718         }
 719 
 720         smbpw->pw_flags |= SMB_PWF_NT;
 721         (void) smb_auth_ntlm_hash(password, smbpw->pw_nthash);
 722         return (SMB_PWE_SUCCESS);
 723 }
 724 
 725 /*
 726  * smb_pwd_fputent
 727  *
 728  * If LM/NTLM hash are present, converts them to hex string
 729  * and write them along with user's name and Id to the smbpasswd
 730  * file.
 731  */
 732 static int
 733 smb_pwd_fputent(FILE *fp, const smb_pwbuf_t *pwbuf)
 734 {
 735         smb_passwd_t *pw = pwbuf->pw_pwd;
 736         char hex_nthash[SMBAUTH_HEXHASH_SZ+1];
 737         char hex_lmhash[SMBAUTH_HEXHASH_SZ+1];
 738         int rc;
 739 
 740         if ((pw->pw_flags & SMB_PWF_LM) == SMB_PWF_LM) {
 741                 (void) bintohex((char *)pw->pw_lmhash, SMBAUTH_HASH_SZ,
 742                     hex_lmhash, SMBAUTH_HEXHASH_SZ);
 743                 hex_lmhash[SMBAUTH_HEXHASH_SZ] = '\0';
 744         } else {
 745                 (void) strcpy(hex_lmhash, (char *)pw->pw_lmhash);
 746         }
 747 
 748         if ((pw->pw_flags & SMB_PWF_NT) == SMB_PWF_NT) {
 749                 (void) bintohex((char *)pw->pw_nthash, SMBAUTH_HASH_SZ,
 750                     hex_nthash, SMBAUTH_HEXHASH_SZ);
 751                 hex_nthash[SMBAUTH_HEXHASH_SZ] = '\0';
 752         } else {
 753                 (void) strcpy(hex_nthash, (char *)pw->pw_nthash);
 754         }
 755 
 756         rc = fprintf(fp, "%s:%u:%s:%s\n", pw->pw_name, pw->pw_uid,
 757             hex_lmhash, hex_nthash);
 758 
 759         if (rc <= 0)
 760                 return (SMB_PWE_WRITE_FAILED);
 761 
 762         return (SMB_PWE_SUCCESS);
 763 }
 764 
 765 /*
 766  * smb_pwd_lock
 767  *
 768  * A wrapper around smb_pwd_flck() which locks smb password
 769  * file so that only one thread at a time is operational.
 770  */
 771 static int
 772 smb_pwd_lock(void)
 773 {
 774         int res;
 775 
 776         if (smb_pwd_flck()) {
 777                 switch (errno) {
 778                 case EINTR:
 779                         res = SMB_PWE_BUSY;
 780                         break;
 781                 case EACCES:
 782                         res = SMB_PWE_DENIED;
 783                         break;
 784                 case 0:
 785                         res = SMB_PWE_SUCCESS;
 786                         break;
 787                 }
 788         } else
 789                 res = SMB_PWE_SUCCESS;
 790 
 791         return (res);
 792 }
 793 
 794 /*
 795  * smb_pwd_unlock
 796  *
 797  * A wrapper around smb_pwd_fulck() which unlocks
 798  * smb password file.
 799  */
 800 static int
 801 smb_pwd_unlock(void)
 802 {
 803         if (smb_pwd_fulck())
 804                 return (SMB_PWE_SYSTEM_ERROR);
 805 
 806         return (SMB_PWE_SUCCESS);
 807 }
 808 
 809 /*
 810  * smb_pwd_flck
 811  *
 812  * Creates a lock file and grabs an exclusive (write) lock on it.
 813  */
 814 static int
 815 smb_pwd_flck(void)
 816 {
 817         int seconds = 0;
 818 
 819         (void) mutex_lock(&lck_lock);
 820         for (;;) {
 821                 if (lck_pid != 0 && lck_pid != getpid()) {
 822                         /* somebody forked */
 823                         lck_pid = 0;
 824                         lck_tid = 0;
 825                 }
 826 
 827                 if (lck_tid == 0) {
 828                         if ((fildes = creat(SMB_PASSLCK, 0600)) == -1)
 829                                 break;
 830                         flock.l_type = F_WRLCK;
 831                         if (fcntl(fildes, F_SETLK, &flock) != -1) {
 832                                 lck_pid = getpid();
 833                                 lck_tid = thr_self();
 834                                 (void) mutex_unlock(&lck_lock);
 835                                 return (0);
 836                         }
 837                         (void) close(fildes);
 838                         fildes = -1;
 839                 }
 840 
 841                 if (seconds++ >= S_WAITTIME) {
 842                         /*
 843                          * For compatibility with the past, pretend
 844                          * that we were interrupted by SIGALRM.
 845                          */
 846                         errno = EINTR;
 847                         break;
 848                 }
 849 
 850                 (void) mutex_unlock(&lck_lock);
 851                 (void) sleep(1);
 852                 (void) mutex_lock(&lck_lock);
 853         }
 854         (void) mutex_unlock(&lck_lock);
 855 
 856         return (-1);
 857 }
 858 
 859 /*
 860  * smb_pwd_fulck
 861  *
 862  * Unlocks smb password file for operations done via
 863  * this library APIs.
 864  */
 865 static int
 866 smb_pwd_fulck(void)
 867 {
 868         (void) mutex_lock(&lck_lock);
 869         if (lck_tid == thr_self() && fildes >= 0) {
 870                 flock.l_type = F_UNLCK;
 871                 (void) fcntl(fildes, F_SETLK, &flock);
 872                 (void) close(fildes);
 873                 fildes = -1;
 874                 lck_pid = 0;
 875                 lck_tid = 0;
 876                 (void) mutex_unlock(&lck_lock);
 877                 return (0);
 878         }
 879         (void) mutex_unlock(&lck_lock);
 880         return (-1);
 881 }
 882 
 883 /*
 884  * Local User Cache Functions
 885  *
 886  * Local user cache is implemented using AVL tree
 887  */
 888 
 889 /*
 890  * smb_lucache_cmp
 891  *
 892  * AVL compare function, the key is username.
 893  */
 894 static int
 895 smb_lucache_cmp(const void *p1, const void *p2)
 896 {
 897         smb_ucnode_t *u1 = (smb_ucnode_t *)p1;
 898         smb_ucnode_t *u2 = (smb_ucnode_t *)p2;
 899         int rc;
 900 
 901         rc = strcmp(u1->cn_user.su_name, u2->cn_user.su_name);
 902 
 903         if (rc < 0)
 904                 return (-1);
 905 
 906         if (rc > 0)
 907                 return (1);
 908 
 909         return (0);
 910 }
 911 
 912 /*
 913  * smb_lucache_update
 914  *
 915  * Updates the cache if needed. Whether an update is needed
 916  * is determined based on smbpasswd file modification timestamp
 917  */
 918 static void
 919 smb_lucache_update(void)
 920 {
 921         struct stat64 stbuf;
 922         int rc;
 923 
 924         (void) mutex_lock(&smb_uch.uc_mtx);
 925         switch (smb_uch.uc_state) {
 926         default:
 927         case SMB_UCHS_NOCACHE:
 928                 assert(0);
 929                 (void) mutex_unlock(&smb_uch.uc_mtx);
 930                 return;
 931 
 932         case SMB_UCHS_CREATED:
 933         case SMB_UCHS_UPDATED:
 934                 break;
 935 
 936         case SMB_UCHS_UPDATING:
 937                 /* Want only one thread executing this function at a time */
 938                 (void) mutex_unlock(&smb_uch.uc_mtx);
 939                 return;
 940 
 941         case SMB_UCHS_DESTROYING:
 942                 (void) mutex_unlock(&smb_uch.uc_mtx);
 943                 return;
 944         }
 945 
 946         /*
 947          * smb_pwd_lock() is not called here so it can
 948          * be checked quickly whether an updated is needed
 949          */
 950         if (stat64(SMB_PASSWD, &stbuf) < 0) {
 951                 (void) mutex_unlock(&smb_uch.uc_mtx);
 952                 if (errno != ENOENT)
 953                         return;
 954 
 955                 /* no smbpasswd file; empty the cache */
 956                 smb_lucache_flush();
 957                 return;
 958         }
 959 
 960         if (stbuf.st_size == 0) {
 961                 (void) mutex_unlock(&smb_uch.uc_mtx);
 962 
 963                 /* empty smbpasswd file; empty the cache */
 964                 smb_lucache_flush();
 965                 return;
 966         }
 967 
 968         if ((smb_uch.uc_timestamp.tv_sec == stbuf.st_mtim.tv_sec) &&
 969             (smb_uch.uc_timestamp.tv_nsec == stbuf.st_mtim.tv_nsec)) {
 970                 (void) mutex_unlock(&smb_uch.uc_mtx);
 971                 /* No changes since the last cache update */
 972                 return;
 973         }
 974 
 975         smb_uch.uc_state = SMB_UCHS_UPDATING;
 976         smb_uch.uc_refcnt++;
 977         (void) mutex_unlock(&smb_uch.uc_mtx);
 978 
 979         rc = smb_lucache_do_update();
 980 
 981         (void) mutex_lock(&smb_uch.uc_mtx);
 982         if ((rc == SMB_PWE_SUCCESS) && (stat64(SMB_PASSWD, &stbuf) == 0))
 983                 smb_uch.uc_timestamp = stbuf.st_mtim;
 984         smb_uch.uc_state = SMB_UCHS_UPDATED;
 985         smb_uch.uc_refcnt--;
 986         (void) cond_broadcast(&smb_uch.uc_cv);
 987         (void) mutex_unlock(&smb_uch.uc_mtx);
 988 }
 989 
 990 /*
 991  * smb_lucache_do_update
 992  *
 993  * This function takes care of updating the AVL tree.
 994  * If an entry has been updated, it'll be modified in place.
 995  *
 996  * New entries will be added to a temporary AVL tree then
 997  * passwod file is unlocked and all the new entries will
 998  * be transferred to the main cache from the temporary tree.
 999  *
1000  * This function MUST NOT be called directly
1001  */
1002 static int
1003 smb_lucache_do_update(void)
1004 {
1005         avl_tree_t tmp_cache;
1006         smb_pwbuf_t pwbuf;
1007         smb_passwd_t smbpw;
1008         smb_ucnode_t uc_node;
1009         smb_ucnode_t *uc_newnode;
1010         smb_luser_t *user;
1011         smb_sid_t *sid;
1012         idmap_stat idm_stat;
1013         int rc = SMB_PWE_SUCCESS;
1014         void *cookie = NULL;
1015         FILE *fp;
1016 
1017         if ((rc = smb_pwd_lock()) != SMB_PWE_SUCCESS) {
1018                 syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", rc);
1019                 return (rc);
1020         }
1021 
1022         if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
1023                 syslog(LOG_WARNING, "smb_pwdutil: open failed, %m");
1024                 (void) smb_pwd_unlock();
1025                 return (SMB_PWE_OPEN_FAILED);
1026         }
1027 
1028         avl_create(&tmp_cache, smb_lucache_cmp,
1029             sizeof (smb_ucnode_t), offsetof(smb_ucnode_t, cn_link));
1030 
1031         bzero(&pwbuf, sizeof (smb_pwbuf_t));
1032         pwbuf.pw_pwd = &smbpw;
1033 
1034         (void) rw_rdlock(&smb_uch.uc_cache_lck);
1035 
1036         while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_NOPWD) != NULL) {
1037                 uc_node.cn_user.su_name = smbpw.pw_name;
1038                 uc_newnode = avl_find(&smb_uch.uc_cache, &uc_node, NULL);
1039                 if (uc_newnode) {
1040                         /* update the node info */
1041                         uc_newnode->cn_user.su_ctrl = smbpw.pw_flags;
1042                         continue;
1043                 }
1044 
1045                 /* create a new node */
1046                 if ((uc_newnode = malloc(sizeof (smb_ucnode_t))) == NULL) {
1047                         rc = SMB_PWE_NO_MEMORY;
1048                         break;
1049                 }
1050 
1051                 bzero(uc_newnode, sizeof (smb_ucnode_t));
1052                 user = &uc_newnode->cn_user;
1053                 user->su_ctrl = smbpw.pw_flags;
1054 
1055                 idm_stat = smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, &sid);
1056                 if (idm_stat != IDMAP_SUCCESS) {
1057                         syslog(LOG_WARNING, "smb_pwdutil: couldn't obtain SID "
1058                             "for uid=%u (%d)", smbpw.pw_uid, idm_stat);
1059                         free(uc_newnode);
1060                         continue;
1061                 }
1062                 (void) smb_sid_getrid(sid, &user->su_rid);
1063                 smb_sid_free(sid);
1064 
1065                 user->su_name = strdup(smbpw.pw_name);
1066                 if (user->su_name == NULL) {
1067                         rc = SMB_PWE_NO_MEMORY;
1068                         free(uc_newnode);
1069                         break;
1070                 }
1071 
1072                 avl_add(&tmp_cache, uc_newnode);
1073         }
1074 
1075         (void) rw_unlock(&smb_uch.uc_cache_lck);
1076         (void) fclose(fp);
1077         (void) smb_pwd_unlock();
1078 
1079         /* Destroy the temporary list */
1080         (void) rw_wrlock(&smb_uch.uc_cache_lck);
1081         while ((uc_newnode = avl_destroy_nodes(&tmp_cache, &cookie)) != NULL) {
1082                 avl_add(&smb_uch.uc_cache, uc_newnode);
1083         }
1084         (void) rw_unlock(&smb_uch.uc_cache_lck);
1085 
1086         avl_destroy(&tmp_cache);
1087 
1088         return (rc);
1089 }
1090 
1091 /*
1092  * smb_lucache_create
1093  *
1094  * Creates the AVL tree and initializes the global user cache handle.
1095  * This function doesn't populate the cache.
1096  * User cache is only created by smbd at startup
1097  */
1098 static void
1099 smb_lucache_create(void)
1100 {
1101         (void) mutex_lock(&smb_uch.uc_mtx);
1102         if (smb_uch.uc_state != SMB_UCHS_NOCACHE) {
1103                 (void) mutex_unlock(&smb_uch.uc_mtx);
1104                 return;
1105         }
1106 
1107         avl_create(&smb_uch.uc_cache, smb_lucache_cmp,
1108             sizeof (smb_ucnode_t), offsetof(smb_ucnode_t, cn_link));
1109 
1110         smb_uch.uc_state = SMB_UCHS_CREATED;
1111         bzero(&smb_uch.uc_timestamp, sizeof (timestruc_t));
1112         smb_uch.uc_refcnt = 0;
1113         (void) mutex_unlock(&smb_uch.uc_mtx);
1114 }
1115 
1116 /*
1117  * smb_lucache_flush
1118  *
1119  * Removes and frees all the cache entries
1120  */
1121 static void
1122 smb_lucache_flush(void)
1123 {
1124         void *cookie = NULL;
1125         smb_ucnode_t *ucnode;
1126 
1127         (void) rw_wrlock(&smb_uch.uc_cache_lck);
1128         while ((ucnode = avl_destroy_nodes(&smb_uch.uc_cache, &cookie))
1129             != NULL) {
1130                 free(ucnode->cn_user.su_name);
1131                 free(ucnode->cn_user.su_fullname);
1132                 free(ucnode->cn_user.su_desc);
1133                 free(ucnode);
1134         }
1135         (void) rw_unlock(&smb_uch.uc_cache_lck);
1136 }
1137 
1138 /*
1139  * smb_lucache_destroy
1140  *
1141  * Destroys the cache.
1142  * This function is only called in smb_pwd_fini()
1143  * User cache is only destroyed by smbd upon shutdown
1144  */
1145 static void
1146 smb_lucache_destroy(void)
1147 {
1148         (void) mutex_lock(&smb_uch.uc_mtx);
1149         switch (smb_uch.uc_state) {
1150         case SMB_UCHS_NOCACHE:
1151         case SMB_UCHS_DESTROYING:
1152                 (void) mutex_unlock(&smb_uch.uc_mtx);
1153                 return;
1154 
1155         default:
1156                 break;
1157         }
1158 
1159         smb_uch.uc_state = SMB_UCHS_DESTROYING;
1160 
1161         while (smb_uch.uc_refcnt > 0)
1162                 (void) cond_wait(&smb_uch.uc_cv, &smb_uch.uc_mtx);
1163 
1164         smb_lucache_flush();
1165 
1166         avl_destroy(&smb_uch.uc_cache);
1167         smb_uch.uc_state = SMB_UCHS_NOCACHE;
1168         (void) mutex_unlock(&smb_uch.uc_mtx);
1169 }
1170 
1171 /*
1172  * smb_lucache_lock
1173  *
1174  * Locks the user cache for reading and also
1175  * increment the handle reference count.
1176  */
1177 static int
1178 smb_lucache_lock(void)
1179 {
1180         (void) mutex_lock(&smb_uch.uc_mtx);
1181         switch (smb_uch.uc_state) {
1182         case SMB_UCHS_NOCACHE:
1183                 assert(0);
1184                 (void) mutex_unlock(&smb_uch.uc_mtx);
1185                 return (SMB_PWE_DENIED);
1186 
1187         case SMB_UCHS_DESTROYING:
1188                 (void) mutex_unlock(&smb_uch.uc_mtx);
1189                 return (SMB_PWE_DENIED);
1190         }
1191         smb_uch.uc_refcnt++;
1192         (void) mutex_unlock(&smb_uch.uc_mtx);
1193 
1194         (void) rw_rdlock(&smb_uch.uc_cache_lck);
1195         return (SMB_PWE_SUCCESS);
1196 }
1197 
1198 /*
1199  * smb_lucache_unlock
1200  *
1201  * Unlock the cache
1202  */
1203 static void
1204 smb_lucache_unlock(void)
1205 {
1206         (void) rw_unlock(&smb_uch.uc_cache_lck);
1207 
1208         (void) mutex_lock(&smb_uch.uc_mtx);
1209         smb_uch.uc_refcnt--;
1210         (void) cond_broadcast(&smb_uch.uc_cv);
1211         (void) mutex_unlock(&smb_uch.uc_mtx);
1212 }
1213 
1214 /*
1215  * smb_lucache_num
1216  *
1217  * Returns the number of cache entries
1218  */
1219 static int
1220 smb_lucache_num(void)
1221 {
1222         int num;
1223 
1224         (void) mutex_lock(&smb_uch.uc_mtx);
1225         switch (smb_uch.uc_state) {
1226         case SMB_UCHS_NOCACHE:
1227                 assert(0);
1228                 (void) mutex_unlock(&smb_uch.uc_mtx);
1229                 return (0);
1230 
1231         case SMB_UCHS_DESTROYING:
1232                 (void) mutex_unlock(&smb_uch.uc_mtx);
1233                 return (0);
1234         }
1235         (void) mutex_unlock(&smb_uch.uc_mtx);
1236 
1237         (void) rw_rdlock(&smb_uch.uc_cache_lck);
1238         num = (int)avl_numnodes(&smb_uch.uc_cache);
1239         (void) rw_unlock(&smb_uch.uc_cache_lck);
1240 
1241         return (num);
1242 }