#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/sunddi.h>
#include <sys/acl.h>
#include <sys/vnode.h>
#include <sys/vfs.h>
#include <sys/byteorder.h>
#include <netsmb/mchain.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb_subr.h>
#include <smbfs/smbfs.h>
#include <smbfs/smbfs_node.h>
#include <smbfs/smbfs_subr.h>
#include <sys/fs/smbfs_ioctl.h>
#include <fs/fs_subr.h>
#include "smbfs_ntacl.h"
#define MAX_RAW_SD_SIZE 32768
#define SMALL_SD_SIZE 1024
static int
smbfs_getsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr)
{
struct smb_cred scred;
smbmntinfo_t *smi;
smbnode_t *np;
smb_fh_t *fid = NULL;
uint32_t sdlen = SMALL_SD_SIZE;
uint32_t rights = STD_RIGHT_READ_CONTROL_ACCESS;
int error;
if (selector & SACL_SECURITY_INFORMATION)
rights |= SEC_RIGHT_SYSTEM_SECURITY;
np = VTOSMB(vp);
smi = VTOSMI(vp);
smb_credinit(&scred, cr);
error = smbfs_smb_tmpopen(np, rights, &scred, &fid);
if (error)
goto out;
again:
error = smbfs_smb_getsec(smi->smi_share, fid,
selector, mp, &sdlen, &scred);
if (error == E2BIG &&
sdlen > SMALL_SD_SIZE &&
sdlen <= MAX_RAW_SD_SIZE)
goto again;
smbfs_smb_tmpclose(np, fid);
out:
smb_credrele(&scred);
return (error);
}
static int
smbfs_setsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr)
{
struct smb_cred scred;
smbmntinfo_t *smi;
smbnode_t *np;
uint32_t rights;
smb_fh_t *fid = NULL;
int error;
np = VTOSMB(vp);
smi = VTOSMI(vp);
if (selector == 0)
return (0);
rights = 0;
if (selector & (OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION))
rights |= STD_RIGHT_WRITE_OWNER_ACCESS;
if (selector & DACL_SECURITY_INFORMATION)
rights |= STD_RIGHT_WRITE_DAC_ACCESS;
if (selector & SACL_SECURITY_INFORMATION)
rights |= SEC_RIGHT_SYSTEM_SECURITY;
smb_credinit(&scred, cr);
error = smbfs_smb_tmpopen(np, rights, &scred, &fid);
if (error)
goto out;
mutex_enter(&np->r_statelock);
np->r_sectime = gethrtime();
mutex_exit(&np->r_statelock);
error = smbfs_smb_setsec(smi->smi_share, fid,
selector, mp, &scred);
smbfs_smb_tmpclose(np, fid);
out:
smb_credrele(&scred);
return (error);
}
int
smbfs_acl_iocget(vnode_t *vp, intptr_t arg, int flag, cred_t *cr)
{
ioc_sdbuf_t iocb;
mdchain_t *mdp, md_store;
mblk_t *m;
void *ubuf;
int error;
if (ddi_copyin((void *)arg, &iocb, sizeof (iocb), flag))
return (EFAULT);
error = smbfs_getsd(vp, iocb.selector, &m, cr);
if (error)
return (error);
mdp = &md_store;
md_initm(mdp, m);
iocb.used = m_fixhdr(m);
if (ddi_copyout(&iocb, (void *)arg, sizeof (iocb), flag)) {
error = EFAULT;
goto out;
}
if (iocb.used > iocb.alloc) {
error = EOVERFLOW;
goto out;
}
ubuf = (void *)(uintptr_t)iocb.addr;
error = md_get_mem(mdp, ubuf, iocb.used, MB_MUSER);
out:
md_done(mdp);
return (error);
}
int
smbfs_acl_iocset(vnode_t *vp, intptr_t arg, int flag, cred_t *cr)
{
ioc_sdbuf_t iocb;
mbchain_t *mbp, mb_store;
void *ubuf;
int error;
if (ddi_copyin((void *)arg, &iocb, sizeof (iocb), flag))
return (EFAULT);
if (iocb.used < sizeof (ntsecdesc_t) ||
iocb.used >= MAX_RAW_SD_SIZE)
return (EINVAL);
mbp = &mb_store;
(void) mb_init(mbp);
ubuf = (void *)(uintptr_t)iocb.addr;
error = mb_put_mem(mbp, ubuf, iocb.used, MB_MUSER);
if (error)
goto out;
error = smbfs_setsd(vp, iocb.selector, &mbp->mb_top, cr);
out:
mb_done(mbp);
return (error);
}
static int
smbfs_acl_refresh(vnode_t *vp, cred_t *cr)
{
smbnode_t *np;
smbmntinfo_t *smi;
mdchain_t *mdp, md_store;
mblk_t *m = NULL;
i_ntsd_t *sd = NULL;
vsecattr_t vsa, ovsa;
uint32_t selector;
uid_t uid;
gid_t gid;
int error;
np = VTOSMB(vp);
smi = VTOSMI(vp);
bzero(&md_store, sizeof (md_store));
mdp = &md_store;
selector = DACL_SECURITY_INFORMATION |
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION;
error = smbfs_getsd(vp, selector, &m, cr);
if (error)
goto out;
md_initm(mdp, m);
error = md_get_ntsd(mdp, &sd);
if (error)
goto out;
bzero(&vsa, sizeof (vsa));
vsa.vsa_mask = VSA_ACE | VSA_ACECNT;
error = smbfs_acl_sd2zfs(sd, &vsa, &uid, &gid);
if (error)
goto out;
ASSERT(vsa.vsa_aclentp != NULL);
SMBVDEBUG("uid=%u, gid=%u", uid, gid);
mutex_enter(&np->r_statelock);
ovsa = np->r_secattr;
np->r_secattr = vsa;
np->n_uid = uid;
np->n_gid = gid;
np->r_sectime = gethrtime() + smi->smi_acdirmax;
mutex_exit(&np->r_statelock);
if (ovsa.vsa_aclentp != NULL)
kmem_free(ovsa.vsa_aclentp, ovsa.vsa_aclentsz);
out:
if (sd != NULL)
smbfs_acl_free_sd(sd);
md_done(mdp);
return (error);
}
int
smbfs_acl_getids(vnode_t *vp, cred_t *cr)
{
smbnode_t *np;
int error;
np = VTOSMB(vp);
if (np->n_flag & N_XATTR)
return (ENOSYS);
mutex_enter(&np->r_statelock);
if (gethrtime() >= np->r_sectime) {
mutex_exit(&np->r_statelock);
error = smbfs_acl_refresh(vp, cr);
return (error);
}
mutex_exit(&np->r_statelock);
return (0);
}
int
smbfs_acl_getvsa(vnode_t *vp, vsecattr_t *vsa,
int flag, cred_t *cr)
{
smbnode_t *np;
int error;
np = VTOSMB(vp);
if (np->n_flag & N_XATTR)
return (ENOSYS);
mutex_enter(&np->r_statelock);
if (np->r_secattr.vsa_aclentp == NULL ||
gethrtime() >= np->r_sectime) {
mutex_exit(&np->r_statelock);
error = smbfs_acl_refresh(vp, cr);
if (error)
return (error);
mutex_enter(&np->r_statelock);
}
ASSERT(np->r_secattr.vsa_aclentp != NULL);
if (vsa->vsa_mask & VSA_ACECNT)
vsa->vsa_aclcnt = np->r_secattr.vsa_aclcnt;
if (vsa->vsa_mask & VSA_ACE) {
vsa->vsa_aclentsz = np->r_secattr.vsa_aclentsz;
vsa->vsa_aclentp = kmem_alloc(vsa->vsa_aclentsz, KM_SLEEP);
bcopy(np->r_secattr.vsa_aclentp, vsa->vsa_aclentp,
vsa->vsa_aclentsz);
}
mutex_exit(&np->r_statelock);
return (0);
}
static int
smbfs_acl_store(vnode_t *vp, vsecattr_t *vsa, uid_t uid, gid_t gid,
uint32_t selector, cred_t *cr)
{
mbchain_t *mbp, mb_store;
i_ntsd_t *sd;
int error;
ASSERT(selector != 0);
sd = NULL;
bzero(&mb_store, sizeof (mb_store));
mbp = &mb_store;
error = smbfs_acl_zfs2sd(vsa, uid, gid, selector, &sd);
if (error)
goto out;
(void) mb_init(mbp);
error = mb_put_ntsd(mbp, sd);
if (error)
goto out;
error = smbfs_setsd(vp, selector, &mbp->mb_top, cr);
out:
if (sd != NULL)
smbfs_acl_free_sd(sd);
mb_done(mbp);
return (error);
}
int
smbfs_acl_setids(vnode_t *vp, vattr_t *vap, cred_t *cr)
{
uid_t uid = (uid_t)-1;
gid_t gid = (uid_t)-1;
uint32_t selector = 0;
int error;
if (vap->va_mask & AT_UID) {
selector |= OWNER_SECURITY_INFORMATION;
uid = vap->va_uid;
}
if (vap->va_mask & AT_GID) {
selector |= GROUP_SECURITY_INFORMATION;
gid = vap->va_gid;
}
if (selector == 0)
return (0);
error = smbfs_acl_store(vp, NULL, uid, gid, selector, cr);
return (error);
}
int
smbfs_acl_setvsa(vnode_t *vp, vsecattr_t *vsa,
int flag, cred_t *cr)
{
uint32_t selector = DACL_SECURITY_INFORMATION;
smbnode_t *np = VTOSMB(vp);
int error;
if (np->n_flag & N_XATTR)
return (ENOSYS);
error = smbfs_acl_getids(vp, cr);
if (error)
return (error);
error = smbfs_acl_store(vp, vsa, np->n_uid, np->n_gid, selector, cr);
return (error);
}