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