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