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