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