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