14bff34ethurlow/*
24bff34ethurlow * CDDL HEADER START
34bff34ethurlow *
44bff34ethurlow * The contents of this file are subject to the terms of the
54bff34ethurlow * Common Development and Distribution License (the "License").
64bff34ethurlow * You may not use this file except in compliance with the License.
74bff34ethurlow *
84bff34ethurlow * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94bff34ethurlow * or http://www.opensolaris.org/os/licensing.
104bff34ethurlow * See the License for the specific language governing permissions
114bff34ethurlow * and limitations under the License.
124bff34ethurlow *
134bff34ethurlow * When distributing Covered Code, include this CDDL HEADER in each
144bff34ethurlow * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154bff34ethurlow * If applicable, add the following below this CDDL HEADER, with the
164bff34ethurlow * fields enclosed by brackets "[]" replaced with your own identifying
174bff34ethurlow * information: Portions Copyright [yyyy] [name of copyright owner]
184bff34ethurlow *
194bff34ethurlow * CDDL HEADER END
204bff34ethurlow */
214bff34ethurlow
224bff34ethurlow/*
23613a2f6Gordon Ross * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
244bff34ethurlow * Use is subject to license terms.
254bff34ethurlow */
264bff34ethurlow
274bff34ethurlow/*
284bff34ethurlow * Password Keychain storage mechanism.
294bff34ethurlow */
304bff34ethurlow
314bff34ethurlow#include <sys/types.h>
324bff34ethurlow#include <sys/param.h>
334bff34ethurlow#include <sys/errno.h>
344bff34ethurlow#include <sys/sysmacros.h>
354bff34ethurlow#include <sys/uio.h>
364bff34ethurlow#include <sys/buf.h>
374bff34ethurlow#include <sys/modctl.h>
384bff34ethurlow#include <sys/open.h>
394bff34ethurlow#include <sys/file.h>
404bff34ethurlow#include <sys/kmem.h>
414bff34ethurlow#include <sys/conf.h>
424bff34ethurlow#include <sys/cmn_err.h>
434bff34ethurlow#include <sys/stat.h>
444bff34ethurlow#include <sys/ddi.h>
454bff34ethurlow#include <sys/sunddi.h>
464bff34ethurlow#include <sys/sunldi.h>
474bff34ethurlow#include <sys/policy.h>
484bff34ethurlow#include <sys/zone.h>
494bff34ethurlow#include <sys/pathname.h>
504bff34ethurlow#include <sys/mount.h>
514bff34ethurlow#include <sys/sdt.h>
524bff34ethurlow#include <fs/fs_subr.h>
534bff34ethurlow#include <sys/devops.h>
544bff34ethurlow#include <sys/thread.h>
554bff34ethurlow#include <sys/mkdev.h>
564bff34ethurlow#include <sys/avl.h>
574bff34ethurlow#include <sys/avl_impl.h>
58613a2f6Gordon Ross#include <sys/u8_textprep.h>
594bff34ethurlow
604bff34ethurlow#include <netsmb/smb_osdep.h>
614bff34ethurlow
624bff34ethurlow#include <netsmb/smb.h>
634bff34ethurlow#include <netsmb/smb_conn.h>
644bff34ethurlow#include <netsmb/smb_subr.h>
654bff34ethurlow#include <netsmb/smb_dev.h>
664bff34ethurlow#include <netsmb/smb_pass.h>
674bff34ethurlow
684bff34ethurlow/*
694bff34ethurlow * The smb_ptd is a cache of Uid's, User names, passwords and domain names.
704bff34ethurlow * It will be used for storing the password information for a user and will
714bff34ethurlow * be used to for connections without entering the pasword again if its
724bff34ethurlow * already keyed in by the user. Its a kind of Key-Chain mechanism
734bff34ethurlow * implemented by Apple folks.
744bff34ethurlow */
754bff34ethurlow
764bff34ethurlow/*
774bff34ethurlow * Information stored in the nodes:
784bff34ethurlow * UID:  Uid of the person who initiated the login request.
794bff34ethurlow * ZoneID: ZoneID of the zone from where the login request is initiated.
804bff34ethurlow * Username: Username in the CIFS server.
814bff34ethurlow * Srvdom: Domain name/ Server name of the CIFS server.
824bff34ethurlow * Password: Password of the user.
834bff34ethurlow * For more information, see smb_pass.h and sys/avl.h
844bff34ethurlow */
854bff34ethurlow
864bff34ethurlow/*
874bff34ethurlow * Information retrieved from the node.
884bff34ethurlow * Node/password information can only be retrived with a call
894bff34ethurlow * to smb_pkey_getpw(). Password never gets copied to the userspace.
904bff34ethurlow * It will be copied to the Kernel data structure smbioc_ossn->ioc_password
914bff34ethurlow * when needed for doing the "Session Setup". All other calls will return
924bff34ethurlow * either a success or a failure.
934bff34ethurlow */
944bff34ethurlow
954bff34ethurlowavl_tree_t smb_ptd; /* AVL password tree descriptor */
964bff34ethurlowunsigned int smb_list_len = 0;	/* No. of elements in the tree. */
974bff34ethurlowkmutex_t smb_ptd_lock; 	/* Mutex lock for controlled access */
984bff34ethurlow
99613a2f6Gordon Rossint smb_pkey_check(smbioc_pk_t *pk, cred_t *cr);
100613a2f6Gordon Rossint smb_pkey_deluid(uid_t ioc_uid, cred_t *cr);
101613a2f6Gordon Ross
1024bff34ethurlow/*
1034bff34ethurlow * This routine is called by AVL tree calls when they want to find a
1044bff34ethurlow * node, find the next position in the tree to add or for deletion.
1054bff34ethurlow * Compare nodes from the tree to find the actual node based on
1064bff34ethurlow * uid/zoneid/username/domainname.
1074bff34ethurlow */
1084bff34ethurlowint
1094bff34ethurlowsmb_pkey_cmp(const void *a, const void *b)
1104bff34ethurlow{
1114bff34ethurlow	const smb_passid_t *pa = (smb_passid_t *)a;
1124bff34ethurlow	const smb_passid_t *pb = (smb_passid_t *)b;
113613a2f6Gordon Ross	int duser, dsrv, error;
1144bff34ethurlow
1154bff34ethurlow	ASSERT(MUTEX_HELD(&smb_ptd_lock));
1164bff34ethurlow
1174bff34ethurlow	/*
1184bff34ethurlow	 * The nodes are added sorted on the uid/zoneid/domainname/username
1194bff34ethurlow	 * We will do this:
1204bff34ethurlow	 * Compare uid's. The owner who stored the node gets access.
1214bff34ethurlow	 * Then zoneid to check if the access is from the same zone.
1224bff34ethurlow	 * Compare usernames.
1234bff34ethurlow	 * If the above are same, then compare domain/server names.
1244bff34ethurlow	 */
1254bff34ethurlow	if (pa->uid < pb->uid)
1264bff34ethurlow		return (-1);
1274bff34ethurlow	if (pa->uid > pb->uid)
1284bff34ethurlow		return (+1);
1294bff34ethurlow	if (pa->zoneid < pb->zoneid)
1304bff34ethurlow		return (-1);
1314bff34ethurlow	if (pa->zoneid > pb->zoneid)
1324bff34ethurlow		return (+1);
133613a2f6Gordon Ross	dsrv = u8_strcmp(pa->srvdom, pb->srvdom, 0,
134613a2f6Gordon Ross	    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error);
1354bff34ethurlow	if (dsrv < 0)
1364bff34ethurlow		return (-1);
1374bff34ethurlow	if (dsrv > 0)
1384bff34ethurlow		return (+1);
139613a2f6Gordon Ross	duser = u8_strcmp(pa->username, pb->username, 0,
140613a2f6Gordon Ross	    U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error);
1414bff34ethurlow	if (duser < 0)
1424bff34ethurlow		return (-1);
1434bff34ethurlow	if (duser > 0)
1444bff34ethurlow		return (+1);
1454bff34ethurlow	return (0);
1464bff34ethurlow}
1474bff34ethurlow
1484bff34ethurlow/*
1494bff34ethurlow * Initialization of the code that deals with uid and passwords.
1504bff34ethurlow */
1514bff34ethurlowvoid
1524bff34ethurlowsmb_pkey_init()
1534bff34ethurlow{
1544bff34ethurlow	avl_create(&smb_ptd,
1554bff34ethurlow	    smb_pkey_cmp,
1564bff34ethurlow	    sizeof (smb_passid_t),
1574bff34ethurlow	    offsetof(smb_passid_t,
1584bff34ethurlow	    cpnode));
1594bff34ethurlow	mutex_init(&smb_ptd_lock, NULL, MUTEX_DEFAULT, NULL);
1604bff34ethurlow}
1614bff34ethurlow
1624bff34ethurlow/*
1634bff34ethurlow * Destroy the full AVL tree.
164613a2f6Gordon Ross * Called just before unload.
1654bff34ethurlow */
1664bff34ethurlowvoid
1674bff34ethurlowsmb_pkey_fini()
1684bff34ethurlow{
16902d09e0Gordon Ross	(void) smb_pkey_deluid((uid_t)-1, kcred);
1704bff34ethurlow	avl_destroy(&smb_ptd);
1714bff34ethurlow	mutex_destroy(&smb_ptd_lock);
1724bff34ethurlow}
1734bff34ethurlow
1744bff34ethurlow/*
1754bff34ethurlow * Driver unload calls this to ask if we
1764bff34ethurlow * have any stored passwords
1774bff34ethurlow */
1784bff34ethurlowint
1794bff34ethurlowsmb_pkey_idle()
1804bff34ethurlow{
1814bff34ethurlow	int n;
1824bff34ethurlow
1834bff34ethurlow	mutex_enter(&smb_ptd_lock);
1844bff34ethurlow	n = avl_numnodes(&smb_ptd);
1854bff34ethurlow	mutex_exit(&smb_ptd_lock);
1864bff34ethurlow
1874bff34ethurlow	return ((n) ? EBUSY : 0);
1884bff34ethurlow}
1894bff34ethurlow
19002d09e0Gordon Rossstatic void
19102d09e0Gordon Rosssmb_pkey_delete(smb_passid_t *tmp)
1924bff34ethurlow{
1934bff34ethurlow	ASSERT(MUTEX_HELD(&smb_ptd_lock));
1944bff34ethurlow	avl_remove(&smb_ptd, tmp);
195613a2f6Gordon Ross	strfree(tmp->srvdom);
196613a2f6Gordon Ross	strfree(tmp->username);
1974bff34ethurlow	kmem_free(tmp, sizeof (*tmp));
1984bff34ethurlow}
1994bff34ethurlow
2004bff34ethurlow
2014bff34ethurlow/*
2024bff34ethurlow * Remove a node from the AVL tree identified by cpid.
2034bff34ethurlow */
2044bff34ethurlowint
2054bff34ethurlowsmb_pkey_del(smbioc_pk_t *pk, cred_t *cr)
2064bff34ethurlow{
2074bff34ethurlow	avl_index_t where;
2084bff34ethurlow	smb_passid_t buf, *cpid, *tmp;
2094bff34ethurlow	uid_t uid;
2104bff34ethurlow
2114bff34ethurlow	tmp = &buf;
2124bff34ethurlow	uid = pk->pk_uid;
2134bff34ethurlow	if (uid == (uid_t)-1)
2144bff34ethurlow		uid = crgetruid(cr);
2154bff34ethurlow	else {
2164bff34ethurlow		if (secpolicy_smbfs_login(cr, uid))
2174bff34ethurlow			return (EPERM);
2184bff34ethurlow	}
2194bff34ethurlow	tmp->uid = uid;
2204bff34ethurlow	tmp->zoneid = getzoneid();
2214bff34ethurlow	tmp->srvdom = pk->pk_dom;
2224bff34ethurlow	tmp->username = pk->pk_usr;
2234bff34ethurlow
2244bff34ethurlow	mutex_enter(&smb_ptd_lock);
2254bff34ethurlow	if ((cpid = (smb_passid_t *)avl_find(&smb_ptd,
2264bff34ethurlow	    tmp, &where)) != NULL) {
22702d09e0Gordon Ross		smb_pkey_delete(cpid);
2284bff34ethurlow	}
2294bff34ethurlow	mutex_exit(&smb_ptd_lock);
2304bff34ethurlow
2314bff34ethurlow	return (0);
2324bff34ethurlow}
2334bff34ethurlow
2344bff34ethurlow/*
2354bff34ethurlow * Delete the entries owned by a particular user
2364bff34ethurlow * based on uid. We go through all the nodes and
2374bff34ethurlow * delete the nodes whereever the uid matches.
2384bff34ethurlow *
2394bff34ethurlow * Also implements "delete all" when uid == -1.
2404bff34ethurlow *
2414bff34ethurlow * You must have privilege to use any uid other
2424bff34ethurlow * than your real uid.
2434bff34ethurlow */
2444bff34ethurlowint
2454bff34ethurlowsmb_pkey_deluid(uid_t ioc_uid, cred_t *cr)
2464bff34ethurlow{
2474bff34ethurlow	smb_passid_t *cpid, *tmp;
2484bff34ethurlow
2494bff34ethurlow	if (secpolicy_smbfs_login(cr, ioc_uid))
2504bff34ethurlow		return (EPERM);
2514bff34ethurlow
2524bff34ethurlow	mutex_enter(&smb_ptd_lock);
2534bff34ethurlow	for (tmp = avl_first(&smb_ptd); tmp != NULL;
2544bff34ethurlow	    tmp = cpid) {
2554bff34ethurlow		cpid = AVL_NEXT(&smb_ptd, tmp);
2564bff34ethurlow		if (ioc_uid == (uid_t)-1 ||
2574bff34ethurlow		    ioc_uid == tmp->uid) {
2584bff34ethurlow			/*
2594bff34ethurlow			 * Delete the node.
2604bff34ethurlow			 */
26102d09e0Gordon Ross			smb_pkey_delete(tmp);
2624bff34ethurlow		}
2634bff34ethurlow	}
2644bff34ethurlow	mutex_exit(&smb_ptd_lock);
2654bff34ethurlow
2664bff34ethurlow	return (0);
2674bff34ethurlow}
2684bff34ethurlow
2694bff34ethurlow/*
2704bff34ethurlow * Add entry or modify existing.
2714bff34ethurlow * Check for existing entry..
2724bff34ethurlow * If present, delete.
2734bff34ethurlow * Now, add the new entry.
2744bff34ethurlow */
2754bff34ethurlowint
2764bff34ethurlowsmb_pkey_add(smbioc_pk_t *pk, cred_t *cr)
2774bff34ethurlow{
2784bff34ethurlow	avl_tree_t *t = &smb_ptd;
2794bff34ethurlow	avl_index_t	where;
2804bff34ethurlow	smb_passid_t *tmp, *cpid;
2814bff34ethurlow	int ret;
2824bff34ethurlow	uid_t uid;
2834bff34ethurlow
2844bff34ethurlow	uid = pk->pk_uid;
2854bff34ethurlow	if (uid == (uid_t)-1)
2864bff34ethurlow		uid = crgetruid(cr);
2874bff34ethurlow	else {
2884bff34ethurlow		if (secpolicy_smbfs_login(cr, uid))
2894bff34ethurlow			return (EPERM);
2904bff34ethurlow	}
2914bff34ethurlow	cpid = kmem_zalloc(sizeof (smb_passid_t), KM_SLEEP);
2924bff34ethurlow	cpid->uid = uid;
2934bff34ethurlow	cpid->zoneid = getzoneid();
294613a2f6Gordon Ross	cpid->srvdom = strdup(pk->pk_dom);
295613a2f6Gordon Ross	cpid->username = strdup(pk->pk_usr);
296613a2f6Gordon Ross	bcopy(pk->pk_lmhash, cpid->lmhash, SMBIOC_HASH_SZ);
297613a2f6Gordon Ross	bcopy(pk->pk_nthash, cpid->nthash, SMBIOC_HASH_SZ);
2984bff34ethurlow
2994bff34ethurlow	/*
3004bff34ethurlow	 * XXX: Instead of calling smb_pkey_check here,
3014bff34ethurlow	 * should call avl_find directly, and hold the
3024bff34ethurlow	 * lock across: avl_find, avl_remove, avl_insert.
3034bff34ethurlow	 */
3044bff34ethurlow
3054bff34ethurlow	/* If it already exists, delete it. */
3064bff34ethurlow	ret = smb_pkey_check(pk, cr);
3074bff34ethurlow	if (ret == 0) {
30802d09e0Gordon Ross		(void) smb_pkey_del(pk, cr);
3094bff34ethurlow	}
3104bff34ethurlow
3114bff34ethurlow	mutex_enter(&smb_ptd_lock);
3124bff34ethurlow	tmp = (smb_passid_t *)avl_find(t, cpid, &where);
3134bff34ethurlow	if (tmp == NULL) {
3144bff34ethurlow		avl_insert(t, cpid, where);
3154bff34ethurlow	} else {
316613a2f6Gordon Ross		strfree(cpid->srvdom);
317613a2f6Gordon Ross		strfree(cpid->username);
3184bff34ethurlow		kmem_free(cpid, sizeof (smb_passid_t));
3194bff34ethurlow	}
3204bff34ethurlow	mutex_exit(&smb_ptd_lock);
3214bff34ethurlow
3224bff34ethurlow	return (0);
3234bff34ethurlow}
3244bff34ethurlow
3254bff34ethurlow/*
3264bff34ethurlow * Determine if a node with uid,zoneid, uname & dname exists in the tree
327613a2f6Gordon Ross * given the information, and if found, return the hashes.
3284bff34ethurlow */
3294bff34ethurlowint
3304bff34ethurlowsmb_pkey_check(smbioc_pk_t *pk, cred_t *cr)
3314bff34ethurlow{
3324bff34ethurlow	avl_tree_t *t = &smb_ptd;
3334bff34ethurlow	avl_index_t	where;
3344bff34ethurlow	smb_passid_t *tmp, *cpid;
3354bff34ethurlow	int error = ENOENT;
3364bff34ethurlow	uid_t uid;
3374bff34ethurlow
3384bff34ethurlow	uid = pk->pk_uid;
3394bff34ethurlow	if (uid == (uid_t)-1)
3404bff34ethurlow		uid = crgetruid(cr);
3414bff34ethurlow	else {
3424bff34ethurlow		if (secpolicy_smbfs_login(cr, uid))
3434bff34ethurlow			return (EPERM);
3444bff34ethurlow	}
3454bff34ethurlow	cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP);
3464bff34ethurlow	cpid->uid = uid;
3474bff34ethurlow	cpid->zoneid = getzoneid();
3484bff34ethurlow	cpid->srvdom = pk->pk_dom;
3494bff34ethurlow	cpid->username = pk->pk_usr;
3504bff34ethurlow
3514bff34ethurlow	mutex_enter(&smb_ptd_lock);
3524bff34ethurlow	tmp = (smb_passid_t *)avl_find(t, cpid, &where);
3534bff34ethurlow	mutex_exit(&smb_ptd_lock);
3544bff34ethurlow
355613a2f6Gordon Ross	if (tmp != NULL) {
356613a2f6Gordon Ross		bcopy(tmp->lmhash, pk->pk_lmhash, SMBIOC_HASH_SZ);
357613a2f6Gordon Ross		bcopy(tmp->nthash, pk->pk_nthash, SMBIOC_HASH_SZ);
358613a2f6Gordon Ross		error = 0;
359613a2f6Gordon Ross	}
360613a2f6Gordon Ross
3614bff34ethurlow	kmem_free(cpid, sizeof (smb_passid_t));
3624bff34ethurlow	return (error);
3634bff34ethurlow}
3644bff34ethurlow
365613a2f6Gordon Ross
3664bff34ethurlowint
367613a2f6Gordon Rosssmb_pkey_ioctl(int cmd, intptr_t arg, int flags, cred_t *cr)
3684bff34ethurlow{
369613a2f6Gordon Ross	smbioc_pk_t  *pk;
370613a2f6Gordon Ross	uid_t uid;
371613a2f6Gordon Ross	int err = 0;
372613a2f6Gordon Ross
373613a2f6Gordon Ross	pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
374613a2f6Gordon Ross
375613a2f6Gordon Ross	switch (cmd) {
376613a2f6Gordon Ross	case SMBIOC_PK_ADD:
377613a2f6Gordon Ross	case SMBIOC_PK_DEL:
378613a2f6Gordon Ross	case SMBIOC_PK_CHK:
379613a2f6Gordon Ross		if (ddi_copyin((void *)arg, pk,
380613a2f6Gordon Ross		    sizeof (*pk), flags)) {
381613a2f6Gordon Ross			err = EFAULT;
382613a2f6Gordon Ross			goto out;
383613a2f6Gordon Ross		}
384613a2f6Gordon Ross		/* Make strlen (etc) on these safe. */
385613a2f6Gordon Ross		pk->pk_dom[SMBIOC_MAX_NAME-1] = '\0';
386613a2f6Gordon Ross		pk->pk_usr[SMBIOC_MAX_NAME-1] = '\0';
387613a2f6Gordon Ross		break;
388613a2f6Gordon Ross	}
3894bff34ethurlow
390613a2f6Gordon Ross	switch (cmd) {
391613a2f6Gordon Ross	case SMBIOC_PK_ADD:
392613a2f6Gordon Ross		err = smb_pkey_add(pk, cr);
393613a2f6Gordon Ross		break;
3944bff34ethurlow
395613a2f6Gordon Ross	case SMBIOC_PK_DEL:
396613a2f6Gordon Ross		err = smb_pkey_del(pk, cr);
397613a2f6Gordon Ross		break;
3984bff34ethurlow
399613a2f6Gordon Ross	case SMBIOC_PK_CHK:
400613a2f6Gordon Ross		err = smb_pkey_check(pk, cr);
401613a2f6Gordon Ross		/* This is just a hash now. */
402613a2f6Gordon Ross		(void) ddi_copyout(pk, (void *)arg,
403613a2f6Gordon Ross		    sizeof (*pk), flags);
404613a2f6Gordon Ross		break;
405613a2f6Gordon Ross
406613a2f6Gordon Ross	case SMBIOC_PK_DEL_OWNER:
407613a2f6Gordon Ross		uid = crgetruid(cr);
408613a2f6Gordon Ross		err = smb_pkey_deluid(uid, cr);
409613a2f6Gordon Ross		break;
410613a2f6Gordon Ross
411613a2f6Gordon Ross	case SMBIOC_PK_DEL_EVERYONE:
412613a2f6Gordon Ross		uid = (uid_t)-1;
413613a2f6Gordon Ross		err = smb_pkey_deluid(uid, cr);
414613a2f6Gordon Ross		break;
415613a2f6Gordon Ross
416613a2f6Gordon Ross	default:
417613a2f6Gordon Ross		err = ENODEV;
4184bff34ethurlow	}
4194bff34ethurlow
420613a2f6Gordon Rossout:
421613a2f6Gordon Ross	kmem_free(pk, sizeof (*pk));
422613a2f6Gordon Ross	return (err);
4234bff34ethurlow}
424