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