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
56typedef 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
64static struct flock flock = { 0, 0, 0, 0, 0, 0 };
65static pid_t lck_pid = 0;	/* process's pid at last lock */
66static thread_t lck_tid = 0;	/* thread that holds the lock */
67static int fildes = -1;
68static mutex_t lck_lock = DEFAULTMUTEX;
69static void *smb_pwd_hdl = NULL;
70
71static 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
82static int smb_pwd_lock(void);
83static int smb_pwd_unlock(void);
84static int smb_pwd_flck(void);
85static int smb_pwd_fulck(void);
86
87/*
88 * buffer structure used by smb_pwd_fgetent/smb_pwd_fputent
89 */
90typedef 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
101static smb_pwbuf_t *smb_pwd_fgetent(FILE *, smb_pwbuf_t *, uint32_t);
102static int smb_pwd_fputent(FILE *, const smb_pwbuf_t *);
103static int smb_pwd_chgpwent(smb_passwd_t *, const char *, int);
104static 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 */
125typedef 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 */
144typedef struct smb_ucnode {
145	smb_luser_t	cn_user;
146	avl_node_t	cn_link;
147} smb_ucnode_t;
148
149static void smb_lucache_create(void);
150static void smb_lucache_destroy(void);
151static void smb_lucache_update(void);
152static int smb_lucache_num(void);
153static int smb_lucache_lock(void);
154static void smb_lucache_unlock(void);
155static int smb_lucache_do_update(void);
156static void smb_lucache_flush(void);
157
158static 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 */
168void
169smb_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 */
238void
239smb_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 */
255smb_passwd_t *
256smb_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 */
306smb_passwd_t *
307smb_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 */
354int
355smb_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 */
369int
370smb_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 */
386int
387smb_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 */
405int
406smb_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 */
426smb_luser_t *
427smb_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 */
453void
454smb_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 */
472static int
473smb_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
595passwd_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 */
615static smb_pwbuf_t *
616smb_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 */
683static int
684smb_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 */
732static int
733smb_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 */
771static int
772smb_pwd_lock(void)
773{
774	int res;
775
776	if (smb_pwd_flck()) {
777		switch (errno) {
778		case EINTR:
779			res = SMB_PWE_BUSY;
780			break;
781		case EACCES:
782			res = SMB_PWE_DENIED;
783			break;
784		case 0:
785			res = SMB_PWE_SUCCESS;
786			break;
787		}
788	} else
789		res = SMB_PWE_SUCCESS;
790
791	return (res);
792}
793
794/*
795 * smb_pwd_unlock
796 *
797 * A wrapper around smb_pwd_fulck() which unlocks
798 * smb password file.
799 */
800static int
801smb_pwd_unlock(void)
802{
803	if (smb_pwd_fulck())
804		return (SMB_PWE_SYSTEM_ERROR);
805
806	return (SMB_PWE_SUCCESS);
807}
808
809/*
810 * smb_pwd_flck
811 *
812 * Creates a lock file and grabs an exclusive (write) lock on it.
813 */
814static int
815smb_pwd_flck(void)
816{
817	int seconds = 0;
818
819	(void) mutex_lock(&lck_lock);
820	for (;;) {
821		if (lck_pid != 0 && lck_pid != getpid()) {
822			/* somebody forked */
823			lck_pid = 0;
824			lck_tid = 0;
825		}
826
827		if (lck_tid == 0) {
828			if ((fildes = creat(SMB_PASSLCK, 0600)) == -1)
829				break;
830			flock.l_type = F_WRLCK;
831			if (fcntl(fildes, F_SETLK, &flock) != -1) {
832				lck_pid = getpid();
833				lck_tid = thr_self();
834				(void) mutex_unlock(&lck_lock);
835				return (0);
836			}
837			(void) close(fildes);
838			fildes = -1;
839		}
840
841		if (seconds++ >= S_WAITTIME) {
842			/*
843			 * For compatibility with the past, pretend
844			 * that we were interrupted by SIGALRM.
845			 */
846			errno = EINTR;
847			break;
848		}
849
850		(void) mutex_unlock(&lck_lock);
851		(void) sleep(1);
852		(void) mutex_lock(&lck_lock);
853	}
854	(void) mutex_unlock(&lck_lock);
855
856	return (-1);
857}
858
859/*
860 * smb_pwd_fulck
861 *
862 * Unlocks smb password file for operations done via
863 * this library APIs.
864 */
865static int
866smb_pwd_fulck(void)
867{
868	(void) mutex_lock(&lck_lock);
869	if (lck_tid == thr_self() && fildes >= 0) {
870		flock.l_type = F_UNLCK;
871		(void) fcntl(fildes, F_SETLK, &flock);
872		(void) close(fildes);
873		fildes = -1;
874		lck_pid = 0;
875		lck_tid = 0;
876		(void) mutex_unlock(&lck_lock);
877		return (0);
878	}
879	(void) mutex_unlock(&lck_lock);
880	return (-1);
881}
882
883/*
884 * Local User Cache Functions
885 *
886 * Local user cache is implemented using AVL tree
887 */
888
889/*
890 * smb_lucache_cmp
891 *
892 * AVL compare function, the key is username.
893 */
894static int
895smb_lucache_cmp(const void *p1, const void *p2)
896{
897	smb_ucnode_t *u1 = (smb_ucnode_t *)p1;
898	smb_ucnode_t *u2 = (smb_ucnode_t *)p2;
899	int rc;
900
901	rc = strcmp(u1->cn_user.su_name, u2->cn_user.su_name);
902
903	if (rc < 0)
904		return (-1);
905
906	if (rc > 0)
907		return (1);
908
909	return (0);
910}
911
912/*
913 * smb_lucache_update
914 *
915 * Updates the cache if needed. Whether an update is needed
916 * is determined based on smbpasswd file modification timestamp
917 */
918static void
919smb_lucache_update(void)
920{
921	struct stat64 stbuf;
922	int rc;
923
924	(void) mutex_lock(&smb_uch.uc_mtx);
925	switch (smb_uch.uc_state) {
926	default:
927	case SMB_UCHS_NOCACHE:
928		assert(0);
929		(void) mutex_unlock(&smb_uch.uc_mtx);
930		return;
931
932	case SMB_UCHS_CREATED:
933	case SMB_UCHS_UPDATED:
934		break;
935
936	case SMB_UCHS_UPDATING:
937		/* Want only one thread executing this function at a time */
938		(void) mutex_unlock(&smb_uch.uc_mtx);
939		return;
940
941	case SMB_UCHS_DESTROYING:
942		(void) mutex_unlock(&smb_uch.uc_mtx);
943		return;
944	}
945
946	/*
947	 * smb_pwd_lock() is not called here so it can
948	 * be checked quickly whether an updated is needed
949	 */
950	if (stat64(SMB_PASSWD, &stbuf) < 0) {
951		(void) mutex_unlock(&smb_uch.uc_mtx);
952		if (errno != ENOENT)
953			return;
954
955		/* no smbpasswd file; empty the cache */
956		smb_lucache_flush();
957		return;
958	}
959
960	if (stbuf.st_size == 0) {
961		(void) mutex_unlock(&smb_uch.uc_mtx);
962
963		/* empty smbpasswd file; empty the cache */
964		smb_lucache_flush();
965		return;
966	}
967
968	if ((smb_uch.uc_timestamp.tv_sec == stbuf.st_mtim.tv_sec) &&
969	    (smb_uch.uc_timestamp.tv_nsec == stbuf.st_mtim.tv_nsec)) {
970		(void) mutex_unlock(&smb_uch.uc_mtx);
971		/* No changes since the last cache update */
972		return;
973	}
974
975	smb_uch.uc_state = SMB_UCHS_UPDATING;
976	smb_uch.uc_refcnt++;
977	(void) mutex_unlock(&smb_uch.uc_mtx);
978
979	rc = smb_lucache_do_update();
980
981	(void) mutex_lock(&smb_uch.uc_mtx);
982	if ((rc == SMB_PWE_SUCCESS) && (stat64(SMB_PASSWD, &stbuf) == 0))
983		smb_uch.uc_timestamp = stbuf.st_mtim;
984	smb_uch.uc_state = SMB_UCHS_UPDATED;
985	smb_uch.uc_refcnt--;
986	(void) cond_broadcast(&smb_uch.uc_cv);
987	(void) mutex_unlock(&smb_uch.uc_mtx);
988}
989
990/*
991 * smb_lucache_do_update
992 *
993 * This function takes care of updating the AVL tree.
994 * If an entry has been updated, it'll be modified in place.
995 *
996 * New entries will be added to a temporary AVL tree then
997 * passwod file is unlocked and all the new entries will
998 * be transferred to the main cache from the temporary tree.
999 *
1000 * This function MUST NOT be called directly
1001 */
1002static int
1003smb_lucache_do_update(void)
1004{
1005	avl_tree_t tmp_cache;
1006	smb_pwbuf_t pwbuf;
1007	smb_passwd_t smbpw;
1008	smb_ucnode_t uc_node;
1009	smb_ucnode_t *uc_newnode;
1010	smb_luser_t *user;
1011	smb_sid_t *sid;
1012	idmap_stat idm_stat;
1013	int rc = SMB_PWE_SUCCESS;
1014	void *cookie = NULL;
1015	FILE *fp;
1016
1017	if ((rc = smb_pwd_lock()) != SMB_PWE_SUCCESS) {
1018		syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", rc);
1019		return (rc);
1020	}
1021
1022	if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
1023		syslog(LOG_WARNING, "smb_pwdutil: open failed, %m");
1024		(void) smb_pwd_unlock();
1025		return (SMB_PWE_OPEN_FAILED);
1026	}
1027
1028	avl_create(&tmp_cache, smb_lucache_cmp,
1029	    sizeof (smb_ucnode_t), offsetof(smb_ucnode_t, cn_link));
1030
1031	bzero(&pwbuf, sizeof (smb_pwbuf_t));
1032	pwbuf.pw_pwd = &smbpw;
1033
1034	(void) rw_rdlock(&smb_uch.uc_cache_lck);
1035
1036	while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_NOPWD) != NULL) {
1037		uc_node.cn_user.su_name = smbpw.pw_name;
1038		uc_newnode = avl_find(&smb_uch.uc_cache, &uc_node, NULL);
1039		if (uc_newnode) {
1040			/* update the node info */
1041			uc_newnode->cn_user.su_ctrl = smbpw.pw_flags;
1042			continue;
1043		}
1044
1045		/* create a new node */
1046		if ((uc_newnode = malloc(sizeof (smb_ucnode_t))) == NULL) {
1047			rc = SMB_PWE_NO_MEMORY;
1048			break;
1049		}
1050
1051		bzero(uc_newnode, sizeof (smb_ucnode_t));
1052		user = &uc_newnode->cn_user;
1053		user->su_ctrl = smbpw.pw_flags;
1054
1055		idm_stat = smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, &sid);
1056		if (idm_stat != IDMAP_SUCCESS) {
1057			syslog(LOG_WARNING, "smb_pwdutil: couldn't obtain SID "
1058			    "for uid=%u (%d)", smbpw.pw_uid, idm_stat);
1059			free(uc_newnode);
1060			continue;
1061		}
1062		(void) smb_sid_getrid(sid, &user->su_rid);
1063		smb_sid_free(sid);
1064
1065		user->su_name = strdup(smbpw.pw_name);
1066		if (user->su_name == NULL) {
1067			rc = SMB_PWE_NO_MEMORY;
1068			free(uc_newnode);
1069			break;
1070		}
1071
1072		avl_add(&tmp_cache, uc_newnode);
1073	}
1074
1075	(void) rw_unlock(&smb_uch.uc_cache_lck);
1076	(void) fclose(fp);
1077	(void) smb_pwd_unlock();
1078
1079	/* Destroy the temporary list */
1080	(void) rw_wrlock(&smb_uch.uc_cache_lck);
1081	while ((uc_newnode = avl_destroy_nodes(&tmp_cache, &cookie)) != NULL) {
1082		avl_add(&smb_uch.uc_cache, uc_newnode);
1083	}
1084	(void) rw_unlock(&smb_uch.uc_cache_lck);
1085
1086	avl_destroy(&tmp_cache);
1087
1088	return (rc);
1089}
1090
1091/*
1092 * smb_lucache_create
1093 *
1094 * Creates the AVL tree and initializes the global user cache handle.
1095 * This function doesn't populate the cache.
1096 * User cache is only created by smbd at startup
1097 */
1098static void
1099smb_lucache_create(void)
1100{
1101	(void) mutex_lock(&smb_uch.uc_mtx);
1102	if (smb_uch.uc_state != SMB_UCHS_NOCACHE) {
1103		(void) mutex_unlock(&smb_uch.uc_mtx);
1104		return;
1105	}
1106
1107	avl_create(&smb_uch.uc_cache, smb_lucache_cmp,
1108	    sizeof (smb_ucnode_t), offsetof(smb_ucnode_t, cn_link));
1109
1110	smb_uch.uc_state = SMB_UCHS_CREATED;
1111	bzero(&smb_uch.uc_timestamp, sizeof (timestruc_t));
1112	smb_uch.uc_refcnt = 0;
1113	(void) mutex_unlock(&smb_uch.uc_mtx);
1114}
1115
1116/*
1117 * smb_lucache_flush
1118 *
1119 * Removes and frees all the cache entries
1120 */
1121static void
1122smb_lucache_flush(void)
1123{
1124	void *cookie = NULL;
1125	smb_ucnode_t *ucnode;
1126
1127	(void) rw_wrlock(&smb_uch.uc_cache_lck);
1128	while ((ucnode = avl_destroy_nodes(&smb_uch.uc_cache, &cookie))
1129	    != NULL) {
1130		free(ucnode->cn_user.su_name);
1131		free(ucnode->cn_user.su_fullname);
1132		free(ucnode->cn_user.su_desc);
1133		free(ucnode);
1134	}
1135	(void) rw_unlock(&smb_uch.uc_cache_lck);
1136}
1137
1138/*
1139 * smb_lucache_destroy
1140 *
1141 * Destroys the cache.
1142 * This function is only called in smb_pwd_fini()
1143 * User cache is only destroyed by smbd upon shutdown
1144 */
1145static void
1146smb_lucache_destroy(void)
1147{
1148	(void) mutex_lock(&smb_uch.uc_mtx);
1149	switch (smb_uch.uc_state) {
1150	case SMB_UCHS_NOCACHE:
1151	case SMB_UCHS_DESTROYING:
1152		(void) mutex_unlock(&smb_uch.uc_mtx);
1153		return;
1154
1155	default:
1156		break;
1157	}
1158
1159	smb_uch.uc_state = SMB_UCHS_DESTROYING;
1160
1161	while (smb_uch.uc_refcnt > 0)
1162		(void) cond_wait(&smb_uch.uc_cv, &smb_uch.uc_mtx);
1163
1164	smb_lucache_flush();
1165
1166	avl_destroy(&smb_uch.uc_cache);
1167	smb_uch.uc_state = SMB_UCHS_NOCACHE;
1168	(void) mutex_unlock(&smb_uch.uc_mtx);
1169}
1170
1171/*
1172 * smb_lucache_lock
1173 *
1174 * Locks the user cache for reading and also
1175 * increment the handle reference count.
1176 */
1177static int
1178smb_lucache_lock(void)
1179{
1180	(void) mutex_lock(&smb_uch.uc_mtx);
1181	switch (smb_uch.uc_state) {
1182	case SMB_UCHS_NOCACHE:
1183		assert(0);
1184		(void) mutex_unlock(&smb_uch.uc_mtx);
1185		return (SMB_PWE_DENIED);
1186
1187	case SMB_UCHS_DESTROYING:
1188		(void) mutex_unlock(&smb_uch.uc_mtx);
1189		return (SMB_PWE_DENIED);
1190	}
1191	smb_uch.uc_refcnt++;
1192	(void) mutex_unlock(&smb_uch.uc_mtx);
1193
1194	(void) rw_rdlock(&smb_uch.uc_cache_lck);
1195	return (SMB_PWE_SUCCESS);
1196}
1197
1198/*
1199 * smb_lucache_unlock
1200 *
1201 * Unlock the cache
1202 */
1203static void
1204smb_lucache_unlock(void)
1205{
1206	(void) rw_unlock(&smb_uch.uc_cache_lck);
1207
1208	(void) mutex_lock(&smb_uch.uc_mtx);
1209	smb_uch.uc_refcnt--;
1210	(void) cond_broadcast(&smb_uch.uc_cv);
1211	(void) mutex_unlock(&smb_uch.uc_mtx);
1212}
1213
1214/*
1215 * smb_lucache_num
1216 *
1217 * Returns the number of cache entries
1218 */
1219static int
1220smb_lucache_num(void)
1221{
1222	int num;
1223
1224	(void) mutex_lock(&smb_uch.uc_mtx);
1225	switch (smb_uch.uc_state) {
1226	case SMB_UCHS_NOCACHE:
1227		assert(0);
1228		(void) mutex_unlock(&smb_uch.uc_mtx);
1229		return (0);
1230
1231	case SMB_UCHS_DESTROYING:
1232		(void) mutex_unlock(&smb_uch.uc_mtx);
1233		return (0);
1234	}
1235	(void) mutex_unlock(&smb_uch.uc_mtx);
1236
1237	(void) rw_rdlock(&smb_uch.uc_cache_lck);
1238	num = (int)avl_numnodes(&smb_uch.uc_cache);
1239	(void) rw_unlock(&smb_uch.uc_cache_lck);
1240
1241	return (num);
1242}
1243