xref: /titanic_53/usr/src/lib/smbsrv/libsmb/common/smb_pwdutil.c (revision 12b65585e720714b31036daaa2b30eb76014048e)
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.
24*12b65585SGordon Ross  *
25*12b65585SGordon Ross  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
26da6c28aaSamw  */
27da6c28aaSamw 
289fb67ea3Safshin salek ardakani - Sun Microsystems - Irvine United States #include <syslog.h>
29da6c28aaSamw #include <stdlib.h>
30da6c28aaSamw #include <unistd.h>
31da6c28aaSamw #include <limits.h>
32da6c28aaSamw #include <strings.h>
33da6c28aaSamw #include <synch.h>
34da6c28aaSamw #include <errno.h>
35da6c28aaSamw #include <sys/types.h>
36da6c28aaSamw #include <sys/stat.h>
373db3f65cSamw #include <sys/avl.h>
38da6c28aaSamw #include <fcntl.h>
39da6c28aaSamw #include <thread.h>
40da6c28aaSamw #include <pwd.h>
417b59d02dSjb150015 #include <dlfcn.h>
427b59d02dSjb150015 #include <link.h>
433db3f65cSamw #include <assert.h>
44da6c28aaSamw #include <smbsrv/libsmb.h>
45da6c28aaSamw 
46da6c28aaSamw #define	SMB_PASSWD	"/var/smb/smbpasswd"
47da6c28aaSamw #define	SMB_OPASSWD	"/var/smb/osmbpasswd"
48da6c28aaSamw #define	SMB_PASSTEMP	"/var/smb/ptmp"
49da6c28aaSamw #define	SMB_PASSLCK	"/var/smb/.pwd.lock"
503db3f65cSamw 
51da6c28aaSamw #define	SMB_PWD_DISABLE	"*DIS*"
52da6c28aaSamw #define	SMB_PWD_BUFSIZE 256
53da6c28aaSamw 
54da6c28aaSamw #define	S_WAITTIME	15
55da6c28aaSamw 
56da6c28aaSamw typedef enum {
57da6c28aaSamw 	SMB_PWD_NAME = 0,
58da6c28aaSamw 	SMB_PWD_UID,
59da6c28aaSamw 	SMB_PWD_LMHASH,
60da6c28aaSamw 	SMB_PWD_NTHASH,
61da6c28aaSamw 	SMB_PWD_NARG
62da6c28aaSamw } smb_pwdarg_t;
63da6c28aaSamw 
643db3f65cSamw static struct flock flock = { 0, 0, 0, 0, 0, 0 };
65da6c28aaSamw static pid_t lck_pid = 0;	/* process's pid at last lock */
66da6c28aaSamw static thread_t lck_tid = 0;	/* thread that holds the lock */
67da6c28aaSamw static int fildes = -1;
68da6c28aaSamw static mutex_t lck_lock = DEFAULTMUTEX;
697b59d02dSjb150015 static void *smb_pwd_hdl = NULL;
70da6c28aaSamw 
717b59d02dSjb150015 static struct {
7289dc44ceSjose borrego 	smb_passwd_t *(*pwop_getpwnam)(const char *, smb_passwd_t *);
7389dc44ceSjose borrego 	smb_passwd_t *(*pwop_getpwuid)(uid_t, smb_passwd_t *);
743db3f65cSamw 	int (*pwop_setcntl)(const char *, int);
753db3f65cSamw 	int (*pwop_setpasswd)(const char *, const char *);
763db3f65cSamw 	int (*pwop_num)(void);
773db3f65cSamw 	int (*pwop_iteropen)(smb_pwditer_t *);
783db3f65cSamw 	smb_luser_t *(*pwop_iterate)(smb_pwditer_t *);
793db3f65cSamw 	void (*pwop_iterclose)(smb_pwditer_t *);
807b59d02dSjb150015 } smb_pwd_ops;
817b59d02dSjb150015 
82da6c28aaSamw static int smb_pwd_lock(void);
83da6c28aaSamw static int smb_pwd_unlock(void);
84da6c28aaSamw static int smb_pwd_flck(void);
85da6c28aaSamw static int smb_pwd_fulck(void);
86da6c28aaSamw 
873db3f65cSamw /*
883db3f65cSamw  * buffer structure used by smb_pwd_fgetent/smb_pwd_fputent
893db3f65cSamw  */
903db3f65cSamw typedef struct smb_pwbuf {
913db3f65cSamw 	char		pw_buf[SMB_PWD_BUFSIZE];
923db3f65cSamw 	smb_passwd_t	*pw_pwd;
933db3f65cSamw } smb_pwbuf_t;
943db3f65cSamw 
953db3f65cSamw /*
963db3f65cSamw  * flag values used with smb_pwd_fgetent
973db3f65cSamw  */
983db3f65cSamw #define	SMB_PWD_GETF_ALL	1	/* get all the account info */
993db3f65cSamw #define	SMB_PWD_GETF_NOPWD	2	/* password is not needed */
1003db3f65cSamw 
1013db3f65cSamw static smb_pwbuf_t *smb_pwd_fgetent(FILE *, smb_pwbuf_t *, uint32_t);
10229bd2886SAlan Wright static int smb_pwd_fputent(FILE *, const smb_pwbuf_t *);
103da6c28aaSamw static int smb_pwd_chgpwent(smb_passwd_t *, const char *, int);
104da6c28aaSamw static int smb_pwd_update(const char *, const char *, int);
105da6c28aaSamw 
1063db3f65cSamw /*
1073db3f65cSamw  * Local Users Cache
1083db3f65cSamw  *
1093db3f65cSamw  * Simplifying assumptions
1103db3f65cSamw  *
1113db3f65cSamw  * 	o smbpasswd is a service private file and shouldn't be edited manually
1123db3f65cSamw  * 	o accounts are only added/modified via passwd and/or smbadm CLIs
1133db3f65cSamw  * 	o accounts are not removed but disabled using smbadm CLI
1143db3f65cSamw  * 	o editing smbpasswd manually might result in cache inconsistency
1153db3f65cSamw  *
1163db3f65cSamw  * Cache is created and populated upon service startup.
1173db3f65cSamw  * Cache is updated each time users list is requested if there's been
1183db3f65cSamw  * any change in smbpasswd file. The change criteria is smbpasswd's
1193db3f65cSamw  * modification timestamp.
1203db3f65cSamw  */
1217b59d02dSjb150015 
1223db3f65cSamw /*
1233db3f65cSamw  * User cache handle
1243db3f65cSamw  */
1253db3f65cSamw typedef struct smb_uchandle {
1263db3f65cSamw 	avl_tree_t	uc_cache;
1273db3f65cSamw 	rwlock_t	uc_cache_lck;
1283db3f65cSamw 	timestruc_t	uc_timestamp;
1293db3f65cSamw 	uint32_t	uc_refcnt;
1303db3f65cSamw 	uint32_t	uc_state;
1313db3f65cSamw 	mutex_t		uc_mtx;
1323db3f65cSamw 	cond_t		uc_cv;
1333db3f65cSamw } smb_uchandle_t;
1343db3f65cSamw 
1353db3f65cSamw #define	SMB_UCHS_NOCACHE	0
1363db3f65cSamw #define	SMB_UCHS_CREATED	1
1373db3f65cSamw #define	SMB_UCHS_UPDATING	2
1383db3f65cSamw #define	SMB_UCHS_UPDATED	3
1393db3f65cSamw #define	SMB_UCHS_DESTROYING	4
1403db3f65cSamw 
1413db3f65cSamw /*
1423db3f65cSamw  * User cache node
1433db3f65cSamw  */
1443db3f65cSamw typedef struct smb_ucnode {
1453db3f65cSamw 	smb_luser_t	cn_user;
1463db3f65cSamw 	avl_node_t	cn_link;
1473db3f65cSamw } smb_ucnode_t;
1483db3f65cSamw 
1493db3f65cSamw static void smb_lucache_create(void);
1503db3f65cSamw static void smb_lucache_destroy(void);
1513db3f65cSamw static void smb_lucache_update(void);
1523db3f65cSamw static int smb_lucache_num(void);
1533db3f65cSamw static int smb_lucache_lock(void);
1543db3f65cSamw static void smb_lucache_unlock(void);
1553db3f65cSamw static int smb_lucache_do_update(void);
1563db3f65cSamw static void smb_lucache_flush(void);
1573db3f65cSamw 
1583db3f65cSamw static smb_uchandle_t smb_uch;
1593db3f65cSamw 
1603db3f65cSamw /*
1613db3f65cSamw  * smb_pwd_init
1623db3f65cSamw  *
1633db3f65cSamw  * Initializes the cache if requested.
1643db3f65cSamw  * Checks to see if a password management utility library
1653db3f65cSamw  * is interposed. If yes then it'll initializes smb_pwd_ops
1663db3f65cSamw  * structure with function pointers from this library.
1673db3f65cSamw  */
1683db3f65cSamw void
smb_pwd_init(boolean_t create_cache)1693db3f65cSamw smb_pwd_init(boolean_t create_cache)
1703db3f65cSamw {
1713db3f65cSamw 	if (create_cache) {
1723db3f65cSamw 		smb_lucache_create();
173fe1c642dSBill Krier #if 0
174fe1c642dSBill Krier 		/*
175fe1c642dSBill Krier 		 * This pre-loading of the cache results in idmapd requests.
176fe1c642dSBill Krier 		 * With the change to allow idmapd to call into libsmb to
177fe1c642dSBill Krier 		 * map names and SIDs, this creates a circular startup
178fe1c642dSBill Krier 		 * dependency.  This call has been temporarily disabled to
179fe1c642dSBill Krier 		 * avoid this issue.  It can be enabled when the name/SID
180fe1c642dSBill Krier 		 * lookup can be done directly on the LSA service.
181fe1c642dSBill Krier 		 */
1823db3f65cSamw 		smb_lucache_update();
183fe1c642dSBill Krier #endif
1843db3f65cSamw 	}
1853db3f65cSamw 
1861fcced4cSJordan Brown 	smb_pwd_hdl = smb_dlopen();
1871fcced4cSJordan Brown 	if (smb_pwd_hdl == NULL)
1883db3f65cSamw 		return;
1897b59d02dSjb150015 
1907b59d02dSjb150015 	bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
1917b59d02dSjb150015 
19289dc44ceSjose borrego 	smb_pwd_ops.pwop_getpwnam =
19389dc44ceSjose borrego 	    (smb_passwd_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_getpwnam");
19489dc44ceSjose borrego 
19589dc44ceSjose borrego 	smb_pwd_ops.pwop_getpwuid =
19689dc44ceSjose borrego 	    (smb_passwd_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_getpwuid");
1977b59d02dSjb150015 
1983db3f65cSamw 	smb_pwd_ops.pwop_setcntl =
1997b59d02dSjb150015 	    (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_setcntl");
2007b59d02dSjb150015 
2013db3f65cSamw 	smb_pwd_ops.pwop_setpasswd =
2027b59d02dSjb150015 	    (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_setpasswd");
2037b59d02dSjb150015 
2043db3f65cSamw 	smb_pwd_ops.pwop_num =
2053db3f65cSamw 	    (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_num");
2063db3f65cSamw 
2073db3f65cSamw 	smb_pwd_ops.pwop_iteropen =
2083db3f65cSamw 	    (int (*)())dlsym(smb_pwd_hdl, "smb_pwd_iteropen");
2093db3f65cSamw 
2103db3f65cSamw 	smb_pwd_ops.pwop_iterclose =
2113db3f65cSamw 	    (void (*)())dlsym(smb_pwd_hdl, "smb_pwd_iterclose");
2123db3f65cSamw 
2133db3f65cSamw 	smb_pwd_ops.pwop_iterate =
2143db3f65cSamw 	    (smb_luser_t *(*)())dlsym(smb_pwd_hdl, "smb_pwd_iterate");
2153db3f65cSamw 
21689dc44ceSjose borrego 	if (smb_pwd_ops.pwop_getpwnam == NULL ||
21789dc44ceSjose borrego 	    smb_pwd_ops.pwop_getpwuid == NULL ||
2183db3f65cSamw 	    smb_pwd_ops.pwop_setcntl == NULL ||
2193db3f65cSamw 	    smb_pwd_ops.pwop_setpasswd == NULL ||
2203db3f65cSamw 	    smb_pwd_ops.pwop_num == NULL ||
2213db3f65cSamw 	    smb_pwd_ops.pwop_iteropen == NULL ||
2223db3f65cSamw 	    smb_pwd_ops.pwop_iterclose == NULL ||
2233db3f65cSamw 	    smb_pwd_ops.pwop_iterate == NULL) {
2241fcced4cSJordan Brown 		smb_dlclose(smb_pwd_hdl);
2257b59d02dSjb150015 		smb_pwd_hdl = NULL;
2267b59d02dSjb150015 
2277b59d02dSjb150015 		/* If error or function(s) are missing, use original lib */
2287b59d02dSjb150015 		bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
2297b59d02dSjb150015 	}
2307b59d02dSjb150015 }
2317b59d02dSjb150015 
2323db3f65cSamw /*
2333db3f65cSamw  * smb_pwd_fini
2343db3f65cSamw  *
2353db3f65cSamw  * Destroys the cache.
2363db3f65cSamw  * Closes interposed library.
2373db3f65cSamw  */
2387b59d02dSjb150015 void
smb_pwd_fini(void)2397b59d02dSjb150015 smb_pwd_fini(void)
2407b59d02dSjb150015 {
2413db3f65cSamw 	smb_lucache_destroy();
2421fcced4cSJordan Brown 	smb_dlclose(smb_pwd_hdl);
2437b59d02dSjb150015 	smb_pwd_hdl = NULL;
2441fcced4cSJordan Brown 	bzero((void *)&smb_pwd_ops, sizeof (smb_pwd_ops));
2457b59d02dSjb150015 }
2467b59d02dSjb150015 
247da6c28aaSamw /*
24889dc44ceSjose borrego  * smb_pwd_getpwnam
249da6c28aaSamw  *
250da6c28aaSamw  * Returns a smb password structure for the given user name.
251da6c28aaSamw  * smbpw is a pointer to a buffer allocated by the caller.
252da6c28aaSamw  *
253da6c28aaSamw  * Returns NULL upon failure.
254da6c28aaSamw  */
255da6c28aaSamw smb_passwd_t *
smb_pwd_getpwnam(const char * name,smb_passwd_t * smbpw)25689dc44ceSjose borrego smb_pwd_getpwnam(const char *name, smb_passwd_t *smbpw)
257da6c28aaSamw {
258da6c28aaSamw 	boolean_t found = B_FALSE;
259da6c28aaSamw 	smb_pwbuf_t pwbuf;
260da6c28aaSamw 	FILE *fp;
2613db3f65cSamw 	int err;
262da6c28aaSamw 
26389dc44ceSjose borrego 	if (smb_pwd_ops.pwop_getpwnam != NULL)
26489dc44ceSjose borrego 		return (smb_pwd_ops.pwop_getpwnam(name, smbpw));
2657b59d02dSjb150015 
266da6c28aaSamw 	err = smb_pwd_lock();
267*12b65585SGordon Ross 	if (err != SMB_PWE_SUCCESS) {
268*12b65585SGordon Ross 		syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", err);
269da6c28aaSamw 		return (NULL);
270*12b65585SGordon Ross 	}
271da6c28aaSamw 
272da6c28aaSamw 	if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
273*12b65585SGordon Ross 		syslog(LOG_WARNING, "smb_pwdutil: open failed, %m");
274da6c28aaSamw 		(void) smb_pwd_unlock();
275da6c28aaSamw 		return (NULL);
276da6c28aaSamw 	}
277da6c28aaSamw 
278da6c28aaSamw 	pwbuf.pw_pwd = smbpw;
279da6c28aaSamw 
2803db3f65cSamw 	while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
28189dc44ceSjose borrego 		if (strcmp(name, smbpw->pw_name) == 0) {
28289dc44ceSjose borrego 			found = B_TRUE;
28389dc44ceSjose borrego 			break;
28489dc44ceSjose borrego 		}
28589dc44ceSjose borrego 	}
28689dc44ceSjose borrego 
28789dc44ceSjose borrego 	(void) fclose(fp);
28889dc44ceSjose borrego 	(void) smb_pwd_unlock();
28989dc44ceSjose borrego 
29089dc44ceSjose borrego 	if (!found) {
29189dc44ceSjose borrego 		bzero(smbpw, sizeof (smb_passwd_t));
29289dc44ceSjose borrego 		return (NULL);
29389dc44ceSjose borrego 	}
29489dc44ceSjose borrego 
29589dc44ceSjose borrego 	return (smbpw);
29689dc44ceSjose borrego }
29789dc44ceSjose borrego 
29889dc44ceSjose borrego /*
29989dc44ceSjose borrego  * smb_pwd_getpwuid
30089dc44ceSjose borrego  *
30189dc44ceSjose borrego  * Returns a smb password structure for the given UID
30289dc44ceSjose borrego  * smbpw is a pointer to a buffer allocated by the caller.
30389dc44ceSjose borrego  *
30489dc44ceSjose borrego  * Returns NULL upon failure.
30589dc44ceSjose borrego  */
30689dc44ceSjose borrego smb_passwd_t *
smb_pwd_getpwuid(uid_t uid,smb_passwd_t * smbpw)30789dc44ceSjose borrego smb_pwd_getpwuid(uid_t uid, smb_passwd_t *smbpw)
30889dc44ceSjose borrego {
30989dc44ceSjose borrego 	boolean_t found = B_FALSE;
31089dc44ceSjose borrego 	smb_pwbuf_t pwbuf;
31189dc44ceSjose borrego 	FILE *fp;
31289dc44ceSjose borrego 	int err;
31389dc44ceSjose borrego 
31489dc44ceSjose borrego 	if (smb_pwd_ops.pwop_getpwuid != NULL)
31589dc44ceSjose borrego 		return (smb_pwd_ops.pwop_getpwuid(uid, smbpw));
31689dc44ceSjose borrego 
31789dc44ceSjose borrego 	err = smb_pwd_lock();
318*12b65585SGordon Ross 	if (err != SMB_PWE_SUCCESS) {
319*12b65585SGordon Ross 		syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", err);
32089dc44ceSjose borrego 		return (NULL);
321*12b65585SGordon Ross 	}
32289dc44ceSjose borrego 
32389dc44ceSjose borrego 	if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
324*12b65585SGordon Ross 		syslog(LOG_WARNING, "smb_pwdutil: open failed, %m");
32589dc44ceSjose borrego 		(void) smb_pwd_unlock();
32689dc44ceSjose borrego 		return (NULL);
32789dc44ceSjose borrego 	}
32889dc44ceSjose borrego 
32989dc44ceSjose borrego 	pwbuf.pw_pwd = smbpw;
33089dc44ceSjose borrego 
33189dc44ceSjose borrego 	while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
33289dc44ceSjose borrego 		if (uid == smbpw->pw_uid) {
333da6c28aaSamw 			found = B_TRUE;
334da6c28aaSamw 			break;
335da6c28aaSamw 		}
336da6c28aaSamw 	}
337da6c28aaSamw 
338da6c28aaSamw 	(void) fclose(fp);
339da6c28aaSamw 	(void) smb_pwd_unlock();
340da6c28aaSamw 
341da6c28aaSamw 	if (!found) {
342da6c28aaSamw 		bzero(smbpw, sizeof (smb_passwd_t));
343da6c28aaSamw 		return (NULL);
344da6c28aaSamw 	}
345da6c28aaSamw 
346da6c28aaSamw 	return (smbpw);
347da6c28aaSamw }
348da6c28aaSamw 
349da6c28aaSamw /*
3503db3f65cSamw  * smb_pwd_setpasswd
351da6c28aaSamw  *
352da6c28aaSamw  * Update/add the given user to the smbpasswd file.
353da6c28aaSamw  */
354da6c28aaSamw int
smb_pwd_setpasswd(const char * name,const char * password)355da6c28aaSamw smb_pwd_setpasswd(const char *name, const char *password)
356da6c28aaSamw {
3573db3f65cSamw 	if (smb_pwd_ops.pwop_setpasswd != NULL)
3583db3f65cSamw 		return (smb_pwd_ops.pwop_setpasswd(name, password));
3597b59d02dSjb150015 
360da6c28aaSamw 	return (smb_pwd_update(name, password, 0));
361da6c28aaSamw }
362da6c28aaSamw 
363da6c28aaSamw /*
364da6c28aaSamw  * smb_pwd_setcntl
365da6c28aaSamw  *
366da6c28aaSamw  * Change the account state. This can be making the account
367da6c28aaSamw  * disable/enable or removing its LM hash.
368da6c28aaSamw  */
369da6c28aaSamw int
smb_pwd_setcntl(const char * name,int control)370da6c28aaSamw smb_pwd_setcntl(const char *name, int control)
371da6c28aaSamw {
3723db3f65cSamw 	if (smb_pwd_ops.pwop_setcntl != NULL)
3733db3f65cSamw 		return (smb_pwd_ops.pwop_setcntl(name, control));
3747b59d02dSjb150015 
375da6c28aaSamw 	if (control == 0)
376da6c28aaSamw 		return (SMB_PWE_SUCCESS);
377da6c28aaSamw 
378da6c28aaSamw 	return (smb_pwd_update(name, NULL, control));
379da6c28aaSamw }
380da6c28aaSamw 
3813db3f65cSamw /*
3823db3f65cSamw  * smb_pwd_num
3833db3f65cSamw  *
3843db3f65cSamw  * Returns the number of cached local users
3853db3f65cSamw  */
3863db3f65cSamw int
smb_pwd_num(void)3873db3f65cSamw smb_pwd_num(void)
3883db3f65cSamw {
3893db3f65cSamw 	if (smb_pwd_ops.pwop_num != NULL)
3903db3f65cSamw 		return (smb_pwd_ops.pwop_num());
3913db3f65cSamw 
39296a62adaSjoyce mcintosh 	smb_lucache_update();
39396a62adaSjoyce mcintosh 
3943db3f65cSamw 	return (smb_lucache_num());
3953db3f65cSamw }
3963db3f65cSamw 
3973db3f65cSamw /*
3983db3f65cSamw  * smb_pwd_iteropen
3993db3f65cSamw  *
4003db3f65cSamw  * Initalizes the given iterator handle.
4013db3f65cSamw  * This handle will be used to iterate the users cache
4023db3f65cSamw  * by the caller. The cache will be locked for read and it
4033db3f65cSamw  * will remain locked until smb_pwd_iterclose() is called.
4043db3f65cSamw  */
4053db3f65cSamw int
smb_pwd_iteropen(smb_pwditer_t * iter)4063db3f65cSamw smb_pwd_iteropen(smb_pwditer_t *iter)
4073db3f65cSamw {
4083db3f65cSamw 	if (iter == NULL)
4093db3f65cSamw 		return (SMB_PWE_INVALID_PARAM);
4103db3f65cSamw 
4113db3f65cSamw 	if (smb_pwd_ops.pwop_iteropen != NULL)
4123db3f65cSamw 		return (smb_pwd_ops.pwop_iteropen(iter));
4133db3f65cSamw 
4143db3f65cSamw 	iter->spi_next = NULL;
4153db3f65cSamw 
4163db3f65cSamw 	smb_lucache_update();
4173db3f65cSamw 
4183db3f65cSamw 	return (smb_lucache_lock());
4193db3f65cSamw }
4203db3f65cSamw 
4213db3f65cSamw /*
4223db3f65cSamw  * smb_pwd_iterate
4233db3f65cSamw  *
4243db3f65cSamw  * Scans through users cache using the given iterator
4253db3f65cSamw  */
4263db3f65cSamw smb_luser_t *
smb_pwd_iterate(smb_pwditer_t * iter)4273db3f65cSamw smb_pwd_iterate(smb_pwditer_t *iter)
4283db3f65cSamw {
4293db3f65cSamw 	smb_ucnode_t *ucnode;
4303db3f65cSamw 
4313db3f65cSamw 	if (iter == NULL)
4323db3f65cSamw 		return (NULL);
4333db3f65cSamw 
4343db3f65cSamw 	if (smb_pwd_ops.pwop_iterate != NULL)
4353db3f65cSamw 		return (smb_pwd_ops.pwop_iterate(iter));
4363db3f65cSamw 
4373db3f65cSamw 	if (iter->spi_next == NULL)
4383db3f65cSamw 		ucnode = avl_first(&smb_uch.uc_cache);
4393db3f65cSamw 	else
4403db3f65cSamw 		ucnode = AVL_NEXT(&smb_uch.uc_cache, iter->spi_next);
4413db3f65cSamw 
4423db3f65cSamw 	if ((iter->spi_next = ucnode) != NULL)
4433db3f65cSamw 		return (&ucnode->cn_user);
4443db3f65cSamw 
4453db3f65cSamw 	return (NULL);
4463db3f65cSamw }
4473db3f65cSamw 
4483db3f65cSamw /*
4493db3f65cSamw  * smb_pwd_iterclose
4503db3f65cSamw  *
4513db3f65cSamw  * Closes the given iterator. Effectively it only unlocks the cache
4523db3f65cSamw  */
4533db3f65cSamw void
smb_pwd_iterclose(smb_pwditer_t * iter)4543db3f65cSamw smb_pwd_iterclose(smb_pwditer_t *iter)
4553db3f65cSamw {
4563db3f65cSamw 	if (smb_pwd_ops.pwop_iterclose != NULL) {
4573db3f65cSamw 		smb_pwd_ops.pwop_iterclose(iter);
4583db3f65cSamw 		return;
4593db3f65cSamw 	}
4603db3f65cSamw 
4613db3f65cSamw 	if (iter != NULL)
4623db3f65cSamw 		smb_lucache_unlock();
4633db3f65cSamw }
4643db3f65cSamw 
4653db3f65cSamw /*
4663db3f65cSamw  * smb_pwd_update
4673db3f65cSamw  *
4683db3f65cSamw  * Updates the password entry of the given user if the user already
4693db3f65cSamw  * has an entry, otherwise it'll add an entry for the user with
4703db3f65cSamw  * given password and control information.
4713db3f65cSamw  */
472da6c28aaSamw static int
smb_pwd_update(const char * name,const char * password,int control)473da6c28aaSamw smb_pwd_update(const char *name, const char *password, int control)
474da6c28aaSamw {
475da6c28aaSamw 	struct stat64 stbuf;
476da6c28aaSamw 	FILE *src, *dst;
477da6c28aaSamw 	int tempfd;
478da6c28aaSamw 	int err = SMB_PWE_SUCCESS;
479da6c28aaSamw 	smb_pwbuf_t pwbuf;
480da6c28aaSamw 	smb_passwd_t smbpw;
481da6c28aaSamw 	boolean_t newent = B_TRUE;
482da6c28aaSamw 	boolean_t user_disable = B_FALSE;
483da6c28aaSamw 	char uxbuf[1024];
484da6c28aaSamw 	struct passwd uxpw;
485dc20a302Sas200622 	int64_t lm_level;
486da6c28aaSamw 
487da6c28aaSamw 	err = smb_pwd_lock();
488da6c28aaSamw 	if (err != SMB_PWE_SUCCESS)
489da6c28aaSamw 		return (err);
490da6c28aaSamw 
491da6c28aaSamw 	if (stat64(SMB_PASSWD, &stbuf) < 0) {
492da6c28aaSamw 		err = SMB_PWE_STAT_FAILED;
493da6c28aaSamw 		goto passwd_exit;
494da6c28aaSamw 	}
495da6c28aaSamw 
496da6c28aaSamw 	if ((tempfd = open(SMB_PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
497da6c28aaSamw 		err = SMB_PWE_OPEN_FAILED;
498da6c28aaSamw 		goto passwd_exit;
499da6c28aaSamw 	}
500da6c28aaSamw 
501da6c28aaSamw 	if ((dst = fdopen(tempfd, "wF")) == NULL) {
502da6c28aaSamw 		err = SMB_PWE_OPEN_FAILED;
503da6c28aaSamw 		goto passwd_exit;
504da6c28aaSamw 	}
505da6c28aaSamw 
506da6c28aaSamw 	if ((src = fopen(SMB_PASSWD, "rF")) == NULL) {
507da6c28aaSamw 		err = SMB_PWE_OPEN_FAILED;
508da6c28aaSamw 		(void) fclose(dst);
509da6c28aaSamw 		(void) unlink(SMB_PASSTEMP);
510da6c28aaSamw 		goto passwd_exit;
511da6c28aaSamw 	}
512da6c28aaSamw 
513dc20a302Sas200622 	if (smb_config_getnum(SMB_CI_LM_LEVEL, &lm_level) != SMBD_SMF_OK)
514da6c28aaSamw 		lm_level = 4;
515da6c28aaSamw 
516da6c28aaSamw 	if (lm_level >= 4)
517da6c28aaSamw 		control |= SMB_PWC_NOLM;
518da6c28aaSamw 
5193db3f65cSamw 	pwbuf.pw_pwd = &smbpw;
5203db3f65cSamw 
521da6c28aaSamw 	/*
522da6c28aaSamw 	 * copy old password entries to temporary file while replacing
523da6c28aaSamw 	 * the entry that matches "name"
524da6c28aaSamw 	 */
5253db3f65cSamw 	while (smb_pwd_fgetent(src, &pwbuf, SMB_PWD_GETF_ALL) != NULL) {
52689dc44ceSjose borrego 		if (strcmp(smbpw.pw_name, name) == 0) {
527da6c28aaSamw 			err = smb_pwd_chgpwent(&smbpw, password, control);
528da6c28aaSamw 			if (err == SMB_PWE_USER_DISABLE)
529da6c28aaSamw 				user_disable = B_TRUE;
530da6c28aaSamw 			err = smb_pwd_fputent(dst, &pwbuf);
531da6c28aaSamw 			newent = B_FALSE;
532da6c28aaSamw 		} else {
533da6c28aaSamw 			err = smb_pwd_fputent(dst, &pwbuf);
534da6c28aaSamw 		}
535da6c28aaSamw 
536da6c28aaSamw 		if (err != SMB_PWE_SUCCESS) {
537da6c28aaSamw 			(void) fclose(src);
538da6c28aaSamw 			(void) fclose(dst);
539da6c28aaSamw 			goto passwd_exit;
540da6c28aaSamw 		}
541da6c28aaSamw 	}
542da6c28aaSamw 
543da6c28aaSamw 	if (newent) {
544da6c28aaSamw 		if (getpwnam_r(name, &uxpw, uxbuf, sizeof (uxbuf))) {
5453db3f65cSamw 			bzero(&smbpw, sizeof (smb_passwd_t));
54689dc44ceSjose borrego 			(void) strlcpy(smbpw.pw_name, uxpw.pw_name,
54789dc44ceSjose borrego 			    sizeof (smbpw.pw_name));
548da6c28aaSamw 			smbpw.pw_uid = uxpw.pw_uid;
549da6c28aaSamw 			(void) smb_pwd_chgpwent(&smbpw, password, control);
550da6c28aaSamw 			err = smb_pwd_fputent(dst, &pwbuf);
551da6c28aaSamw 		} else {
552da6c28aaSamw 			err = SMB_PWE_USER_UNKNOWN;
553da6c28aaSamw 		}
554da6c28aaSamw 
555da6c28aaSamw 		if (err != SMB_PWE_SUCCESS) {
556da6c28aaSamw 			(void) fclose(src);
557da6c28aaSamw 			(void) fclose(dst);
558da6c28aaSamw 			goto passwd_exit;
559da6c28aaSamw 		}
560da6c28aaSamw 	}
561da6c28aaSamw 
562da6c28aaSamw 	(void) fclose(src);
563da6c28aaSamw 	if (fclose(dst) != 0) {
564da6c28aaSamw 		err = SMB_PWE_CLOSE_FAILED;
565da6c28aaSamw 		goto passwd_exit; /* Don't trust the temporary file */
566da6c28aaSamw 	}
567da6c28aaSamw 
568da6c28aaSamw 	/* Rename temp to passwd */
569da6c28aaSamw 	if (unlink(SMB_OPASSWD) && access(SMB_OPASSWD, 0) == 0) {
570da6c28aaSamw 		err = SMB_PWE_UPDATE_FAILED;
571da6c28aaSamw 		(void) unlink(SMB_PASSTEMP);
572da6c28aaSamw 		goto passwd_exit;
573da6c28aaSamw 	}
574da6c28aaSamw 
575da6c28aaSamw 	if (link(SMB_PASSWD, SMB_OPASSWD) == -1) {
576da6c28aaSamw 		err = SMB_PWE_UPDATE_FAILED;
577da6c28aaSamw 		(void) unlink(SMB_PASSTEMP);
578da6c28aaSamw 		goto passwd_exit;
579da6c28aaSamw 	}
580da6c28aaSamw 
581da6c28aaSamw 	if (rename(SMB_PASSTEMP, SMB_PASSWD) == -1) {
582da6c28aaSamw 		err = SMB_PWE_UPDATE_FAILED;
583da6c28aaSamw 		(void) unlink(SMB_PASSTEMP);
584da6c28aaSamw 		goto passwd_exit;
585da6c28aaSamw 	}
586da6c28aaSamw 
587da6c28aaSamw 	(void) chmod(SMB_PASSWD, 0400);
588da6c28aaSamw 
589da6c28aaSamw passwd_exit:
590da6c28aaSamw 	(void) smb_pwd_unlock();
591da6c28aaSamw 	if ((err == SMB_PWE_SUCCESS) && user_disable)
592da6c28aaSamw 		err = SMB_PWE_USER_DISABLE;
593da6c28aaSamw 
594da6c28aaSamw 	return (err);
595da6c28aaSamw }
596da6c28aaSamw 
597da6c28aaSamw /*
5983db3f65cSamw  * smb_pwd_fgetent
599da6c28aaSamw  *
600da6c28aaSamw  * Parse the buffer in the passed pwbuf and fill in the
601da6c28aaSamw  * smb password structure to point to the parsed information.
602da6c28aaSamw  * The entry format is:
603da6c28aaSamw  *
604da6c28aaSamw  *	<user-name>:<user-id>:<LM hash>:<NTLM hash>
605da6c28aaSamw  *
6063db3f65cSamw  * Returns a pointer to the passed pwbuf structure on success,
607da6c28aaSamw  * otherwise returns NULL.
608da6c28aaSamw  */
609da6c28aaSamw static smb_pwbuf_t *
smb_pwd_fgetent(FILE * fp,smb_pwbuf_t * pwbuf,uint32_t flags)6103db3f65cSamw smb_pwd_fgetent(FILE *fp, smb_pwbuf_t *pwbuf, uint32_t flags)
611da6c28aaSamw {
612da6c28aaSamw 	char *argv[SMB_PWD_NARG];
6133db3f65cSamw 	char *pwentry;
614da6c28aaSamw 	smb_passwd_t *pw;
615da6c28aaSamw 	smb_pwdarg_t i;
616da6c28aaSamw 	int lm_len, nt_len;
617da6c28aaSamw 
6183db3f65cSamw 	pwentry = pwbuf->pw_buf;
6193db3f65cSamw 	if (fgets(pwentry, SMB_PWD_BUFSIZE, fp) == NULL)
620da6c28aaSamw 		return (NULL);
6213db3f65cSamw 	(void) trim_whitespace(pwentry);
622da6c28aaSamw 
623da6c28aaSamw 	for (i = 0; i < SMB_PWD_NARG; ++i) {
6243db3f65cSamw 		if ((argv[i] = strsep((char **)&pwentry, ":")) == NULL)
625da6c28aaSamw 			return (NULL);
626da6c28aaSamw 	}
627da6c28aaSamw 
628da6c28aaSamw 	if ((*argv[SMB_PWD_NAME] == '\0') || (*argv[SMB_PWD_UID] == '\0'))
629da6c28aaSamw 		return (NULL);
630da6c28aaSamw 
631da6c28aaSamw 	pw = pwbuf->pw_pwd;
632da6c28aaSamw 	bzero(pw, sizeof (smb_passwd_t));
633da6c28aaSamw 	pw->pw_uid = strtoul(argv[SMB_PWD_UID], 0, 10);
63489dc44ceSjose borrego 	(void) strlcpy(pw->pw_name, argv[SMB_PWD_NAME], sizeof (pw->pw_name));
635da6c28aaSamw 
636da6c28aaSamw 	if (strcmp(argv[SMB_PWD_LMHASH], SMB_PWD_DISABLE) == 0) {
637da6c28aaSamw 		pw->pw_flags |= SMB_PWF_DISABLE;
6383db3f65cSamw 		if (flags != SMB_PWD_GETF_NOPWD) {
639da6c28aaSamw 			(void) strcpy((char *)pw->pw_lmhash, SMB_PWD_DISABLE);
640da6c28aaSamw 			(void) strcpy((char *)pw->pw_nthash, SMB_PWD_DISABLE);
6413db3f65cSamw 		}
642da6c28aaSamw 		return (pwbuf);
643da6c28aaSamw 	}
644da6c28aaSamw 
6453db3f65cSamw 	if (flags == SMB_PWD_GETF_NOPWD)
6463db3f65cSamw 		return (pwbuf);
6473db3f65cSamw 
648da6c28aaSamw 	lm_len = strlen(argv[SMB_PWD_LMHASH]);
649da6c28aaSamw 	if (lm_len == SMBAUTH_HEXHASH_SZ) {
650da6c28aaSamw 		(void) hextobin(argv[SMB_PWD_LMHASH], SMBAUTH_HEXHASH_SZ,
651da6c28aaSamw 		    (char *)pw->pw_lmhash, SMBAUTH_HASH_SZ);
652da6c28aaSamw 
653da6c28aaSamw 		pw->pw_flags |= SMB_PWF_LM;
654da6c28aaSamw 	} else if (lm_len != 0) {
655da6c28aaSamw 		return (NULL);
656da6c28aaSamw 	}
657da6c28aaSamw 
658da6c28aaSamw 	nt_len = strlen(argv[SMB_PWD_NTHASH]);
659da6c28aaSamw 	if (nt_len == SMBAUTH_HEXHASH_SZ) {
660da6c28aaSamw 		(void) hextobin(argv[SMB_PWD_NTHASH], SMBAUTH_HEXHASH_SZ,
661da6c28aaSamw 		    (char *)pw->pw_nthash, SMBAUTH_HASH_SZ);
662da6c28aaSamw 
663da6c28aaSamw 		pw->pw_flags |= SMB_PWF_NT;
664da6c28aaSamw 	} else if (nt_len != 0) {
665da6c28aaSamw 		return (NULL);
666da6c28aaSamw 	}
667da6c28aaSamw 
668da6c28aaSamw 	return (pwbuf);
669da6c28aaSamw }
670da6c28aaSamw 
6713db3f65cSamw /*
6723db3f65cSamw  * smb_pwd_chgpwent
6733db3f65cSamw  *
6743db3f65cSamw  * Updates the given smb_passwd_t structure with given password and
6753db3f65cSamw  * control information.
6763db3f65cSamw  */
677da6c28aaSamw static int
smb_pwd_chgpwent(smb_passwd_t * smbpw,const char * password,int control)678da6c28aaSamw smb_pwd_chgpwent(smb_passwd_t *smbpw, const char *password, int control)
679da6c28aaSamw {
680da6c28aaSamw 	if (control & SMB_PWC_DISABLE) {
6813db3f65cSamw 		/* disable the user */
682da6c28aaSamw 		smbpw->pw_flags |= SMB_PWF_DISABLE;
683da6c28aaSamw 		(void) strcpy((char *)smbpw->pw_lmhash, SMB_PWD_DISABLE);
684da6c28aaSamw 		(void) strcpy((char *)smbpw->pw_nthash, SMB_PWD_DISABLE);
685da6c28aaSamw 		smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
686da6c28aaSamw 		return (SMB_PWE_SUCCESS);
6873db3f65cSamw 	}
6883db3f65cSamw 
6893db3f65cSamw 	if ((control & SMB_PWC_ENABLE) && (smbpw->pw_flags & SMB_PWF_DISABLE)) {
6903db3f65cSamw 		/* enable the user if it's been disabled */
691da6c28aaSamw 		*smbpw->pw_lmhash = '\0';
692da6c28aaSamw 		*smbpw->pw_nthash = '\0';
693da6c28aaSamw 		smbpw->pw_flags &= ~(SMB_PWF_LM | SMB_PWF_NT);
694da6c28aaSamw 		return (SMB_PWE_SUCCESS);
695da6c28aaSamw 	}
696da6c28aaSamw 
697da6c28aaSamw 	/* No password update if account is disabled */
698da6c28aaSamw 	if (smbpw->pw_flags & SMB_PWF_DISABLE)
699da6c28aaSamw 		return (SMB_PWE_USER_DISABLE);
700da6c28aaSamw 
7013db3f65cSamw 	/* This call was just to update the control flags */
7023db3f65cSamw 	if (password == NULL)
7033db3f65cSamw 		return (SMB_PWE_SUCCESS);
7043db3f65cSamw 
705da6c28aaSamw 	if (control & SMB_PWC_NOLM) {
7063db3f65cSamw 		/* LM hash should not be present */
707da6c28aaSamw 		smbpw->pw_flags &= ~SMB_PWF_LM;
708da6c28aaSamw 		*smbpw->pw_lmhash = '\0';
709da6c28aaSamw 	} else {
710da6c28aaSamw 		smbpw->pw_flags |= SMB_PWF_LM;
71129bd2886SAlan Wright 		(void) smb_auth_lm_hash(password, smbpw->pw_lmhash);
712da6c28aaSamw 	}
713da6c28aaSamw 
714da6c28aaSamw 	smbpw->pw_flags |= SMB_PWF_NT;
71529bd2886SAlan Wright 	(void) smb_auth_ntlm_hash(password, smbpw->pw_nthash);
716da6c28aaSamw 	return (SMB_PWE_SUCCESS);
717da6c28aaSamw }
718da6c28aaSamw 
719da6c28aaSamw /*
7203db3f65cSamw  * smb_pwd_fputent
721da6c28aaSamw  *
7223db3f65cSamw  * If LM/NTLM hash are present, converts them to hex string
723da6c28aaSamw  * and write them along with user's name and Id to the smbpasswd
724da6c28aaSamw  * file.
725da6c28aaSamw  */
726da6c28aaSamw static int
smb_pwd_fputent(FILE * fp,const smb_pwbuf_t * pwbuf)72729bd2886SAlan Wright smb_pwd_fputent(FILE *fp, const smb_pwbuf_t *pwbuf)
728da6c28aaSamw {
729da6c28aaSamw 	smb_passwd_t *pw = pwbuf->pw_pwd;
730da6c28aaSamw 	char hex_nthash[SMBAUTH_HEXHASH_SZ+1];
731da6c28aaSamw 	char hex_lmhash[SMBAUTH_HEXHASH_SZ+1];
732da6c28aaSamw 	int rc;
733da6c28aaSamw 
734da6c28aaSamw 	if ((pw->pw_flags & SMB_PWF_LM) == SMB_PWF_LM) {
735da6c28aaSamw 		(void) bintohex((char *)pw->pw_lmhash, SMBAUTH_HASH_SZ,
736da6c28aaSamw 		    hex_lmhash, SMBAUTH_HEXHASH_SZ);
737da6c28aaSamw 		hex_lmhash[SMBAUTH_HEXHASH_SZ] = '\0';
738da6c28aaSamw 	} else {
739da6c28aaSamw 		(void) strcpy(hex_lmhash, (char *)pw->pw_lmhash);
740da6c28aaSamw 	}
741da6c28aaSamw 
742da6c28aaSamw 	if ((pw->pw_flags & SMB_PWF_NT) == SMB_PWF_NT) {
743da6c28aaSamw 		(void) bintohex((char *)pw->pw_nthash, SMBAUTH_HASH_SZ,
744da6c28aaSamw 		    hex_nthash, SMBAUTH_HEXHASH_SZ);
745da6c28aaSamw 		hex_nthash[SMBAUTH_HEXHASH_SZ] = '\0';
746da6c28aaSamw 	} else {
747da6c28aaSamw 		(void) strcpy(hex_nthash, (char *)pw->pw_nthash);
748da6c28aaSamw 	}
749da6c28aaSamw 
75089dc44ceSjose borrego 	rc = fprintf(fp, "%s:%u:%s:%s\n", pw->pw_name, pw->pw_uid,
751da6c28aaSamw 	    hex_lmhash, hex_nthash);
752da6c28aaSamw 
753da6c28aaSamw 	if (rc <= 0)
754da6c28aaSamw 		return (SMB_PWE_WRITE_FAILED);
755da6c28aaSamw 
756da6c28aaSamw 	return (SMB_PWE_SUCCESS);
757da6c28aaSamw }
758da6c28aaSamw 
7593db3f65cSamw /*
7603db3f65cSamw  * smb_pwd_lock
7613db3f65cSamw  *
7623db3f65cSamw  * A wrapper around smb_pwd_flck() which locks smb password
7633db3f65cSamw  * file so that only one thread at a time is operational.
7643db3f65cSamw  */
765da6c28aaSamw static int
smb_pwd_lock(void)766da6c28aaSamw smb_pwd_lock(void)
767da6c28aaSamw {
768da6c28aaSamw 	int res;
769da6c28aaSamw 
770da6c28aaSamw 	if (smb_pwd_flck()) {
771da6c28aaSamw 		switch (errno) {
772da6c28aaSamw 		case EINTR:
773da6c28aaSamw 			res = SMB_PWE_BUSY;
774da6c28aaSamw 			break;
775da6c28aaSamw 		case EACCES:
776da6c28aaSamw 			res = SMB_PWE_DENIED;
777da6c28aaSamw 			break;
778da6c28aaSamw 		case 0:
779da6c28aaSamw 			res = SMB_PWE_SUCCESS;
780da6c28aaSamw 			break;
781da6c28aaSamw 		}
782da6c28aaSamw 	} else
783da6c28aaSamw 		res = SMB_PWE_SUCCESS;
784da6c28aaSamw 
785da6c28aaSamw 	return (res);
786da6c28aaSamw }
787da6c28aaSamw 
7883db3f65cSamw /*
78996a62adaSjoyce mcintosh  * smb_pwd_unlock
7903db3f65cSamw  *
7913db3f65cSamw  * A wrapper around smb_pwd_fulck() which unlocks
7923db3f65cSamw  * smb password file.
7933db3f65cSamw  */
794da6c28aaSamw static int
smb_pwd_unlock(void)795da6c28aaSamw smb_pwd_unlock(void)
796da6c28aaSamw {
797da6c28aaSamw 	if (smb_pwd_fulck())
798da6c28aaSamw 		return (SMB_PWE_SYSTEM_ERROR);
799da6c28aaSamw 
800da6c28aaSamw 	return (SMB_PWE_SUCCESS);
801da6c28aaSamw }
802da6c28aaSamw 
8033db3f65cSamw /*
8043db3f65cSamw  * smb_pwd_flck
8053db3f65cSamw  *
8063db3f65cSamw  * Creates a lock file and grabs an exclusive (write) lock on it.
8073db3f65cSamw  */
808da6c28aaSamw static int
smb_pwd_flck(void)809da6c28aaSamw smb_pwd_flck(void)
810da6c28aaSamw {
811da6c28aaSamw 	int seconds = 0;
812da6c28aaSamw 
813da6c28aaSamw 	(void) mutex_lock(&lck_lock);
814da6c28aaSamw 	for (;;) {
815da6c28aaSamw 		if (lck_pid != 0 && lck_pid != getpid()) {
816da6c28aaSamw 			/* somebody forked */
817da6c28aaSamw 			lck_pid = 0;
818da6c28aaSamw 			lck_tid = 0;
819da6c28aaSamw 		}
820da6c28aaSamw 
821da6c28aaSamw 		if (lck_tid == 0) {
822da6c28aaSamw 			if ((fildes = creat(SMB_PASSLCK, 0600)) == -1)
823da6c28aaSamw 				break;
824da6c28aaSamw 			flock.l_type = F_WRLCK;
825da6c28aaSamw 			if (fcntl(fildes, F_SETLK, &flock) != -1) {
826da6c28aaSamw 				lck_pid = getpid();
827da6c28aaSamw 				lck_tid = thr_self();
828da6c28aaSamw 				(void) mutex_unlock(&lck_lock);
829da6c28aaSamw 				return (0);
830da6c28aaSamw 			}
831da6c28aaSamw 			(void) close(fildes);
832da6c28aaSamw 			fildes = -1;
833da6c28aaSamw 		}
834da6c28aaSamw 
835da6c28aaSamw 		if (seconds++ >= S_WAITTIME) {
836da6c28aaSamw 			/*
837da6c28aaSamw 			 * For compatibility with the past, pretend
838da6c28aaSamw 			 * that we were interrupted by SIGALRM.
839da6c28aaSamw 			 */
840da6c28aaSamw 			errno = EINTR;
841da6c28aaSamw 			break;
842da6c28aaSamw 		}
843da6c28aaSamw 
844da6c28aaSamw 		(void) mutex_unlock(&lck_lock);
845da6c28aaSamw 		(void) sleep(1);
846da6c28aaSamw 		(void) mutex_lock(&lck_lock);
847da6c28aaSamw 	}
848da6c28aaSamw 	(void) mutex_unlock(&lck_lock);
849da6c28aaSamw 
850da6c28aaSamw 	return (-1);
851da6c28aaSamw }
852da6c28aaSamw 
8533db3f65cSamw /*
8543db3f65cSamw  * smb_pwd_fulck
8553db3f65cSamw  *
8563db3f65cSamw  * Unlocks smb password file for operations done via
8573db3f65cSamw  * this library APIs.
8583db3f65cSamw  */
859da6c28aaSamw static int
smb_pwd_fulck(void)860da6c28aaSamw smb_pwd_fulck(void)
861da6c28aaSamw {
862da6c28aaSamw 	(void) mutex_lock(&lck_lock);
863da6c28aaSamw 	if (lck_tid == thr_self() && fildes >= 0) {
864da6c28aaSamw 		flock.l_type = F_UNLCK;
865da6c28aaSamw 		(void) fcntl(fildes, F_SETLK, &flock);
866da6c28aaSamw 		(void) close(fildes);
867da6c28aaSamw 		fildes = -1;
868da6c28aaSamw 		lck_pid = 0;
869da6c28aaSamw 		lck_tid = 0;
870da6c28aaSamw 		(void) mutex_unlock(&lck_lock);
871da6c28aaSamw 		return (0);
872da6c28aaSamw 	}
873da6c28aaSamw 	(void) mutex_unlock(&lck_lock);
874da6c28aaSamw 	return (-1);
875da6c28aaSamw }
8763db3f65cSamw 
8773db3f65cSamw /*
8783db3f65cSamw  * Local User Cache Functions
8793db3f65cSamw  *
8803db3f65cSamw  * Local user cache is implemented using AVL tree
8813db3f65cSamw  */
8823db3f65cSamw 
8833db3f65cSamw /*
8843db3f65cSamw  * smb_lucache_cmp
8853db3f65cSamw  *
8863db3f65cSamw  * AVL compare function, the key is username.
8873db3f65cSamw  */
8883db3f65cSamw static int
smb_lucache_cmp(const void * p1,const void * p2)8893db3f65cSamw smb_lucache_cmp(const void *p1, const void *p2)
8903db3f65cSamw {
8913db3f65cSamw 	smb_ucnode_t *u1 = (smb_ucnode_t *)p1;
8923db3f65cSamw 	smb_ucnode_t *u2 = (smb_ucnode_t *)p2;
8933db3f65cSamw 	int rc;
8943db3f65cSamw 
8953db3f65cSamw 	rc = strcmp(u1->cn_user.su_name, u2->cn_user.su_name);
8963db3f65cSamw 
8973db3f65cSamw 	if (rc < 0)
8983db3f65cSamw 		return (-1);
8993db3f65cSamw 
9003db3f65cSamw 	if (rc > 0)
9013db3f65cSamw 		return (1);
9023db3f65cSamw 
9033db3f65cSamw 	return (0);
9043db3f65cSamw }
9053db3f65cSamw 
9063db3f65cSamw /*
9073db3f65cSamw  * smb_lucache_update
9083db3f65cSamw  *
9093db3f65cSamw  * Updates the cache if needed. Whether an update is needed
9103db3f65cSamw  * is determined based on smbpasswd file modification timestamp
9113db3f65cSamw  */
9123db3f65cSamw static void
smb_lucache_update(void)9133db3f65cSamw smb_lucache_update(void)
9143db3f65cSamw {
9153db3f65cSamw 	struct stat64 stbuf;
9163db3f65cSamw 	int rc;
9173db3f65cSamw 
9183db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
9193db3f65cSamw 	switch (smb_uch.uc_state) {
9203db3f65cSamw 	default:
9213db3f65cSamw 	case SMB_UCHS_NOCACHE:
9223db3f65cSamw 		assert(0);
9233db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9243db3f65cSamw 		return;
9253db3f65cSamw 
9263db3f65cSamw 	case SMB_UCHS_CREATED:
9273db3f65cSamw 	case SMB_UCHS_UPDATED:
9283db3f65cSamw 		break;
9293db3f65cSamw 
9303db3f65cSamw 	case SMB_UCHS_UPDATING:
9313db3f65cSamw 		/* Want only one thread executing this function at a time */
9323db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9333db3f65cSamw 		return;
9343db3f65cSamw 
9353db3f65cSamw 	case SMB_UCHS_DESTROYING:
9363db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9373db3f65cSamw 		return;
9383db3f65cSamw 	}
9393db3f65cSamw 
9403db3f65cSamw 	/*
9413db3f65cSamw 	 * smb_pwd_lock() is not called here so it can
9423db3f65cSamw 	 * be checked quickly whether an updated is needed
9433db3f65cSamw 	 */
9443db3f65cSamw 	if (stat64(SMB_PASSWD, &stbuf) < 0) {
9453db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9463db3f65cSamw 		if (errno != ENOENT)
9473db3f65cSamw 			return;
9483db3f65cSamw 
9493db3f65cSamw 		/* no smbpasswd file; empty the cache */
9503db3f65cSamw 		smb_lucache_flush();
9513db3f65cSamw 		return;
9523db3f65cSamw 	}
9533db3f65cSamw 
9543db3f65cSamw 	if (stbuf.st_size == 0) {
9553db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9563db3f65cSamw 
9573db3f65cSamw 		/* empty smbpasswd file; empty the cache */
9583db3f65cSamw 		smb_lucache_flush();
9593db3f65cSamw 		return;
9603db3f65cSamw 	}
9613db3f65cSamw 
9623db3f65cSamw 	if ((smb_uch.uc_timestamp.tv_sec == stbuf.st_mtim.tv_sec) &&
9633db3f65cSamw 	    (smb_uch.uc_timestamp.tv_nsec == stbuf.st_mtim.tv_nsec)) {
9643db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
9653db3f65cSamw 		/* No changes since the last cache update */
9663db3f65cSamw 		return;
9673db3f65cSamw 	}
9683db3f65cSamw 
9693db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_UPDATING;
9703db3f65cSamw 	smb_uch.uc_refcnt++;
9713db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
9723db3f65cSamw 
9733db3f65cSamw 	rc = smb_lucache_do_update();
9743db3f65cSamw 
9753db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
9763db3f65cSamw 	if ((rc == SMB_PWE_SUCCESS) && (stat64(SMB_PASSWD, &stbuf) == 0))
9773db3f65cSamw 		smb_uch.uc_timestamp = stbuf.st_mtim;
9783db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_UPDATED;
9793db3f65cSamw 	smb_uch.uc_refcnt--;
9803db3f65cSamw 	(void) cond_broadcast(&smb_uch.uc_cv);
9813db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
9823db3f65cSamw }
9833db3f65cSamw 
9843db3f65cSamw /*
9853db3f65cSamw  * smb_lucache_do_update
9863db3f65cSamw  *
9873db3f65cSamw  * This function takes care of updating the AVL tree.
9883db3f65cSamw  * If an entry has been updated, it'll be modified in place.
9893db3f65cSamw  *
9903db3f65cSamw  * New entries will be added to a temporary AVL tree then
9913db3f65cSamw  * passwod file is unlocked and all the new entries will
9923db3f65cSamw  * be transferred to the main cache from the temporary tree.
9933db3f65cSamw  *
9943db3f65cSamw  * This function MUST NOT be called directly
9953db3f65cSamw  */
9963db3f65cSamw static int
smb_lucache_do_update(void)9973db3f65cSamw smb_lucache_do_update(void)
9983db3f65cSamw {
9993db3f65cSamw 	avl_tree_t tmp_cache;
10003db3f65cSamw 	smb_pwbuf_t pwbuf;
10013db3f65cSamw 	smb_passwd_t smbpw;
10023db3f65cSamw 	smb_ucnode_t uc_node;
10033db3f65cSamw 	smb_ucnode_t *uc_newnode;
10043db3f65cSamw 	smb_luser_t *user;
10053db3f65cSamw 	smb_sid_t *sid;
10063db3f65cSamw 	idmap_stat idm_stat;
10073db3f65cSamw 	int rc = SMB_PWE_SUCCESS;
10083db3f65cSamw 	void *cookie = NULL;
10093db3f65cSamw 	FILE *fp;
10103db3f65cSamw 
1011*12b65585SGordon Ross 	if ((rc = smb_pwd_lock()) != SMB_PWE_SUCCESS) {
1012*12b65585SGordon Ross 		syslog(LOG_WARNING, "smb_pwdutil: lock failed, err=%d", rc);
10133db3f65cSamw 		return (rc);
1014*12b65585SGordon Ross 	}
10153db3f65cSamw 
10163db3f65cSamw 	if ((fp = fopen(SMB_PASSWD, "rF")) == NULL) {
1017*12b65585SGordon Ross 		syslog(LOG_WARNING, "smb_pwdutil: open failed, %m");
10183db3f65cSamw 		(void) smb_pwd_unlock();
10193db3f65cSamw 		return (SMB_PWE_OPEN_FAILED);
10203db3f65cSamw 	}
10213db3f65cSamw 
10223db3f65cSamw 	avl_create(&tmp_cache, smb_lucache_cmp,
10233db3f65cSamw 	    sizeof (smb_ucnode_t), offsetof(smb_ucnode_t, cn_link));
10243db3f65cSamw 
10253db3f65cSamw 	bzero(&pwbuf, sizeof (smb_pwbuf_t));
10263db3f65cSamw 	pwbuf.pw_pwd = &smbpw;
10273db3f65cSamw 
10283db3f65cSamw 	(void) rw_rdlock(&smb_uch.uc_cache_lck);
10293db3f65cSamw 
10303db3f65cSamw 	while (smb_pwd_fgetent(fp, &pwbuf, SMB_PWD_GETF_NOPWD) != NULL) {
103189dc44ceSjose borrego 		uc_node.cn_user.su_name = smbpw.pw_name;
10323db3f65cSamw 		uc_newnode = avl_find(&smb_uch.uc_cache, &uc_node, NULL);
10333db3f65cSamw 		if (uc_newnode) {
10343db3f65cSamw 			/* update the node info */
10353db3f65cSamw 			uc_newnode->cn_user.su_ctrl = smbpw.pw_flags;
10363db3f65cSamw 			continue;
10373db3f65cSamw 		}
10383db3f65cSamw 
10393db3f65cSamw 		/* create a new node */
10403db3f65cSamw 		if ((uc_newnode = malloc(sizeof (smb_ucnode_t))) == NULL) {
10413db3f65cSamw 			rc = SMB_PWE_NO_MEMORY;
10423db3f65cSamw 			break;
10433db3f65cSamw 		}
10443db3f65cSamw 
10453db3f65cSamw 		bzero(uc_newnode, sizeof (smb_ucnode_t));
10463db3f65cSamw 		user = &uc_newnode->cn_user;
10473db3f65cSamw 		user->su_ctrl = smbpw.pw_flags;
10483db3f65cSamw 
10493db3f65cSamw 		idm_stat = smb_idmap_getsid(smbpw.pw_uid, SMB_IDMAP_USER, &sid);
10503db3f65cSamw 		if (idm_stat != IDMAP_SUCCESS) {
10513db3f65cSamw 			syslog(LOG_WARNING, "smb_pwdutil: couldn't obtain SID "
10523db3f65cSamw 			    "for uid=%u (%d)", smbpw.pw_uid, idm_stat);
10533db3f65cSamw 			free(uc_newnode);
10543db3f65cSamw 			continue;
10553db3f65cSamw 		}
10563db3f65cSamw 		(void) smb_sid_getrid(sid, &user->su_rid);
10573db3f65cSamw 		smb_sid_free(sid);
10583db3f65cSamw 
105989dc44ceSjose borrego 		user->su_name = strdup(smbpw.pw_name);
10603db3f65cSamw 		if (user->su_name == NULL) {
10613db3f65cSamw 			rc = SMB_PWE_NO_MEMORY;
10623db3f65cSamw 			free(uc_newnode);
10633db3f65cSamw 			break;
10643db3f65cSamw 		}
10653db3f65cSamw 
10663db3f65cSamw 		avl_add(&tmp_cache, uc_newnode);
10673db3f65cSamw 	}
10683db3f65cSamw 
10693db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
10703db3f65cSamw 	(void) fclose(fp);
10713db3f65cSamw 	(void) smb_pwd_unlock();
10723db3f65cSamw 
10733db3f65cSamw 	/* Destroy the temporary list */
10743db3f65cSamw 	(void) rw_wrlock(&smb_uch.uc_cache_lck);
10753db3f65cSamw 	while ((uc_newnode = avl_destroy_nodes(&tmp_cache, &cookie)) != NULL) {
10763db3f65cSamw 		avl_add(&smb_uch.uc_cache, uc_newnode);
10773db3f65cSamw 	}
10783db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
10793db3f65cSamw 
10803db3f65cSamw 	avl_destroy(&tmp_cache);
10813db3f65cSamw 
10823db3f65cSamw 	return (rc);
10833db3f65cSamw }
10843db3f65cSamw 
10853db3f65cSamw /*
10863db3f65cSamw  * smb_lucache_create
10873db3f65cSamw  *
10883db3f65cSamw  * Creates the AVL tree and initializes the global user cache handle.
10893db3f65cSamw  * This function doesn't populate the cache.
10903db3f65cSamw  * User cache is only created by smbd at startup
10913db3f65cSamw  */
10923db3f65cSamw static void
smb_lucache_create(void)10933db3f65cSamw smb_lucache_create(void)
10943db3f65cSamw {
10953db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
10963db3f65cSamw 	if (smb_uch.uc_state != SMB_UCHS_NOCACHE) {
10973db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
10983db3f65cSamw 		return;
10993db3f65cSamw 	}
11003db3f65cSamw 
11013db3f65cSamw 	avl_create(&smb_uch.uc_cache, smb_lucache_cmp,
11023db3f65cSamw 	    sizeof (smb_ucnode_t), offsetof(smb_ucnode_t, cn_link));
11033db3f65cSamw 
11043db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_CREATED;
11053db3f65cSamw 	bzero(&smb_uch.uc_timestamp, sizeof (timestruc_t));
11063db3f65cSamw 	smb_uch.uc_refcnt = 0;
11073db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
11083db3f65cSamw }
11093db3f65cSamw 
11103db3f65cSamw /*
11113db3f65cSamw  * smb_lucache_flush
11123db3f65cSamw  *
11133db3f65cSamw  * Removes and frees all the cache entries
11143db3f65cSamw  */
11153db3f65cSamw static void
smb_lucache_flush(void)11163db3f65cSamw smb_lucache_flush(void)
11173db3f65cSamw {
11183db3f65cSamw 	void *cookie = NULL;
11193db3f65cSamw 	smb_ucnode_t *ucnode;
11203db3f65cSamw 
11213db3f65cSamw 	(void) rw_wrlock(&smb_uch.uc_cache_lck);
11223db3f65cSamw 	while ((ucnode = avl_destroy_nodes(&smb_uch.uc_cache, &cookie))
11233db3f65cSamw 	    != NULL) {
11243db3f65cSamw 		free(ucnode->cn_user.su_name);
11253db3f65cSamw 		free(ucnode->cn_user.su_fullname);
11263db3f65cSamw 		free(ucnode->cn_user.su_desc);
11273db3f65cSamw 		free(ucnode);
11283db3f65cSamw 	}
11293db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
11303db3f65cSamw }
11313db3f65cSamw 
11323db3f65cSamw /*
11333db3f65cSamw  * smb_lucache_destroy
11343db3f65cSamw  *
11353db3f65cSamw  * Destroys the cache.
11363db3f65cSamw  * This function is only called in smb_pwd_fini()
11373db3f65cSamw  * User cache is only destroyed by smbd upon shutdown
11383db3f65cSamw  */
11393db3f65cSamw static void
smb_lucache_destroy(void)11403db3f65cSamw smb_lucache_destroy(void)
11413db3f65cSamw {
11423db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
11433db3f65cSamw 	switch (smb_uch.uc_state) {
11443db3f65cSamw 	case SMB_UCHS_NOCACHE:
11453db3f65cSamw 	case SMB_UCHS_DESTROYING:
11463db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
11473db3f65cSamw 		return;
11483db3f65cSamw 
11493db3f65cSamw 	default:
11503db3f65cSamw 		break;
11513db3f65cSamw 	}
11523db3f65cSamw 
11533db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_DESTROYING;
11543db3f65cSamw 
11553db3f65cSamw 	while (smb_uch.uc_refcnt > 0)
11563db3f65cSamw 		(void) cond_wait(&smb_uch.uc_cv, &smb_uch.uc_mtx);
11573db3f65cSamw 
11583db3f65cSamw 	smb_lucache_flush();
11593db3f65cSamw 
11603db3f65cSamw 	avl_destroy(&smb_uch.uc_cache);
11613db3f65cSamw 	smb_uch.uc_state = SMB_UCHS_NOCACHE;
11623db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
11633db3f65cSamw }
11643db3f65cSamw 
11653db3f65cSamw /*
11663db3f65cSamw  * smb_lucache_lock
11673db3f65cSamw  *
11683db3f65cSamw  * Locks the user cache for reading and also
11693db3f65cSamw  * increment the handle reference count.
11703db3f65cSamw  */
11713db3f65cSamw static int
smb_lucache_lock(void)11723db3f65cSamw smb_lucache_lock(void)
11733db3f65cSamw {
11743db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
11753db3f65cSamw 	switch (smb_uch.uc_state) {
11763db3f65cSamw 	case SMB_UCHS_NOCACHE:
11773db3f65cSamw 		assert(0);
11783db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
11793db3f65cSamw 		return (SMB_PWE_DENIED);
11803db3f65cSamw 
11813db3f65cSamw 	case SMB_UCHS_DESTROYING:
11823db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
11833db3f65cSamw 		return (SMB_PWE_DENIED);
11843db3f65cSamw 	}
11853db3f65cSamw 	smb_uch.uc_refcnt++;
11863db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
11873db3f65cSamw 
11883db3f65cSamw 	(void) rw_rdlock(&smb_uch.uc_cache_lck);
11893db3f65cSamw 	return (SMB_PWE_SUCCESS);
11903db3f65cSamw }
11913db3f65cSamw 
11923db3f65cSamw /*
11933db3f65cSamw  * smb_lucache_unlock
11943db3f65cSamw  *
11953db3f65cSamw  * Unlock the cache
11963db3f65cSamw  */
11973db3f65cSamw static void
smb_lucache_unlock(void)11983db3f65cSamw smb_lucache_unlock(void)
11993db3f65cSamw {
12003db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
12013db3f65cSamw 
12023db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
12033db3f65cSamw 	smb_uch.uc_refcnt--;
12043db3f65cSamw 	(void) cond_broadcast(&smb_uch.uc_cv);
12053db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
12063db3f65cSamw }
12073db3f65cSamw 
12083db3f65cSamw /*
12093db3f65cSamw  * smb_lucache_num
12103db3f65cSamw  *
12113db3f65cSamw  * Returns the number of cache entries
12123db3f65cSamw  */
12133db3f65cSamw static int
smb_lucache_num(void)12143db3f65cSamw smb_lucache_num(void)
12153db3f65cSamw {
12163db3f65cSamw 	int num;
12173db3f65cSamw 
12183db3f65cSamw 	(void) mutex_lock(&smb_uch.uc_mtx);
12193db3f65cSamw 	switch (smb_uch.uc_state) {
12203db3f65cSamw 	case SMB_UCHS_NOCACHE:
12213db3f65cSamw 		assert(0);
12223db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
12233db3f65cSamw 		return (0);
12243db3f65cSamw 
12253db3f65cSamw 	case SMB_UCHS_DESTROYING:
12263db3f65cSamw 		(void) mutex_unlock(&smb_uch.uc_mtx);
12273db3f65cSamw 		return (0);
12283db3f65cSamw 	}
12293db3f65cSamw 	(void) mutex_unlock(&smb_uch.uc_mtx);
12303db3f65cSamw 
12313db3f65cSamw 	(void) rw_rdlock(&smb_uch.uc_cache_lck);
12323db3f65cSamw 	num = (int)avl_numnodes(&smb_uch.uc_cache);
12333db3f65cSamw 	(void) rw_unlock(&smb_uch.uc_cache_lck);
12343db3f65cSamw 
12353db3f65cSamw 	return (num);
12363db3f65cSamw }
1237