#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/sysmacros.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/modctl.h>
#include <sys/open.h>
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunldi.h>
#include <sys/policy.h>
#include <sys/zone.h>
#include <sys/pathname.h>
#include <sys/mount.h>
#include <sys/sdt.h>
#include <fs/fs_subr.h>
#include <sys/devops.h>
#include <sys/thread.h>
#include <sys/mkdev.h>
#include <sys/avl.h>
#include <sys/avl_impl.h>
#include <sys/u8_textprep.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_dev.h>
#include <netsmb/smb_pass.h>
avl_tree_t smb_ptd;
unsigned int smb_list_len = 0;
kmutex_t smb_ptd_lock;
int smb_pkey_check(smbioc_pk_t *pk, cred_t *cr);
int smb_pkey_deluid(uid_t ioc_uid, cred_t *cr);
int
smb_pkey_cmp(const void *a, const void *b)
{
const smb_passid_t *pa = (smb_passid_t *)a;
const smb_passid_t *pb = (smb_passid_t *)b;
int duser, dsrv, error;
ASSERT(MUTEX_HELD(&smb_ptd_lock));
if (pa->uid < pb->uid)
return (-1);
if (pa->uid > pb->uid)
return (+1);
if (pa->zoneid < pb->zoneid)
return (-1);
if (pa->zoneid > pb->zoneid)
return (+1);
dsrv = u8_strcmp(pa->srvdom, pb->srvdom, 0,
U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error);
if (dsrv < 0)
return (-1);
if (dsrv > 0)
return (+1);
duser = u8_strcmp(pa->username, pb->username, 0,
U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error);
if (duser < 0)
return (-1);
if (duser > 0)
return (+1);
return (0);
}
void
smb_pkey_init()
{
avl_create(&smb_ptd,
smb_pkey_cmp,
sizeof (smb_passid_t),
offsetof(smb_passid_t,
cpnode));
mutex_init(&smb_ptd_lock, NULL, MUTEX_DEFAULT, NULL);
}
void
smb_pkey_fini()
{
(void) smb_pkey_deluid((uid_t)-1, kcred);
avl_destroy(&smb_ptd);
mutex_destroy(&smb_ptd_lock);
}
int
smb_pkey_idle()
{
int n;
mutex_enter(&smb_ptd_lock);
n = avl_numnodes(&smb_ptd);
mutex_exit(&smb_ptd_lock);
return ((n) ? EBUSY : 0);
}
static void
smb_pkey_delete(smb_passid_t *tmp)
{
ASSERT(MUTEX_HELD(&smb_ptd_lock));
avl_remove(&smb_ptd, tmp);
strfree(tmp->srvdom);
strfree(tmp->username);
kmem_free(tmp, sizeof (*tmp));
}
int
smb_pkey_del(smbioc_pk_t *pk, cred_t *cr)
{
avl_index_t where;
smb_passid_t buf, *cpid, *tmp;
uid_t uid;
tmp = &buf;
uid = pk->pk_uid;
if (uid == (uid_t)-1)
uid = crgetruid(cr);
else {
if (secpolicy_smbfs_login(cr, uid))
return (EPERM);
}
tmp->uid = uid;
tmp->zoneid = getzoneid();
tmp->srvdom = pk->pk_dom;
tmp->username = pk->pk_usr;
mutex_enter(&smb_ptd_lock);
if ((cpid = (smb_passid_t *)avl_find(&smb_ptd,
tmp, &where)) != NULL) {
smb_pkey_delete(cpid);
}
mutex_exit(&smb_ptd_lock);
return (0);
}
int
smb_pkey_deluid(uid_t ioc_uid, cred_t *cr)
{
smb_passid_t *cpid, *tmp;
if (secpolicy_smbfs_login(cr, ioc_uid))
return (EPERM);
mutex_enter(&smb_ptd_lock);
for (tmp = avl_first(&smb_ptd); tmp != NULL;
tmp = cpid) {
cpid = AVL_NEXT(&smb_ptd, tmp);
if (ioc_uid == (uid_t)-1 ||
ioc_uid == tmp->uid) {
smb_pkey_delete(tmp);
}
}
mutex_exit(&smb_ptd_lock);
return (0);
}
int
smb_pkey_add(smbioc_pk_t *pk, cred_t *cr)
{
avl_tree_t *t = &smb_ptd;
avl_index_t where;
smb_passid_t *tmp, *cpid;
int ret;
uid_t uid;
uid = pk->pk_uid;
if (uid == (uid_t)-1)
uid = crgetruid(cr);
else {
if (secpolicy_smbfs_login(cr, uid))
return (EPERM);
}
cpid = kmem_zalloc(sizeof (smb_passid_t), KM_SLEEP);
cpid->uid = uid;
cpid->zoneid = getzoneid();
cpid->srvdom = strdup(pk->pk_dom);
cpid->username = strdup(pk->pk_usr);
bcopy(pk->pk_lmhash, cpid->lmhash, SMBIOC_HASH_SZ);
bcopy(pk->pk_nthash, cpid->nthash, SMBIOC_HASH_SZ);
ret = smb_pkey_check(pk, cr);
if (ret == 0) {
(void) smb_pkey_del(pk, cr);
}
mutex_enter(&smb_ptd_lock);
tmp = (smb_passid_t *)avl_find(t, cpid, &where);
if (tmp == NULL) {
avl_insert(t, cpid, where);
} else {
strfree(cpid->srvdom);
strfree(cpid->username);
kmem_free(cpid, sizeof (smb_passid_t));
}
mutex_exit(&smb_ptd_lock);
return (0);
}
int
smb_pkey_check(smbioc_pk_t *pk, cred_t *cr)
{
avl_tree_t *t = &smb_ptd;
avl_index_t where;
smb_passid_t *tmp, *cpid;
int error = ENOENT;
uid_t uid;
uid = pk->pk_uid;
if (uid == (uid_t)-1)
uid = crgetruid(cr);
else {
if (secpolicy_smbfs_login(cr, uid))
return (EPERM);
}
cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP);
cpid->uid = uid;
cpid->zoneid = getzoneid();
cpid->srvdom = pk->pk_dom;
cpid->username = pk->pk_usr;
mutex_enter(&smb_ptd_lock);
tmp = (smb_passid_t *)avl_find(t, cpid, &where);
mutex_exit(&smb_ptd_lock);
if (tmp != NULL) {
bcopy(tmp->lmhash, pk->pk_lmhash, SMBIOC_HASH_SZ);
bcopy(tmp->nthash, pk->pk_nthash, SMBIOC_HASH_SZ);
error = 0;
}
kmem_free(cpid, sizeof (smb_passid_t));
return (error);
}
int
smb_pkey_ioctl(int cmd, intptr_t arg, int flags, cred_t *cr)
{
smbioc_pk_t *pk;
uid_t uid;
int err = 0;
pk = kmem_alloc(sizeof (*pk), KM_SLEEP);
switch (cmd) {
case SMBIOC_PK_ADD:
case SMBIOC_PK_DEL:
case SMBIOC_PK_CHK:
if (ddi_copyin((void *)arg, pk,
sizeof (*pk), flags)) {
err = EFAULT;
goto out;
}
pk->pk_dom[SMBIOC_MAX_NAME-1] = '\0';
pk->pk_usr[SMBIOC_MAX_NAME-1] = '\0';
break;
}
switch (cmd) {
case SMBIOC_PK_ADD:
err = smb_pkey_add(pk, cr);
break;
case SMBIOC_PK_DEL:
err = smb_pkey_del(pk, cr);
break;
case SMBIOC_PK_CHK:
err = smb_pkey_check(pk, cr);
(void) ddi_copyout(pk, (void *)arg,
sizeof (*pk), flags);
break;
case SMBIOC_PK_DEL_OWNER:
uid = crgetruid(cr);
err = smb_pkey_deluid(uid, cr);
break;
case SMBIOC_PK_DEL_EVERYONE:
uid = (uid_t)-1;
err = smb_pkey_deluid(uid, cr);
break;
default:
err = ENODEV;
}
out:
kmem_free(pk, sizeof (*pk));
return (err);
}