xref: /titanic_44/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c (revision 9fb67ea305c66b6a297583b9b0db6796b0dfe497)
1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
2296a62adaSjoyce mcintosh  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23da6c28aaSamw  * Use is subject to license terms.
24da6c28aaSamw  */
25da6c28aaSamw 
26*9fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States #include <syslog.h>
27da6c28aaSamw #include <stdlib.h>
28da6c28aaSamw #include <unistd.h>
29da6c28aaSamw #include <limits.h>
30da6c28aaSamw #include <strings.h>
31da6c28aaSamw #include <synch.h>
32da6c28aaSamw #include <errno.h>
33da6c28aaSamw #include <sys/types.h>
34da6c28aaSamw #include <sys/stat.h>
353db3f65cSamw #include <sys/avl.h>
36da6c28aaSamw #include <fcntl.h>
37da6c28aaSamw #include <thread.h>
38da6c28aaSamw #include <pwd.h>
397b59d02dSjb150015 #include <dlfcn.h>
407b59d02dSjb150015 #include <link.h>
413db3f65cSamw #include <assert.h>
42da6c28aaSamw #include <smbsrv/libsmb.h>
43da6c28aaSamw 
44da6c28aaSamw #define	SMB_PASSWD	"/var/smb/smbpasswd"
45da6c28aaSamw #define	SMB_OPASSWD	"/var/smb/osmbpasswd"
46da6c28aaSamw #define	SMB_PASSTEMP	"/var/smb/ptmp"
47da6c28aaSamw #define	SMB_PASSLCK	"/var/smb/.pwd.lock"
483db3f65cSamw 
49da6c28aaSamw #define	SMB_PWD_DISABLE	"*DIS*"
50da6c28aaSamw #define	SMB_PWD_BUFSIZE 256
51da6c28aaSamw 
52da6c28aaSamw #define	S_WAITTIME	15
53da6c28aaSamw 
54da6c28aaSamw typedef enum {
55da6c28aaSamw 	SMB_PWD_NAME = 0,
56da6c28aaSamw 	SMB_PWD_UID,
57da6c28aaSamw 	SMB_PWD_LMHASH,
58da6c28aaSamw 	SMB_PWD_NTHASH,
59da6c28aaSamw 	SMB_PWD_NARG
60da6c28aaSamw } smb_pwdarg_t;
61da6c28aaSamw 
623db3f65cSamw static struct flock flock = { 0, 0, 0, 0, 0, 0 };
63da6c28aaSamw static pid_t lck_pid = 0;	/* process's pid at last lock */
64da6c28aaSamw static thread_t lck_tid = 0;	/* thread that holds the lock */
65da6c28aaSamw static int fildes = -1;
66da6c28aaSamw static mutex_t lck_lock = DEFAULTMUTEX;
677b59d02dSjb150015 static void *smb_pwd_hdl = NULL;
68da6c28aaSamw 
697b59d02dSjb150015 static struct {
7089dc44ceSjose borrego 	smb_passwd_t *(*pwop_getpwnam)(const char *, smb_passwd_t *);
7189dc44ceSjose borrego 	smb_passwd_t *(*pwop_getpwuid)(uid_t, smb_passwd_t *);
723db3f65cSamw 	int (*pwop_setcntl)(const char *, int);
733db3f65cSamw 	int (*pwop_setpasswd)(const char *, const char *);
743db3f65cSamw 	int (*pwop_num)(void);
753db3f65cSamw 	int (*pwop_iteropen)(smb_pwditer_t *);
763db3f65cSamw 	smb_luser_t *(*pwop_iterate)(smb_pwditer_t *);
773db3f65cSamw 	void (*pwop_iterclose)(smb_pwditer_t *);
787b59d02dSjb150015 } smb_pwd_ops;
797b59d02dSjb150015 
80da6c28aaSamw static int smb_pwd_lock(void);
81da6c28aaSamw static int smb_pwd_unlock(void);
82da6c28aaSamw static int smb_pwd_flck(void);
83da6c28aaSamw static int smb_pwd_fulck(void);
84da6c28aaSamw 
853db3f65cSamw /*
863db3f65cSamw  * buffer structure used by smb_pwd_fgetent/smb_pwd_fputent
873db3f65cSamw  */
883db3f65cSamw typedef struct smb_pwbuf {
893db3f65cSamw 	char		pw_buf[SMB_PWD_BUFSIZE];
903db3f65cSamw 	smb_passwd_t	*pw_pwd;
913db3f65cSamw } smb_pwbuf_t;
923db3f65cSamw 
933db3f65cSamw /*
943db3f65cSamw  * flag values used with smb_pwd_fgetent
953db3f65cSamw  */
963db3f65cSamw #define	SMB_PWD_GETF_ALL	1	/* get all the account info */
973db3f65cSamw #define	SMB_PWD_GETF_NOPWD	2	/* password is not needed */
983db3f65cSamw 
993db3f65cSamw static smb_pwbuf_t *smb_pwd_fgetent(FILE *, smb_pwbuf_t *, uint32_t);
10029bd2886SAlan Wright static int smb_pwd_fputent(FILE *, const smb_pwbuf_t *);
101da6c28aaSamw static int smb_pwd_chgpwent(smb_passwd_t *, const char *, int);
102da6c28aaSamw static int smb_pwd_update(const char *, const char *, int);
103da6c28aaSamw 
1043db3f65cSamw /*
1053db3f65cSamw  * Local Users Cache
1063db3f65cSamw  *
1073db3f65cSamw  * Simplifying assumptions
1083db3f65cSamw  *
1093db3f65cSamw  * 	o smbpasswd is a service private file and shouldn't be edited manually
1103db3f65cSamw  * 	o accounts are only added/modified via passwd and/or smbadm CLIs
1113db3f65cSamw  * 	o accounts are not removed but disabled using smbadm CLI
1123db3f65cSamw  * 	o editing smbpasswd manually might result in cache inconsistency
1133db3f65cSamw  *
1143db3f65cSamw  * Cache is created and populated upon service startup.
1153db3f65cSamw  * Cache is updated each time users list is requested if there's been
1163db3f65cSamw  * any change in smbpasswd file. The change criteria is smbpasswd's
1173db3f65cSamw  * modification timestamp.
1183db3f65cSamw  */
1197b59d02dSjb150015 
1203db3f65cSamw /*
1213db3f65cSamw  * User cache handle
1223db3f65cSamw  */
1233db3f65cSamw typedef struct smb_uchandle {
1243db3f65cSamw 	avl_tree_t	uc_cache;
1253db3f65cSamw 	rwlock_t	uc_cache_lck;
1263db3f65cSamw 	timestruc_t	uc_timestamp;
1273db3f65cSamw 	uint32_t	uc_refcnt;
1283db3f65cSamw 	uint32_t	uc_state;
1293db3f65cSamw 	mutex_t		uc_mtx;
1303db3f65cSamw 	cond_t		uc_cv;
1313db3f65cSamw } smb_uchandle_t;
1323db3f65cSamw 
1333db3f65cSamw #define	SMB_UCHS_NOCACHE	0
1343db3f65cSamw #define	SMB_UCHS_CREATED	1
1353db3f65cSamw #define	SMB_UCHS_UPDATING	2
1363db3f65cSamw #define	SMB_UCHS_UPDATED	3
1373db3f65cSamw #define	SMB_UCHS_DESTROYING	4
1383db3f65cSamw 
1393db3f65cSamw /*
1403db3f65cSamw  * User cache node
1413db3f65cSamw  */
1423db3f65cSamw typedef struct smb_ucnode {
1433db3f65cSamw 	smb_luser_t	cn_user;
1443db3f65cSamw 	avl_node_t	cn_link;
1453db3f65cSamw } smb_ucnode_t;
1463db3f65cSamw 
1473db3f65cSamw static void smb_lucache_create(void);
1483db3f65cSamw static void smb_lucache_destroy(void);
1493db3f65cSamw static void smb_lucache_update(void);
1503db3f65cSamw static int smb_lucache_num(void);
1513db3f65cSamw static int smb_lucache_lock(void);
1523db3f65cSamw static void smb_lucache_unlock(void);
1533db3f65cSamw static int smb_lucache_do_update(void);
1543db3f65cSamw static void smb_lucache_flush(void);
1553db3f65cSamw 
1563db3f65cSamw static smb_uchandle_t smb_uch;
1573db3f65cSamw 
1583db3f65cSamw /*
1593db3f65cSamw  * smb_pwd_init
1603db3f65cSamw  *
1613db3f65cSamw  * Initializes the cache if requested.
1623db3f65cSamw  * Checks to see if a password management utility library
1633db3f65cSamw  * is interposed. If yes then it'll initializes smb_pwd_ops
1643db3f65cSamw  * structure with function pointers from this library.
1653db3f65cSamw  */
1663db3f65cSamw void
smb_pwd_init(boolean_t create_cache)1673db3f65cSamw smb_pwd_init(boolean_t create_cache)
1683db3f65cSamw {
1693db3f65cSamw 	if (create_cache) {
1703db3f65cSamw 		smb_lucache_create();
171fe1c642dSBill Krier #if 0
172fe1c642dSBill Krier 		/*
173fe1c642dSBill Krier 		 * This pre-loading of the cache results in idmapd requests.
174fe1c642dSBill Krier 		 * With the change to allow idmapd to call into libsmb to
175fe1c642dSBill Krier 		 * map names and SIDs, this creates a circular startup
176fe1c642dSBill Krier 		 * dependency.  This call has been temporarily disabled to
177fe1c642dSBill Krier 		 * avoid this issue.  It can be enabled when the name/SID
178fe1c642dSBill Krier 		 * lookup can be done directly on the LSA service.
179fe1c642dSBill Krier 		 */
1803db3f65cSamw 		smb_lucache_update();
181fe1c642dSBill Krier #endif
1823db3f65cSamw 	}
1833db3f65cSamw 
1841fcced4cSJordan Brown 	smb_pwd_hdl = smb_dlopen();
1851fcced4cSJordan Brown 	if (smb_pwd_hdl == NULL)
1863db3f65cSamw 		return;
1877b59d02dSjb150015 
1887b59d02dSjb150015 	bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
1897b59d02dSjb150015 
19089dc44ceSjose borrego 	smb_pwd_ops.pwop_getpwnam =
19189dc44ceSjose borrego 	    (smb_passwd_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_getpwnam");
19289dc44ceSjose borrego 
19389dc44ceSjose borrego 	smb_pwd_ops.pwop_getpwuid =
19489dc44ceSjose borrego 	    (smb_passwd_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_getpwuid");
1957b59d02dSjb150015 
1963db3f65cSamw 	smb_pwd_ops.pwop_setcntl =
1977b59d02dSjb150015 	    (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_setcntl");
1987b59d02dSjb150015 
1993db3f65cSamw 	smb_pwd_ops.pwop_setpasswd =
2007b59d02dSjb150015 	    (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_setpasswd");
2017b59d02dSjb150015 
2023db3f65cSamw 	smb_pwd_ops.pwop_num =
2033db3f65cSamw 	    (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_num");
2043db3f65cSamw 
2053db3f65cSamw 	smb_pwd_ops.pwop_iteropen =
2063db3f65cSamw 	    (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_iteropen");
2073db3f65cSamw 
2083db3f65cSamw 	smb_pwd_ops.pwop_iterclose =
2093db3f65cSamw 	    (void (*)())dlsym(smb_pwd_hdl, "smb_pwd_iterclose");
2103db3f65cSamw 
2113db3f65cSamw 	smb_pwd_ops.pwop_iterate =
2123db3f65cSamw 	    (smb_luser_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_iterate");
2133db3f65cSamw 
21489dc44ceSjose borrego 	if (smb_pwd_ops.pwop_getpwnam == NULL ||
21589dc44ceSjose borrego 	    smb_pwd_ops.pwop_getpwuid == NULL ||
2163db3f65cSamw 	    smb_pwd_ops.pwop_setcntl == NULL ||
2173db3f65cSamw 	    smb_pwd_ops.pwop_setpasswd == NULL ||
2183db3f65cSamw 	    smb_pwd_ops.pwop_num == NULL ||
2193db3f65cSamw 	    smb_pwd_ops.pwop_iteropen == NULL ||
2203db3f65cSamw 	    smb_pwd_ops.pwop_iterclose == NULL ||
2213db3f65cSamw 	    smb_pwd_ops.pwop_iterate == NULL) {
2221fcced4cSJordan Brown 		smb_dlclose(smb_pwd_hdl);
2237b59d02dSjb150015 		smb_pwd_hdl = NULL;
2247b59d02dSjb150015 
2257b59d02dSjb150015 		/* If error or function(s) are missing, use original lib */
2267b59d02dSjb150015 		bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
2277b59d02dSjb150015 	}
2287b59d02dSjb150015 }
2297b59d02dSjb150015 
2303db3f65cSamw /*
2313db3f65cSamw  * smb_pwd_fini
2323db3f65cSamw  *
2333db3f65cSamw  * Destroys the cache.
2343db3f65cSamw  * Closes interposed library.
2353db3f65cSamw  */
2367b59d02dSjb150015 void
smb_pwd_fini(void)2377b59d02dSjb150015 smb_pwd_fini(void)
2387b59d02dSjb150015 {
2393db3f65cSamw 	smb_lucache_destroy();
2401fcced4cSJordan Brown 	smb_dlclose(smb_pwd_hdl);
2417b59d02dSjb150015 	smb_pwd_hdl = NULL;
2421fcced4cSJordan Brown 	bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
2437b59d02dSjb150015 }
2447b59d02dSjb150015 
245da6c28aaSamw /*
24689dc44ceSjose borrego  * smb_pwd_getpwnam
247da6c28aaSamw  *
248da6c28aaSamw  * Returns a smb password structure for the given user name.
249da6c28aaSamw  * smbpw is a pointer to a buffer allocated by the caller.
250da6c28aaSamw  *
251da6c28aaSamw  * Returns NULL upon failure.
252da6c28aaSamw  */
253da6c28aaSamw smb_passwd_t *
smb_pwd_getpwnam(const char * name,smb_passwd_t * smbpw)25489dc44ceSjose borrego smb_pwd_getpwnam(const char *name, smb_passwd_t *smbpw)
255da6c28aaSamw {
256da6c28aaSamw 	boolean_t found = B_FALSE;
257da6c28aaSamw 	smb_pwbuf_t pwbuf;
258da6c28aaSamw 	FILE *fp;
2593db3f65cSamw 	int err;
260da6c28aaSamw 
26189dc44ceSjose borrego 	if (smb_pwd_ops.pwop_getpwnam != NULL)
26289dc44ceSjose borrego 		return (smb_pwd_ops.pwop_getpwnam(name, smbpw));
2637b59d02dSjb150015 
264da6c28aaSamw 	err = smb_pwd_lock();
265da6c28aaSamw 	if (err != SMB_PWE_SUCCESS)
266da6c28aaSamw 		return (NULL);
267da6c28aaSamw 
268da6c28aaSamw 	if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
269da6c28aaSamw 		(void) smb_pwd_unlock();
270da6c28aaSamw 		return (NULL);
271da6c28aaSamw 	}
272da6c28aaSamw 
273da6c28aaSamw 	pwbuf.pw_pwd = smbpw;
274da6c28aaSamw 
2753db3f65cSamw 	while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
27689dc44ceSjose borrego 		if (strcmp(name, smbpw->pw_name) == 0) {
27789dc44ceSjose borrego 			if ((smbpw->pw_flags & (SMB_PWF_LM | SMB_PWF_NT)))
27889dc44ceSjose borrego 				found = B_TRUE;
27989dc44ceSjose borrego 			break;
28089dc44ceSjose borrego 		}
28189dc44ceSjose borrego 	}
28289dc44ceSjose borrego 
28389dc44ceSjose borrego 	(void) fclose(fp);
28489dc44ceSjose borrego 	(void) smb_pwd_unlock();
28589dc44ceSjose borrego 
28689dc44ceSjose borrego 	if (!found) {
28789dc44ceSjose borrego 		bzero(smbpw, sizeof (smb_passwd_t));
28889dc44ceSjose borrego 		return (NULL);
28989dc44ceSjose borrego 	}
29089dc44ceSjose borrego 
29189dc44ceSjose borrego 	return (smbpw);
29289dc44ceSjose borrego }
29389dc44ceSjose borrego 
29489dc44ceSjose borrego /*
29589dc44ceSjose borrego  * smb_pwd_getpwuid
29689dc44ceSjose borrego  *
29789dc44ceSjose borrego  * Returns a smb password structure for the given UID
29889dc44ceSjose borrego  * smbpw is a pointer to a buffer allocated by the caller.
29989dc44ceSjose borrego  *
30089dc44ceSjose borrego  * Returns NULL upon failure.
30189dc44ceSjose borrego  */
30289dc44ceSjose borrego smb_passwd_t *
smb_pwd_getpwuid(uid_t uid,smb_passwd_t * smbpw)30389dc44ceSjose borrego smb_pwd_getpwuid(uid_t uid, smb_passwd_t *smbpw)
30489dc44ceSjose borrego {
30589dc44ceSjose borrego 	boolean_t found = B_FALSE;
30689dc44ceSjose borrego 	smb_pwbuf_t pwbuf;
30789dc44ceSjose borrego 	FILE *fp;
30889dc44ceSjose borrego 	int err;
30989dc44ceSjose borrego 
31089dc44ceSjose borrego 	if (smb_pwd_ops.pwop_getpwuid != NULL)
31189dc44ceSjose borrego 		return (smb_pwd_ops.pwop_getpwuid(uid, smbpw));
31289dc44ceSjose borrego 
31389dc44ceSjose borrego 	err = smb_pwd_lock();
31489dc44ceSjose borrego 	if (err != SMB_PWE_SUCCESS)
31589dc44ceSjose borrego 		return (NULL);
31689dc44ceSjose borrego 
31789dc44ceSjose borrego 	if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
31889dc44ceSjose borrego 		(void) smb_pwd_unlock();
31989dc44ceSjose borrego 		return (NULL);
32089dc44ceSjose borrego 	}
32189dc44ceSjose borrego 
32289dc44ceSjose borrego 	pwbuf.pw_pwd = smbpw;
32389dc44ceSjose borrego 
32489dc44ceSjose borrego 	while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
32589dc44ceSjose borrego 		if (uid == smbpw->pw_uid) {
326da6c28aaSamw 			if ((smbpw->pw_flags & (SMB_PWF_LM | SMB_PWF_NT)))
327da6c28aaSamw 				found = B_TRUE;
328da6c28aaSamw 			break;
329da6c28aaSamw 		}
330da6c28aaSamw 	}
331da6c28aaSamw 
332da6c28aaSamw 	(void) fclose(fp);
333da6c28aaSamw 	(void) smb_pwd_unlock();
334da6c28aaSamw 
335da6c28aaSamw 	if (!found) {
336da6c28aaSamw 		bzero(smbpw, sizeof (smb_passwd_t));
337da6c28aaSamw 		return (NULL);
338da6c28aaSamw 	}
339da6c28aaSamw 
340da6c28aaSamw 	return (smbpw);
341da6c28aaSamw }
342da6c28aaSamw 
343da6c28aaSamw /*
3443db3f65cSamw  * smb_pwd_setpasswd
345da6c28aaSamw  *
346da6c28aaSamw  * Update/add the given user to the smbpasswd file.
347da6c28aaSamw  */
348da6c28aaSamw int
smb_pwd_setpasswd(const char * name,const char * password)349da6c28aaSamw smb_pwd_setpasswd(const char *name, const char *password)
350da6c28aaSamw {
3513db3f65cSamw 	if (smb_pwd_ops.pwop_setpasswd != NULL)
3523db3f65cSamw 		return (smb_pwd_ops.pwop_setpasswd(name, password));
3537b59d02dSjb150015 
354da6c28aaSamw 	return (smb_pwd_update(name, password, 0));
355da6c28aaSamw }
356da6c28aaSamw 
357da6c28aaSamw /*
358da6c28aaSamw  * smb_pwd_setcntl
359da6c28aaSamw  *
360da6c28aaSamw  * Change the account state. This can be making the account
361da6c28aaSamw  * disable/enable or removing its LM hash.
362da6c28aaSamw  */
363da6c28aaSamw int
smb_pwd_setcntl(const char * name,int control)364da6c28aaSamw smb_pwd_setcntl(const char *name, int control)
365da6c28aaSamw {
3663db3f65cSamw 	if (smb_pwd_ops.pwop_setcntl != NULL)
3673db3f65cSamw 		return (smb_pwd_ops.pwop_setcntl(name, control));
3687b59d02dSjb150015 
369da6c28aaSamw 	if (control == 0)
370da6c28aaSamw 		return (SMB_PWE_SUCCESS);
371da6c28aaSamw 
372da6c28aaSamw 	return (smb_pwd_update(name, NULL, control));
373da6c28aaSamw }
374da6c28aaSamw 
3753db3f65cSamw /*
3763db3f65cSamw  * smb_pwd_num
3773db3f65cSamw  *
3783db3f65cSamw  * Returns the number of cached local users
3793db3f65cSamw  */
3803db3f65cSamw int
smb_pwd_num(void)3813db3f65cSamw smb_pwd_num(void)
3823db3f65cSamw {
3833db3f65cSamw 	if (smb_pwd_ops.pwop_num != NULL)
3843db3f65cSamw 		return (smb_pwd_ops.pwop_num());
3853db3f65cSamw 
38696a62adaSjoyce mcintosh 	smb_lucache_update();
38796a62adaSjoyce mcintosh 
3883db3f65cSamw 	return (smb_lucache_num());
3893db3f65cSamw }
3903db3f65cSamw 
3913db3f65cSamw /*
3923db3f65cSamw  * smb_pwd_iteropen
3933db3f65cSamw  *
3943db3f65cSamw  * Initalizes the given iterator handle.
3953db3f65cSamw  * This handle will be used to iterate the users cache
3963db3f65cSamw  * by the caller. The cache will be locked for read and it
3973db3f65cSamw  * will remain locked until smb_pwd_iterclose() is called.
3983db3f65cSamw  */
3993db3f65cSamw int
smb_pwd_iteropen(smb_pwditer_t * iter)4003db3f65cSamw smb_pwd_iteropen(smb_pwditer_t *iter)
4013db3f65cSamw {
4023db3f65cSamw 	if (iter == NULL)
4033db3f65cSamw 		return (SMB_PWE_INVALID_PARAM);
4043db3f65cSamw 
4053db3f65cSamw 	if (smb_pwd_ops.pwop_iteropen != NULL)
4063db3f65cSamw 		return (smb_pwd_ops.pwop_iteropen(iter));
4073db3f65cSamw 
4083db3f65cSamw 	iter->spi_next = NULL;
4093db3f65cSamw 
4103db3f65cSamw 	smb_lucache_update();
4113db3f65cSamw 
4123db3f65cSamw 	return (smb_lucache_lock());
4133db3f65cSamw }
4143db3f65cSamw 
4153db3f65cSamw /*
4163db3f65cSamw  * smb_pwd_iterate
4173db3f65cSamw  *
4183db3f65cSamw  * Scans through users cache using the given iterator
4193db3f65cSamw  */
4203db3f65cSamw smb_luser_t *
smb_pwd_iterate(smb_pwditer_t * iter)4213db3f65cSamw smb_pwd_iterate(smb_pwditer_t *iter)
4223db3f65cSamw {
4233db3f65cSamw 	smb_ucnode_t *ucnode;
4243db3f65cSamw 
4253db3f65cSamw 	if (iter == NULL)
4263db3f65cSamw 		return (NULL);
4273db3f65cSamw 
4283db3f65cSamw 	if (smb_pwd_ops.pwop_iterate != NULL)
4293db3f65cSamw 		return (smb_pwd_ops.pwop_iterate(iter));
4303db3f65cSamw 
4313db3f65cSamw 	if (iter->spi_next == NULL)
4323db3f65cSamw 		ucnode = avl_first(&smb_uch.uc_cache);
4333db3f65cSamw 	else
4343db3f65cSamw 		ucnode = AVL_NEXT(&smb_uch.uc_cache, iter->spi_next);
4353db3f65cSamw 
4363db3f65cSamw 	if ((iter->spi_next = ucnode) != NULL)
4373db3f65cSamw 		return (&ucnode->cn_user);
4383db3f65cSamw 
4393db3f65cSamw 	return (NULL);
4403db3f65cSamw }
4413db3f65cSamw 
4423db3f65cSamw /*
4433db3f65cSamw  * smb_pwd_iterclose
4443db3f65cSamw  *
4453db3f65cSamw  * Closes the given iterator. Effectively it only unlocks the cache
4463db3f65cSamw  */
4473db3f65cSamw void
smb_pwd_iterclose(smb_pwditer_t * iter)4483db3f65cSamw smb_pwd_iterclose(smb_pwditer_t *iter)
4493db3f65cSamw {
4503db3f65cSamw 	if (smb_pwd_ops.pwop_iterclose != NULL) {
4513db3f65cSamw 		smb_pwd_ops.pwop_iterclose(iter);
4523db3f65cSamw 		return;
4533db3f65cSamw 	}
4543db3f65cSamw 
4553db3f65cSamw 	if (iter != NULL)
4563db3f65cSamw 		smb_lucache_unlock();
4573db3f65cSamw }
4583db3f65cSamw 
4593db3f65cSamw /*
4603db3f65cSamw  * smb_pwd_update
4613db3f65cSamw  *
4623db3f65cSamw  * Updates the password entry of the given user if the user already
4633db3f65cSamw  * has an entry, otherwise it'll add an entry for the user with
4643db3f65cSamw  * given password and control information.
4653db3f65cSamw  */
466da6c28aaSamw static int
smb_pwd_update(const char * name,const char * password,int control)467da6c28aaSamw smb_pwd_update(const char *name, const char *password, int control)
468da6c28aaSamw {
469da6c28aaSamw 	struct stat64 stbuf;
470da6c28aaSamw 	FILE *src, *dst;
471da6c28aaSamw 	int tempfd;
472da6c28aaSamw 	int err = SMB_PWE_SUCCESS;
473da6c28aaSamw 	smb_pwbuf_t pwbuf;
474da6c28aaSamw 	smb_passwd_t smbpw;
475da6c28aaSamw 	boolean_t newent = B_TRUE;
476da6c28aaSamw 	boolean_t user_disable = B_FALSE;
477da6c28aaSamw 	char uxbuf[1024];
478da6c28aaSamw 	struct passwd uxpw;
479dc20a302Sas200622 	int64_t lm_level;
480da6c28aaSamw 
481da6c28aaSamw 	err = smb_pwd_lock();
482da6c28aaSamw 	if (err != SMB_PWE_SUCCESS)
483da6c28aaSamw 		return (err);
484da6c28aaSamw 
485da6c28aaSamw 	if (stat64(SMB_PASSWD, &stbuf) < 0) {
486da6c28aaSamw 		err = SMB_PWE_STAT_FAILED;
487da6c28aaSamw 		goto passwd_exit;
488da6c28aaSamw 	}
489da6c28aaSamw 
490da6c28aaSamw 	if ((tempfd = open(SMB_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
491da6c28aaSamw 		err = SMB_PWE_OPEN_FAILED;
492da6c28aaSamw 		goto passwd_exit;
493da6c28aaSamw 	}
494da6c28aaSamw 
495da6c28aaSamw 	if ((dst = fdopen(tempfd, "wF")) == NULL) {
496da6c28aaSamw 		err = SMB_PWE_OPEN_FAILED;
497da6c28aaSamw 		goto passwd_exit;
498da6c28aaSamw 	}
499da6c28aaSamw 
500da6c28aaSamw 	if ((src = fopen(SMB_PASSWD, "rF")) == NULL) {
501da6c28aaSamw 		err = SMB_PWE_OPEN_FAILED;
502da6c28aaSamw 		(void) fclose(dst);
503da6c28aaSamw 		(void) unlink(SMB_PASSTEMP);
504da6c28aaSamw 		goto passwd_exit;
505da6c28aaSamw 	}
506da6c28aaSamw 
507dc20a302Sas200622 	if (smb_config_getnum(SMB_CI_LM_LEVEL, &lm_level) != SMBD_SMF_OK)
508da6c28aaSamw 		lm_level = 4;
509da6c28aaSamw 
510da6c28aaSamw 	if (lm_level >= 4)
511da6c28aaSamw 		control |= SMB_PWC_NOLM;
512da6c28aaSamw 
5133db3f65cSamw 	pwbuf.pw_pwd = &smbpw;
5143db3f65cSamw 
515da6c28aaSamw 	/*
516da6c28aaSamw 	 * copy old password entries to temporary file while replacing
517da6c28aaSamw 	 * the entry that matches "name"
518da6c28aaSamw 	 */
5193db3f65cSamw 	while (smb_pwd_fgetent(src, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
52089dc44ceSjose borrego 		if (strcmp(smbpw.pw_name, name) == 0) {
521da6c28aaSamw 			err = smb_pwd_chgpwent(&smbpw, password, control);
522da6c28aaSamw 			if (err == SMB_PWE_USER_DISABLE)
523da6c28aaSamw 				user_disable = B_TRUE;
524da6c28aaSamw 			err = smb_pwd_fputent(dst, &pwbuf);
525da6c28aaSamw 			newent = B_FALSE;
526da6c28aaSamw 		} else {
527da6c28aaSamw 			err = smb_pwd_fputent(dst, &pwbuf);
528da6c28aaSamw 		}
529da6c28aaSamw 
530da6c28aaSamw 		if (err != SMB_PWE_SUCCESS) {
531da6c28aaSamw 			(void) fclose(src);
532da6c28aaSamw 			(void) fclose(dst);
533da6c28aaSamw 			goto passwd_exit;
534da6c28aaSamw 		}
535da6c28aaSamw 	}
536da6c28aaSamw 
537da6c28aaSamw 	if (newent) {
538da6c28aaSamw 		if (getpwnam_r(name, &uxpw, uxbuf, sizeof (uxbuf))) {
5393db3f65cSamw 			bzero(&smbpw, sizeof (smb_passwd_t));
54089dc44ceSjose borrego 			(void) strlcpy(smbpw.pw_name, uxpw.pw_name,
54189dc44ceSjose borrego 			    sizeof (smbpw.pw_name));
542da6c28aaSamw 			smbpw.pw_uid = uxpw.pw_uid;
543da6c28aaSamw 			(void) smb_pwd_chgpwent(&smbpw, password, control);
544da6c28aaSamw 			err = smb_pwd_fputent(dst, &pwbuf);
545da6c28aaSamw 		} else {
546da6c28aaSamw 			err = SMB_PWE_USER_UNKNOWN;
547da6c28aaSamw 		}
548da6c28aaSamw 
549da6c28aaSamw 		if (err != SMB_PWE_SUCCESS) {
550da6c28aaSamw 			(void) fclose(src);
551da6c28aaSamw 			(void) fclose(dst);
552da6c28aaSamw 			goto passwd_exit;
553da6c28aaSamw 		}
554da6c28aaSamw 	}
555da6c28aaSamw 
556da6c28aaSamw 	(void) fclose(src);
557da6c28aaSamw 	if (fclose(dst) != 0) {
558da6c28aaSamw 		err = SMB_PWE_CLOSE_FAILED;
559da6c28aaSamw 		goto passwd_exit; /* Don't trust the temporary file */
560da6c28aaSamw 	}
561da6c28aaSamw 
562da6c28aaSamw 	/* Rename temp to passwd */
563da6c28aaSamw 	if (unlink(SMB_OPASSWD) && access(SMB_OPASSWD, 0) == 0) {
564da6c28aaSamw 		err = SMB_PWE_UPDATE_FAILED;
565da6c28aaSamw 		(void) unlink(SMB_PASSTEMP);
566da6c28aaSamw 		goto passwd_exit;
567da6c28aaSamw 	}
568da6c28aaSamw 
569da6c28aaSamw 	if (link(SMB_PASSWD, SMB_OPASSWD) == -1) {
570da6c28aaSamw 		err = SMB_PWE_UPDATE_FAILED;
571da6c28aaSamw 		(void) unlink(SMB_PASSTEMP);
572da6c28aaSamw 		goto passwd_exit;
573da6c28aaSamw 	}
574da6c28aaSamw 
575da6c28aaSamw 	if (rename(SMB_PASSTEMP, SMB_PASSWD) == -1) {
576da6c28aaSamw 		err = SMB_PWE_UPDATE_FAILED;
577da6c28aaSamw 		(void) unlink(SMB_PASSTEMP);
578da6c28aaSamw 		goto passwd_exit;
579da6c28aaSamw 	}
580da6c28aaSamw 
581da6c28aaSamw 	(void) chmod(SMB_PASSWD, 0400);
582da6c28aaSamw 
583da6c28aaSamw passwd_exit:
584da6c28aaSamw 	(void) smb_pwd_unlock();
585da6c28aaSamw 	if ((err == SMB_PWE_SUCCESS) && user_disable)
586da6c28aaSamw 		err = SMB_PWE_USER_DISABLE;
587da6c28aaSamw 
588da6c28aaSamw 	return (err);
589da6c28aaSamw }
590da6c28aaSamw 
591da6c28aaSamw /*
5923db3f65cSamw  * smb_pwd_fgetent
593da6c28aaSamw  *
594da6c28aaSamw  * Parse the buffer in the passed pwbuf and fill in the
595da6c28aaSamw  * smb password structure to point to the parsed information.
596da6c28aaSamw  * The entry format is:
597da6c28aaSamw  *
598da6c28aaSamw  *	<user-name>:<user-id>:<LM hash>:<NTLM hash>
599da6c28aaSamw  *
6003db3f65cSamw  * Returns a pointer to the passed pwbuf structure on success,
601da6c28aaSamw  * otherwise returns NULL.
602da6c28aaSamw  */
603da6c28aaSamw static smb_pwbuf_t *
smb_pwd_fgetent(FILE * fp,smb_pwbuf_t * pwbuf,uint32_t flags)6043db3f65cSamw smb_pwd_fgetent(FILE *fp, smb_pwbuf_t *pwbuf, uint32_t flags)
605da6c28aaSamw {
606da6c28aaSamw 	char *argv[SMB_PWD_NARG];
6073db3f65cSamw 	char *pwentry;
608da6c28aaSamw 	smb_passwd_t *pw;
609da6c28aaSamw 	smb_pwdarg_t i;
610da6c28aaSamw 	int lm_len, nt_len;
611da6c28aaSamw 
6123db3f65cSamw 	pwentry = pwbuf->pw_buf;
6133db3f65cSamw 	if (fgets(pwentry, SMB_PWD_BUFSIZE, fp) == NULL)
614da6c28aaSamw 		return (NULL);
6153db3f65cSamw 	(void) trim_whitespace(pwentry);
616da6c28aaSamw 
617da6c28aaSamw 	for (i = 0; i < SMB_PWD_NARG; ++i) {
6183db3f65cSamw 		if ((argv[i] = strsep((char **)&pwentry, ":")) == NULL)
619da6c28aaSamw 			return (NULL);
620da6c28aaSamw 	}
621da6c28aaSamw 
622da6c28aaSamw 	if ((*argv[SMB_PWD_NAME] == '\0') || (*argv[SMB_PWD_UID] == '\0'))
623da6c28aaSamw 		return (NULL);
624da6c28aaSamw 
625da6c28aaSamw 	pw = pwbuf->pw_pwd;
626da6c28aaSamw 	bzero(pw, sizeof (smb_passwd_t));
627da6c28aaSamw 	pw->pw_uid = strtoul(argv[SMB_PWD_UID], 0, 10);
62889dc44ceSjose borrego 	(void) strlcpy(pw->pw_name, argv[SMB_PWD_NAME], sizeof (pw->pw_name));
629da6c28aaSamw 
630da6c28aaSamw 	if (strcmp(argv[SMB_PWD_LMHASH], SMB_PWD_DISABLE) == 0) {
631da6c28aaSamw 		pw->pw_flags |= SMB_PWF_DISABLE;
6323db3f65cSamw 		if (flags != SMB_PWD_GETF_NOPWD) {
633da6c28aaSamw 			(void) strcpy((char *)pw->pw_lmhash, SMB_PWD_DISABLE);
634da6c28aaSamw 			(void) strcpy((char *)pw->pw_nthash, SMB_PWD_DISABLE);
6353db3f65cSamw 		}
636da6c28aaSamw 		return (pwbuf);
637da6c28aaSamw 	}
638da6c28aaSamw 
6393db3f65cSamw 	if (flags == SMB_PWD_GETF_NOPWD)
6403db3f65cSamw 		return (pwbuf);
6413db3f65cSamw 
642da6c28aaSamw 	lm_len = strlen(argv[SMB_PWD_LMHASH]);
643da6c28aaSamw 	if (lm_len == SMBAUTH_HEXHASH_SZ) {
644da6c28aaSamw 		(void) hextobin(argv[SMB_PWD_LMHASH], SMBAUTH_HEXHASH_SZ,
645da6c28aaSamw 		    (char *)pw->pw_lmhash, SMBAUTH_HASH_SZ);
646da6c28aaSamw 
647da6c28aaSamw 		pw->pw_flags |= SMB_PWF_LM;
648da6c28aaSamw 	} else if (lm_len != 0) {
649da6c28aaSamw 		return (NULL);
650da6c28aaSamw 	}
651da6c28aaSamw 
652da6c28aaSamw 	nt_len = strlen(argv[SMB_PWD_NTHASH]);
653da6c28aaSamw 	if (nt_len == SMBAUTH_HEXHASH_SZ) {
654da6c28aaSamw 		(void) hextobin(argv[SMB_PWD_NTHASH], SMBAUTH_HEXHASH_SZ,
655da6c28aaSamw 		    (char *)pw->pw_nthash, SMBAUTH_HASH_SZ);
656da6c28aaSamw 
657da6c28aaSamw 		pw->pw_flags |= SMB_PWF_NT;
658da6c28aaSamw 	} else if (nt_len != 0) {
659da6c28aaSamw 		return (NULL);
660da6c28aaSamw 	}
661da6c28aaSamw 
662da6c28aaSamw 	return (pwbuf);
663da6c28aaSamw }
664da6c28aaSamw 
6653db3f65cSamw /*
6663db3f65cSamw  * smb_pwd_chgpwent
6673db3f65cSamw  *
6683db3f65cSamw  * Updates the given smb_passwd_t structure with given password and
6693db3f65cSamw  * control information.
6703db3f65cSamw  */
671da6c28aaSamw static int
smb_pwd_chgpwent(smb_passwd_t * smbpw,const char * password,int control)672da6c28aaSamw smb_pwd_chgpwent(smb_passwd_t *smbpw, const char *password, int control)
673da6c28aaSamw {
674da6c28aaSamw 	if (control & SMB_PWC_DISABLE) {
6753db3f65cSamw 		/* disable the user */
676da6c28aaSamw 		smbpw->pw_flags |= SMB_PWF_DISABLE;
677da6c28aaSamw 		(void) strcpy((char *)smbpw->pw_lmhash, SMB_PWD_DISABLE);
678da6c28aaSamw 		(void) strcpy((char *)smbpw->pw_nthash, SMB_PWD_DISABLE);
679da6c28aaSamw 		smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
680da6c28aaSamw 		return (SMB_PWE_SUCCESS);
6813db3f65cSamw 	}
6823db3f65cSamw 
6833db3f65cSamw 	if ((control & SMB_PWC_ENABLE) && (smbpw->pw_flags & SMB_PWF_DISABLE)) {
6843db3f65cSamw 		/* enable the user if it's been disabled */
685da6c28aaSamw 		*smbpw->pw_lmhash = '\0';
686da6c28aaSamw 		*smbpw->pw_nthash = '\0';
687da6c28aaSamw 		smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
688da6c28aaSamw 		return (SMB_PWE_SUCCESS);
689da6c28aaSamw 	}
690da6c28aaSamw 
691da6c28aaSamw 	/* No password update if account is disabled */
692da6c28aaSamw 	if (smbpw->pw_flags & SMB_PWF_DISABLE)
693da6c28aaSamw 		return (SMB_PWE_USER_DISABLE);
694da6c28aaSamw 
6953db3f65cSamw 	/* This call was just to update the control flags */
6963db3f65cSamw 	if (password == NULL)
6973db3f65cSamw 		return (SMB_PWE_SUCCESS);
6983db3f65cSamw 
699da6c28aaSamw 	if (control & SMB_PWC_NOLM) {
7003db3f65cSamw 		/* LM hash should not be present */
701da6c28aaSamw 		smbpw->pw_flags &= ~SMB_PWF_LM;
702da6c28aaSamw 		*smbpw->pw_lmhash = '\0';
703da6c28aaSamw 	} else {
704da6c28aaSamw 		smbpw->pw_flags |= SMB_PWF_LM;
70529bd2886SAlan Wright 		(void) smb_auth_lm_hash(password, smbpw->pw_lmhash);
706da6c28aaSamw 	}
707da6c28aaSamw 
708da6c28aaSamw 	smbpw->pw_flags |= SMB_PWF_NT;
70929bd2886SAlan Wright 	(void) smb_auth_ntlm_hash(password, smbpw->pw_nthash);
710da6c28aaSamw 	return (SMB_PWE_SUCCESS);
711da6c28aaSamw }
712da6c28aaSamw 
713da6c28aaSamw /*
7143db3f65cSamw  * smb_pwd_fputent
715da6c28aaSamw  *
7163db3f65cSamw  * If LM/NTLM hash are present, converts them to hex string
717da6c28aaSamw  * and write them along with user's name and Id to the smbpasswd
718da6c28aaSamw  * file.
719da6c28aaSamw  */
720da6c28aaSamw static int
smb_pwd_fputent(FILE * fp,const smb_pwbuf_t * pwbuf)72129bd2886SAlan Wright smb_pwd_fputent(FILE *fp, const smb_pwbuf_t *pwbuf)
722da6c28aaSamw {
723da6c28aaSamw 	smb_passwd_t *pw = pwbuf->pw_pwd;
724da6c28aaSamw 	char hex_nthash[SMBAUTH_HEXHASH_SZ+1];
725da6c28aaSamw 	char hex_lmhash[SMBAUTH_HEXHASH_SZ+1];
726da6c28aaSamw 	int rc;
727da6c28aaSamw 
728da6c28aaSamw 	if ((pw->pw_flags & SMB_PWF_LM) == SMB_PWF_LM) {
729da6c28aaSamw 		(void) bintohex((char *)pw->pw_lmhash, SMBAUTH_HASH_SZ,
730da6c28aaSamw 		    hex_lmhash, SMBAUTH_HEXHASH_SZ);
731da6c28aaSamw 		hex_lmhash[SMBAUTH_HEXHASH_SZ] = '\0';
732da6c28aaSamw 	} else {
733da6c28aaSamw 		(void) strcpy(hex_lmhash, (char *)pw->pw_lmhash);
734da6c28aaSamw 	}
735da6c28aaSamw 
736da6c28aaSamw 	if ((pw->pw_flags & SMB_PWF_NT) == SMB_PWF_NT) {
737da6c28aaSamw 		(void) bintohex((char *)pw->pw_nthash, SMBAUTH_HASH_SZ,
738da6c28aaSamw 		    hex_nthash, SMBAUTH_HEXHASH_SZ);
739da6c28aaSamw 		hex_nthash[SMBAUTH_HEXHASH_SZ] = '\0';
740da6c28aaSamw 	} else {
741da6c28aaSamw 		(void) strcpy(hex_nthash, (char *)pw->pw_nthash);
742da6c28aaSamw 	}
743da6c28aaSamw 
74489dc44ceSjose borrego 	rc = fprintf(fp, "%s:%u:%s:%s\n", pw->pw_name, pw->pw_uid,
745da6c28aaSamw 	    hex_lmhash, hex_nthash);
746da6c28aaSamw 
747da6c28aaSamw 	if (rc <= 0)
748da6c28aaSamw 		return (SMB_PWE_WRITE_FAILED);
749da6c28aaSamw 
750da6c28aaSamw 	return (SMB_PWE_SUCCESS);
751da6c28aaSamw }
752da6c28aaSamw 
7533db3f65cSamw /*
7543db3f65cSamw  * smb_pwd_lock
7553db3f65cSamw  *
7563db3f65cSamw  * A wrapper around smb_pwd_flck() which locks smb password
7573db3f65cSamw  * file so that only one thread at a time is operational.
7583db3f65cSamw  */
759da6c28aaSamw static int
smb_pwd_lock(void)760da6c28aaSamw smb_pwd_lock(void)
761da6c28aaSamw {
762da6c28aaSamw 	int res;
763da6c28aaSamw 
764da6c28aaSamw 	if (smb_pwd_flck()) {
765da6c28aaSamw 		switch (errno) {
766da6c28aaSamw 		case EINTR:
767da6c28aaSamw 			res = SMB_PWE_BUSY;
768da6c28aaSamw 			break;
769da6c28aaSamw 		case EACCES:
770da6c28aaSamw 			res = SMB_PWE_DENIED;
771da6c28aaSamw 			break;
772da6c28aaSamw 		case 0:
773da6c28aaSamw 			res = SMB_PWE_SUCCESS;
774da6c28aaSamw 			break;
775da6c28aaSamw 		}
776da6c28aaSamw 	} else
777da6c28aaSamw 		res = SMB_PWE_SUCCESS;
778da6c28aaSamw 
779da6c28aaSamw 	return (res);
780da6c28aaSamw }
781da6c28aaSamw 
7823db3f65cSamw /*
78396a62adaSjoyce mcintosh  * smb_pwd_unlock
7843db3f65cSamw  *
7853db3f65cSamw  * A wrapper around smb_pwd_fulck() which unlocks
7863db3f65cSamw  * smb password file.
7873db3f65cSamw  */
788da6c28aaSamw static int
smb_pwd_unlock(void)789da6c28aaSamw smb_pwd_unlock(void)
790da6c28aaSamw {
791da6c28aaSamw 	if (smb_pwd_fulck())
792da6c28aaSamw 		return (SMB_PWE_SYSTEM_ERROR);
793da6c28aaSamw 
794da6c28aaSamw 	return (SMB_PWE_SUCCESS);
795da6c28aaSamw }
796da6c28aaSamw 
7973db3f65cSamw /*
7983db3f65cSamw  * smb_pwd_flck
7993db3f65cSamw  *
8003db3f65cSamw  * Creates a lock file and grabs an exclusive (write) lock on it.
8013db3f65cSamw  */
802da6c28aaSamw static int
smb_pwd_flck(void)803da6c28aaSamw smb_pwd_flck(void)
804da6c28aaSamw {
805da6c28aaSamw 	int seconds = 0;
806da6c28aaSamw 
807da6c28aaSamw 	(void) mutex_lock(&lck_lock);
808da6c28aaSamw 	for (;;) {
809da6c28aaSamw 		if (lck_pid != 0 && lck_pid != getpid()) {
810da6c28aaSamw 			/* somebody forked */
811da6c28aaSamw 			lck_pid = 0;
812da6c28aaSamw 			lck_tid = 0;
813da6c28aaSamw 		}
814da6c28aaSamw 
815da6c28aaSamw 		if (lck_tid == 0) {
816da6c28aaSamw 			if ((fildes = creat(SMB_PASSLCK, 0600)) == -1)
817da6c28aaSamw 				break;
818da6c28aaSamw 			flock.l_type = F_WRLCK;
819da6c28aaSamw 			if (fcntl(fildes, F_SETLK, &flock) != -1) {
820da6c28aaSamw 				lck_pid = getpid();
821da6c28aaSamw 				lck_tid = thr_self();
822da6c28aaSamw 				(void) mutex_unlock(&lck_lock);
823da6c28aaSamw 				return (0);
824da6c28aaSamw 			}
825da6c28aaSamw 			(void) close(fildes);
826da6c28aaSamw 			fildes = -1;
827da6c28aaSamw 		}
828da6c28aaSamw 
829da6c28aaSamw 		if (seconds++ >= S_WAITTIME) {
830da6c28aaSamw 			/*
831da6c28aaSamw 			 * For compatibility with the past, pretend
832da6c28aaSamw 			 * that we were interrupted by SIGALRM.
833da6c28aaSamw 			 */
834da6c28aaSamw 			errno = EINTR;
835da6c28aaSamw 			break;
836da6c28aaSamw 		}
837da6c28aaSamw 
838da6c28aaSamw 		(void) mutex_unlock(&lck_lock);
839da6c28aaSamw 		(void) sleep(1);
840da6c28aaSamw 		(void) mutex_lock(&lck_lock);
841da6c28aaSamw 	}
842da6c28aaSamw 	(void) mutex_unlock(&lck_lock);
843da6c28aaSamw 
844da6c28aaSamw 	return (-1);
845da6c28aaSamw }
846da6c28aaSamw 
8473db3f65cSamw /*
8483db3f65cSamw  * smb_pwd_fulck
8493db3f65cSamw  *
8503db3f65cSamw  * Unlocks smb password file for operations done via
8513db3f65cSamw  * this library APIs.
8523db3f65cSamw  */
853da6c28aaSamw static int
smb_pwd_fulck(void)854da6c28aaSamw smb_pwd_fulck(void)
855da6c28aaSamw {
856da6c28aaSamw 	(void) mutex_lock(&lck_lock);
857da6c28aaSamw 	if (lck_tid == thr_self() && fildes >= 0) {
858da6c28aaSamw 		flock.l_type = F_UNLCK;
859da6c28aaSamw 		(void) fcntl(fildes, F_SETLK, &flock);
860da6c28aaSamw 		(void) close(fildes);
861da6c28aaSamw 		fildes = -1;
862da6c28aaSamw 		lck_pid = 0;
863da6c28aaSamw 		lck_tid = 0;
864da6c28aaSamw 		(void) mutex_unlock(&lck_lock);
865da6c28aaSamw 		return (0);
866da6c28aaSamw 	}
867da6c28aaSamw 	(void) mutex_unlock(&lck_lock);
868da6c28aaSamw 	return (-1);
869da6c28aaSamw }
8703db3f65cSamw 
8713db3f65cSamw /*
8723db3f65cSamw  * Local User Cache Functions
8733db3f65cSamw  *
8743db3f65cSamw  * Local user cache is implemented using AVL tree
8753db3f65cSamw  */
8763db3f65cSamw 
8773db3f65cSamw /*
8783db3f65cSamw  * smb_lucache_cmp
8793db3f65cSamw  *
8803db3f65cSamw  * AVL compare function, the key is username.
8813db3f65cSamw  */
8823db3f65cSamw static int
smb_lucache_cmp(const void * p1,const void * p2)8833db3f65cSamw smb_lucache_cmp(const void *p1, const void *p2)
8843db3f65cSamw {
8853db3f65cSamw 	smb_ucnode_t *u1 = (smb_ucnode_t *)p1;
8863db3f65cSamw 	smb_ucnode_t *u2 = (smb_ucnode_t *)p2;
8873db3f65cSamw 	int rc;
8883db3f65cSamw 
8893db3f65cSamw 	rc = strcmp(u1->cn_user.su_name, u2->cn_user.su_name);
8903db3f65cSamw 
8913db3f65cSamw 	if (rc < 0)
8923db3f65cSamw 		return (-1);
8933db3f65cSamw 
8943db3f65cSamw 	if (rc > 0)
8953db3f65cSamw 		return (1);
8963db3f65cSamw 
8973db3f65cSamw 	return (0);
8983db3f65cSamw }
8993db3f65cSamw 
9003db3f65cSamw /*
9013db3f65cSamw  * smb_lucache_update
9023db3f65cSamw  *
9033db3f65cSamw  * Updates the cache if needed. Whether an update is needed
9043db3f65cSamw  * is determined based on smbpasswd file modification timestamp
9053db3f65cSamw  */
9063db3f65cSamw static void
smb_lucache_update(void)9073db3f65cSamw smb_lucache_update(void)
9083db3f65cSamw {
9093db3f65cSamw 	struct stat64 stbuf;
9103db3f65cSamw 	int rc;
9113db3f65cSamw 
9123db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
9133db3f65cSamw 	switch (smb_uch.uc_state) {
9143db3f65cSamw 	default:
9153db3f65cSamw 	case SMB_UCHS_NOCACHE:
9163db3f65cSamw 		assert(0);
9173db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9183db3f65cSamw 		return;
9193db3f65cSamw 
9203db3f65cSamw 	case SMB_UCHS_CREATED:
9213db3f65cSamw 	case SMB_UCHS_UPDATED:
9223db3f65cSamw 		break;
9233db3f65cSamw 
9243db3f65cSamw 	case SMB_UCHS_UPDATING:
9253db3f65cSamw 		/* Want only one thread executing this function at a time */
9263db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9273db3f65cSamw 		return;
9283db3f65cSamw 
9293db3f65cSamw 	case SMB_UCHS_DESTROYING:
9303db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9313db3f65cSamw 		return;
9323db3f65cSamw 	}
9333db3f65cSamw 
9343db3f65cSamw 	/*
9353db3f65cSamw 	 * smb_pwd_lock() is not called here so it can
9363db3f65cSamw 	 * be checked quickly whether an updated is needed
9373db3f65cSamw 	 */
9383db3f65cSamw 	if (stat64(SMB_PASSWD, &stbuf) < 0) {
9393db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9403db3f65cSamw 		if (errno != ENOENT)
9413db3f65cSamw 			return;
9423db3f65cSamw 
9433db3f65cSamw 		/* no smbpasswd file; empty the cache */
9443db3f65cSamw 		smb_lucache_flush();
9453db3f65cSamw 		return;
9463db3f65cSamw 	}
9473db3f65cSamw 
9483db3f65cSamw 	if (stbuf.st_size == 0) {
9493db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9503db3f65cSamw 
9513db3f65cSamw 		/* empty smbpasswd file; empty the cache */
9523db3f65cSamw 		smb_lucache_flush();
9533db3f65cSamw 		return;
9543db3f65cSamw 	}
9553db3f65cSamw 
9563db3f65cSamw 	if ((smb_uch.uc_timestamp.tv_sec == stbuf.st_mtim.tv_sec) &&
9573db3f65cSamw 	    (smb_uch.uc_timestamp.tv_nsec == stbuf.st_mtim.tv_nsec)) {
9583db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9593db3f65cSamw 		/* No changes since the last cache update */
9603db3f65cSamw 		return;
9613db3f65cSamw 	}
9623db3f65cSamw 
9633db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_UPDATING;
9643db3f65cSamw 	smb_uch.uc_refcnt++;
9653db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
9663db3f65cSamw 
9673db3f65cSamw 	rc = smb_lucache_do_update();
9683db3f65cSamw 
9693db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
9703db3f65cSamw 	if ((rc == SMB_PWE_SUCCESS) && (stat64(SMB_PASSWD, &stbuf) == 0))
9713db3f65cSamw 		smb_uch.uc_timestamp = stbuf.st_mtim;
9723db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_UPDATED;
9733db3f65cSamw 	smb_uch.uc_refcnt--;
9743db3f65cSamw 	(void) cond_broadcast(&smb_uch.uc_cv);
9753db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
9763db3f65cSamw }
9773db3f65cSamw 
9783db3f65cSamw /*
9793db3f65cSamw  * smb_lucache_do_update
9803db3f65cSamw  *
9813db3f65cSamw  * This function takes care of updating the AVL tree.
9823db3f65cSamw  * If an entry has been updated, it'll be modified in place.
9833db3f65cSamw  *
9843db3f65cSamw  * New entries will be added to a temporary AVL tree then
9853db3f65cSamw  * passwod file is unlocked and all the new entries will
9863db3f65cSamw  * be transferred to the main cache from the temporary tree.
9873db3f65cSamw  *
9883db3f65cSamw  * This function MUST NOT be called directly
9893db3f65cSamw  */
9903db3f65cSamw static int
smb_lucache_do_update(void)9913db3f65cSamw smb_lucache_do_update(void)
9923db3f65cSamw {
9933db3f65cSamw 	avl_tree_t tmp_cache;
9943db3f65cSamw 	smb_pwbuf_t pwbuf;
9953db3f65cSamw 	smb_passwd_t smbpw;
9963db3f65cSamw 	smb_ucnode_t uc_node;
9973db3f65cSamw 	smb_ucnode_t *uc_newnode;
9983db3f65cSamw 	smb_luser_t *user;
9993db3f65cSamw 	smb_sid_t *sid;
10003db3f65cSamw 	idmap_stat idm_stat;
10013db3f65cSamw 	int rc = SMB_PWE_SUCCESS;
10023db3f65cSamw 	void *cookie = NULL;
10033db3f65cSamw 	FILE *fp;
10043db3f65cSamw 
10053db3f65cSamw 	if ((rc = smb_pwd_lock()) != SMB_PWE_SUCCESS)
10063db3f65cSamw 		return (rc);
10073db3f65cSamw 
10083db3f65cSamw 	if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
10093db3f65cSamw 		(void) smb_pwd_unlock();
10103db3f65cSamw 		return (SMB_PWE_OPEN_FAILED);
10113db3f65cSamw 	}
10123db3f65cSamw 
10133db3f65cSamw 	avl_create(&tmp_cache, smb_lucache_cmp,
10143db3f65cSamw 	    sizeof (smb_ucnode_t), offsetof(smb_ucnode_t, cn_link));
10153db3f65cSamw 
10163db3f65cSamw 	bzero(&pwbuf, sizeof (smb_pwbuf_t));
10173db3f65cSamw 	pwbuf.pw_pwd = &smbpw;
10183db3f65cSamw 
10193db3f65cSamw 	(void) rw_rdlock(&smb_uch.uc_cache_lck);
10203db3f65cSamw 
10213db3f65cSamw 	while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_NOPWD) != NULL) {
102289dc44ceSjose borrego 		uc_node.cn_user.su_name = smbpw.pw_name;
10233db3f65cSamw 		uc_newnode = avl_find(&smb_uch.uc_cache, &uc_node, NULL);
10243db3f65cSamw 		if (uc_newnode) {
10253db3f65cSamw 			/* update the node info */
10263db3f65cSamw 			uc_newnode->cn_user.su_ctrl = smbpw.pw_flags;
10273db3f65cSamw 			continue;
10283db3f65cSamw 		}
10293db3f65cSamw 
10303db3f65cSamw 		/* create a new node */
10313db3f65cSamw 		if ((uc_newnode = malloc(sizeof (smb_ucnode_t))) == NULL) {
10323db3f65cSamw 			rc = SMB_PWE_NO_MEMORY;
10333db3f65cSamw 			break;
10343db3f65cSamw 		}
10353db3f65cSamw 
10363db3f65cSamw 		bzero(uc_newnode, sizeof (smb_ucnode_t));
10373db3f65cSamw 		user = &uc_newnode->cn_user;
10383db3f65cSamw 		user->su_ctrl = smbpw.pw_flags;
10393db3f65cSamw 
10403db3f65cSamw 		idm_stat = smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, &sid);
10413db3f65cSamw 		if (idm_stat != IDMAP_SUCCESS) {
10423db3f65cSamw 			syslog(LOG_WARNING, "smb_pwdutil: couldn't obtain SID "
10433db3f65cSamw 			    "for uid=%u (%d)", smbpw.pw_uid, idm_stat);
10443db3f65cSamw 			free(uc_newnode);
10453db3f65cSamw 			continue;
10463db3f65cSamw 		}
10473db3f65cSamw 		(void) smb_sid_getrid(sid, &user->su_rid);
10483db3f65cSamw 		smb_sid_free(sid);
10493db3f65cSamw 
105089dc44ceSjose borrego 		user->su_name = strdup(smbpw.pw_name);
10513db3f65cSamw 		if (user->su_name == NULL) {
10523db3f65cSamw 			rc = SMB_PWE_NO_MEMORY;
10533db3f65cSamw 			free(uc_newnode);
10543db3f65cSamw 			break;
10553db3f65cSamw 		}
10563db3f65cSamw 
10573db3f65cSamw 		avl_add(&tmp_cache, uc_newnode);
10583db3f65cSamw 	}
10593db3f65cSamw 
10603db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
10613db3f65cSamw 	(void) fclose(fp);
10623db3f65cSamw 	(void) smb_pwd_unlock();
10633db3f65cSamw 
10643db3f65cSamw 	/* Destroy the temporary list */
10653db3f65cSamw 	(void) rw_wrlock(&smb_uch.uc_cache_lck);
10663db3f65cSamw 	while ((uc_newnode = avl_destroy_nodes(&tmp_cache, &cookie)) != NULL) {
10673db3f65cSamw 		avl_add(&smb_uch.uc_cache, uc_newnode);
10683db3f65cSamw 	}
10693db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
10703db3f65cSamw 
10713db3f65cSamw 	avl_destroy(&tmp_cache);
10723db3f65cSamw 
10733db3f65cSamw 	return (rc);
10743db3f65cSamw }
10753db3f65cSamw 
10763db3f65cSamw /*
10773db3f65cSamw  * smb_lucache_create
10783db3f65cSamw  *
10793db3f65cSamw  * Creates the AVL tree and initializes the global user cache handle.
10803db3f65cSamw  * This function doesn't populate the cache.
10813db3f65cSamw  * User cache is only created by smbd at startup
10823db3f65cSamw  */
10833db3f65cSamw static void
smb_lucache_create(void)10843db3f65cSamw smb_lucache_create(void)
10853db3f65cSamw {
10863db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
10873db3f65cSamw 	if (smb_uch.uc_state != SMB_UCHS_NOCACHE) {
10883db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
10893db3f65cSamw 		return;
10903db3f65cSamw 	}
10913db3f65cSamw 
10923db3f65cSamw 	avl_create(&smb_uch.uc_cache, smb_lucache_cmp,
10933db3f65cSamw 	    sizeof (smb_ucnode_t), offsetof(smb_ucnode_t, cn_link));
10943db3f65cSamw 
10953db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_CREATED;
10963db3f65cSamw 	bzero(&smb_uch.uc_timestamp, sizeof (timestruc_t));
10973db3f65cSamw 	smb_uch.uc_refcnt = 0;
10983db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
10993db3f65cSamw }
11003db3f65cSamw 
11013db3f65cSamw /*
11023db3f65cSamw  * smb_lucache_flush
11033db3f65cSamw  *
11043db3f65cSamw  * Removes and frees all the cache entries
11053db3f65cSamw  */
11063db3f65cSamw static void
smb_lucache_flush(void)11073db3f65cSamw smb_lucache_flush(void)
11083db3f65cSamw {
11093db3f65cSamw 	void *cookie = NULL;
11103db3f65cSamw 	smb_ucnode_t *ucnode;
11113db3f65cSamw 
11123db3f65cSamw 	(void) rw_wrlock(&smb_uch.uc_cache_lck);
11133db3f65cSamw 	while ((ucnode = avl_destroy_nodes(&smb_uch.uc_cache, &cookie))
11143db3f65cSamw 	    != NULL) {
11153db3f65cSamw 		free(ucnode->cn_user.su_name);
11163db3f65cSamw 		free(ucnode->cn_user.su_fullname);
11173db3f65cSamw 		free(ucnode->cn_user.su_desc);
11183db3f65cSamw 		free(ucnode);
11193db3f65cSamw 	}
11203db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
11213db3f65cSamw }
11223db3f65cSamw 
11233db3f65cSamw /*
11243db3f65cSamw  * smb_lucache_destroy
11253db3f65cSamw  *
11263db3f65cSamw  * Destroys the cache.
11273db3f65cSamw  * This function is only called in smb_pwd_fini()
11283db3f65cSamw  * User cache is only destroyed by smbd upon shutdown
11293db3f65cSamw  */
11303db3f65cSamw static void
smb_lucache_destroy(void)11313db3f65cSamw smb_lucache_destroy(void)
11323db3f65cSamw {
11333db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
11343db3f65cSamw 	switch (smb_uch.uc_state) {
11353db3f65cSamw 	case SMB_UCHS_NOCACHE:
11363db3f65cSamw 	case SMB_UCHS_DESTROYING:
11373db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
11383db3f65cSamw 		return;
11393db3f65cSamw 
11403db3f65cSamw 	default:
11413db3f65cSamw 		break;
11423db3f65cSamw 	}
11433db3f65cSamw 
11443db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_DESTROYING;
11453db3f65cSamw 
11463db3f65cSamw 	while (smb_uch.uc_refcnt > 0)
11473db3f65cSamw 		(void) cond_wait(&smb_uch.uc_cv, &smb_uch.uc_mtx);
11483db3f65cSamw 
11493db3f65cSamw 	smb_lucache_flush();
11503db3f65cSamw 
11513db3f65cSamw 	avl_destroy(&smb_uch.uc_cache);
11523db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_NOCACHE;
11533db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
11543db3f65cSamw }
11553db3f65cSamw 
11563db3f65cSamw /*
11573db3f65cSamw  * smb_lucache_lock
11583db3f65cSamw  *
11593db3f65cSamw  * Locks the user cache for reading and also
11603db3f65cSamw  * increment the handle reference count.
11613db3f65cSamw  */
11623db3f65cSamw static int
smb_lucache_lock(void)11633db3f65cSamw smb_lucache_lock(void)
11643db3f65cSamw {
11653db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
11663db3f65cSamw 	switch (smb_uch.uc_state) {
11673db3f65cSamw 	case SMB_UCHS_NOCACHE:
11683db3f65cSamw 		assert(0);
11693db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
11703db3f65cSamw 		return (SMB_PWE_DENIED);
11713db3f65cSamw 
11723db3f65cSamw 	case SMB_UCHS_DESTROYING:
11733db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
11743db3f65cSamw 		return (SMB_PWE_DENIED);
11753db3f65cSamw 	}
11763db3f65cSamw 	smb_uch.uc_refcnt++;
11773db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
11783db3f65cSamw 
11793db3f65cSamw 	(void) rw_rdlock(&smb_uch.uc_cache_lck);
11803db3f65cSamw 	return (SMB_PWE_SUCCESS);
11813db3f65cSamw }
11823db3f65cSamw 
11833db3f65cSamw /*
11843db3f65cSamw  * smb_lucache_unlock
11853db3f65cSamw  *
11863db3f65cSamw  * Unlock the cache
11873db3f65cSamw  */
11883db3f65cSamw static void
smb_lucache_unlock(void)11893db3f65cSamw smb_lucache_unlock(void)
11903db3f65cSamw {
11913db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
11923db3f65cSamw 
11933db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
11943db3f65cSamw 	smb_uch.uc_refcnt--;
11953db3f65cSamw 	(void) cond_broadcast(&smb_uch.uc_cv);
11963db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
11973db3f65cSamw }
11983db3f65cSamw 
11993db3f65cSamw /*
12003db3f65cSamw  * smb_lucache_num
12013db3f65cSamw  *
12023db3f65cSamw  * Returns the number of cache entries
12033db3f65cSamw  */
12043db3f65cSamw static int
smb_lucache_num(void)12053db3f65cSamw smb_lucache_num(void)
12063db3f65cSamw {
12073db3f65cSamw 	int num;
12083db3f65cSamw 
12093db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
12103db3f65cSamw 	switch (smb_uch.uc_state) {
12113db3f65cSamw 	case SMB_UCHS_NOCACHE:
12123db3f65cSamw 		assert(0);
12133db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
12143db3f65cSamw 		return (0);
12153db3f65cSamw 
12163db3f65cSamw 	case SMB_UCHS_DESTROYING:
12173db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
12183db3f65cSamw 		return (0);
12193db3f65cSamw 	}
12203db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
12213db3f65cSamw 
12223db3f65cSamw 	(void) rw_rdlock(&smb_uch.uc_cache_lck);
12233db3f65cSamw 	num = (int)avl_numnodes(&smb_uch.uc_cache);
12243db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
12253db3f65cSamw 
12263db3f65cSamw 	return (num);
12273db3f65cSamw }
1228