#include <strings.h>
#include <assert.h>
#include <errno.h>
#include <smbsrv/ntifs.h>
#include <smbsrv/smb_idmap.h>
#include <smbsrv/libsmb.h>
#define SMB_SHR_ACE_READ_PERMS (ACE_READ_PERMS | ACE_EXECUTE | ACE_SYNCHRONIZE)
#define SMB_SHR_ACE_CONTROL_PERMS (ACE_MODIFY_PERMS & (~ACE_DELETE_CHILD))
static struct {
int am_ace_perms;
int am_share_perms;
} smb_ace_map[] = {
{ ACE_MODIFY_PERMS, SMB_SHR_ACE_CONTROL_PERMS },
{ ACE_READ_PERMS, SMB_SHR_ACE_READ_PERMS }
};
#define SMB_ACE_MASK_MAP_SIZE (sizeof (smb_ace_map)/sizeof (smb_ace_map[0]))
static void smb_sd_set_sacl(smb_sd_t *, smb_acl_t *, boolean_t, int);
static void smb_sd_set_dacl(smb_sd_t *, smb_acl_t *, boolean_t, int);
void
smb_sd_init(smb_sd_t *sd, uint8_t revision)
{
bzero(sd, sizeof (smb_sd_t));
sd->sd_revision = revision;
}
void
smb_sd_term(smb_sd_t *sd)
{
assert(sd);
assert((sd->sd_control & SE_SELF_RELATIVE) == 0);
smb_sid_free(sd->sd_owner);
smb_sid_free(sd->sd_group);
smb_acl_free(sd->sd_dacl);
smb_acl_free(sd->sd_sacl);
bzero(sd, sizeof (smb_sd_t));
}
uint32_t
smb_sd_len(smb_sd_t *sd, uint32_t secinfo)
{
uint32_t length = SMB_SD_HDRSIZE;
if (secinfo & SMB_OWNER_SECINFO)
length += smb_sid_len(sd->sd_owner);
if (secinfo & SMB_GROUP_SECINFO)
length += smb_sid_len(sd->sd_group);
if (secinfo & SMB_DACL_SECINFO)
length += smb_acl_len(sd->sd_dacl);
if (secinfo & SMB_SACL_SECINFO)
length += smb_acl_len(sd->sd_sacl);
return (length);
}
uint32_t
smb_sd_get_secinfo(smb_sd_t *sd)
{
uint32_t sec_info = 0;
if (sd == NULL)
return (0);
if (sd->sd_owner)
sec_info |= SMB_OWNER_SECINFO;
if (sd->sd_group)
sec_info |= SMB_GROUP_SECINFO;
if (sd->sd_dacl)
sec_info |= SMB_DACL_SECINFO;
if (sd->sd_sacl)
sec_info |= SMB_SACL_SECINFO;
return (sec_info);
}
static int
smb_sd_adjust_read_mask(int mask)
{
int i;
for (i = 0; i < SMB_ACE_MASK_MAP_SIZE; ++i) {
if (smb_ace_map[i].am_ace_perms == mask)
return (smb_ace_map[i].am_share_perms);
}
return (mask);
}
static uint32_t
smb_sd_read_acl(char *path, smb_fssd_t *fs_sd)
{
acl_t *z_acl;
ace_t *z_ace;
fs_sd->sd_gid = fs_sd->sd_uid = 0;
errno = 0;
if (acl_get(path, 0, &z_acl) != 0) {
switch (errno) {
case EACCES:
return (NT_STATUS_ACCESS_DENIED);
case ENOENT:
return (NT_STATUS_OBJECT_PATH_NOT_FOUND);
default:
return (NT_STATUS_INTERNAL_ERROR);
}
}
if ((z_ace = (ace_t *)z_acl->acl_aclp) == NULL)
return (NT_STATUS_INVALID_ACL);
for (int i = 0; i < z_acl->acl_cnt; i++, z_ace++)
z_ace->a_access_mask =
smb_sd_adjust_read_mask(z_ace->a_access_mask);
fs_sd->sd_zdacl = z_acl;
fs_sd->sd_zsacl = NULL;
return (NT_STATUS_SUCCESS);
}
uint32_t
smb_sd_read(char *path, smb_sd_t *sd, uint32_t secinfo)
{
smb_fssd_t fs_sd;
uint32_t status = NT_STATUS_SUCCESS;
uint32_t sd_flags;
int error;
sd_flags = SMB_FSSD_FLAGS_DIR;
smb_fssd_init(&fs_sd, secinfo, sd_flags);
error = smb_sd_read_acl(path, &fs_sd);
if (error != NT_STATUS_SUCCESS) {
smb_fssd_term(&fs_sd);
return (error);
}
status = smb_sd_fromfs(&fs_sd, sd);
smb_fssd_term(&fs_sd);
return (status);
}
static uint32_t
smb_sd_write_acl(char *path, smb_fssd_t *fs_sd)
{
acl_t *z_acl;
ace_t *z_ace;
uint32_t status = NT_STATUS_SUCCESS;
z_acl = fs_sd->sd_zdacl;
if (z_acl == NULL)
return (NT_STATUS_INVALID_ACL);
z_ace = (ace_t *)z_acl->acl_aclp;
if (z_ace == NULL)
return (NT_STATUS_INVALID_ACL);
fs_sd->sd_gid = fs_sd->sd_uid = 0;
if (acl_set(path, z_acl) != 0)
status = NT_STATUS_INTERNAL_ERROR;
return (status);
}
uint32_t
smb_sd_write(char *path, smb_sd_t *sd, uint32_t secinfo)
{
smb_fssd_t fs_sd;
uint32_t status = NT_STATUS_SUCCESS;
uint32_t sd_flags;
int error;
sd_flags = SMB_FSSD_FLAGS_DIR;
smb_fssd_init(&fs_sd, secinfo, sd_flags);
error = smb_sd_tofs(sd, &fs_sd);
if (error != NT_STATUS_SUCCESS) {
smb_fssd_term(&fs_sd);
return (error);
}
status = smb_sd_write_acl(path, &fs_sd);
smb_fssd_term(&fs_sd);
return (status);
}
uint32_t
smb_sd_tofs(smb_sd_t *sd, smb_fssd_t *fs_sd)
{
smb_sid_t *sid;
uint32_t status = NT_STATUS_SUCCESS;
uint16_t sd_control;
idmap_stat idm_stat;
int idtype;
int flags = 0;
sd_control = sd->sd_control;
if (sd_control & SE_DACL_DEFAULTED)
flags |= ACL_DEFAULTED;
if (sd_control & SE_DACL_AUTO_INHERITED)
flags |= ACL_AUTO_INHERIT;
if (sd_control & SE_DACL_PROTECTED)
flags |= ACL_PROTECTED;
if (fs_sd->sd_flags & SMB_FSSD_FLAGS_DIR)
flags |= ACL_IS_DIR;
if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
sid = sd->sd_owner;
if (!smb_sid_isvalid(sid))
return (NT_STATUS_INVALID_SID);
idtype = SMB_IDMAP_USER;
idm_stat = smb_idmap_getid(sid, &fs_sd->sd_uid, &idtype);
if (idm_stat != IDMAP_SUCCESS) {
return (NT_STATUS_NONE_MAPPED);
}
}
if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) {
sid = sd->sd_group;
if (!smb_sid_isvalid(sid))
return (NT_STATUS_INVALID_SID);
idtype = SMB_IDMAP_GROUP;
idm_stat = smb_idmap_getid(sid, &fs_sd->sd_gid, &idtype);
if (idm_stat != IDMAP_SUCCESS) {
return (NT_STATUS_NONE_MAPPED);
}
}
if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) {
if (sd->sd_control & SE_DACL_PRESENT) {
status = smb_acl_to_zfs(sd->sd_dacl, flags,
SMB_DACL_SECINFO, &fs_sd->sd_zdacl);
if (status != NT_STATUS_SUCCESS)
return (status);
}
else
return (NT_STATUS_INVALID_ACL);
}
if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) {
if (sd->sd_control & SE_SACL_PRESENT) {
status = smb_acl_to_zfs(sd->sd_sacl, flags,
SMB_SACL_SECINFO, &fs_sd->sd_zsacl);
if (status != NT_STATUS_SUCCESS) {
return (status);
}
} else {
return (NT_STATUS_INVALID_ACL);
}
}
return (status);
}
uint32_t
smb_sd_fromfs(smb_fssd_t *fs_sd, smb_sd_t *sd)
{
uint32_t status = NT_STATUS_SUCCESS;
smb_acl_t *acl = NULL;
smb_sid_t *sid;
idmap_stat idm_stat;
assert(fs_sd);
assert(sd);
smb_sd_init(sd, SECURITY_DESCRIPTOR_REVISION);
if (fs_sd->sd_secinfo & SMB_OWNER_SECINFO) {
idm_stat = smb_idmap_getsid(fs_sd->sd_uid,
SMB_IDMAP_USER, &sid);
if (idm_stat != IDMAP_SUCCESS) {
smb_sd_term(sd);
return (NT_STATUS_NONE_MAPPED);
}
sd->sd_owner = sid;
}
if (fs_sd->sd_secinfo & SMB_GROUP_SECINFO) {
idm_stat = smb_idmap_getsid(fs_sd->sd_gid,
SMB_IDMAP_GROUP, &sid);
if (idm_stat != IDMAP_SUCCESS) {
smb_sd_term(sd);
return (NT_STATUS_NONE_MAPPED);
}
sd->sd_group = sid;
}
if (fs_sd->sd_secinfo & SMB_DACL_SECINFO) {
if (fs_sd->sd_zdacl != NULL) {
acl = smb_acl_from_zfs(fs_sd->sd_zdacl);
if (acl == NULL) {
smb_sd_term(sd);
return (NT_STATUS_INTERNAL_ERROR);
}
smb_acl_sort(acl);
smb_sd_set_dacl(sd, acl, B_TRUE,
fs_sd->sd_zdacl->acl_flags);
} else {
smb_sd_set_dacl(sd, NULL, B_FALSE, 0);
}
}
if (fs_sd->sd_secinfo & SMB_SACL_SECINFO) {
if (fs_sd->sd_zsacl != NULL) {
acl = smb_acl_from_zfs(fs_sd->sd_zsacl);
if (acl == NULL) {
smb_sd_term(sd);
return (NT_STATUS_INTERNAL_ERROR);
}
smb_sd_set_sacl(sd, acl, B_TRUE,
fs_sd->sd_zsacl->acl_flags);
} else {
smb_sd_set_sacl(sd, NULL, B_FALSE, 0);
}
}
return (status);
}
static void
smb_sd_set_dacl(smb_sd_t *sd, smb_acl_t *acl, boolean_t present, int flags)
{
assert((sd->sd_control & SE_SELF_RELATIVE) == 0);
sd->sd_dacl = acl;
if (flags & ACL_DEFAULTED)
sd->sd_control |= SE_DACL_DEFAULTED;
if (flags & ACL_AUTO_INHERIT)
sd->sd_control |= SE_DACL_AUTO_INHERITED;
if (flags & ACL_PROTECTED)
sd->sd_control |= SE_DACL_PROTECTED;
if (present)
sd->sd_control |= SE_DACL_PRESENT;
}
static void
smb_sd_set_sacl(smb_sd_t *sd, smb_acl_t *acl, boolean_t present, int flags)
{
assert((sd->sd_control & SE_SELF_RELATIVE) == 0);
sd->sd_sacl = acl;
if (flags & ACL_DEFAULTED)
sd->sd_control |= SE_SACL_DEFAULTED;
if (flags & ACL_AUTO_INHERIT)
sd->sd_control |= SE_SACL_AUTO_INHERITED;
if (flags & ACL_PROTECTED)
sd->sd_control |= SE_SACL_PROTECTED;
if (present)
sd->sd_control |= SE_SACL_PRESENT;
}
void
smb_fssd_init(smb_fssd_t *fs_sd, uint32_t secinfo, uint32_t flags)
{
bzero(fs_sd, sizeof (smb_fssd_t));
fs_sd->sd_secinfo = secinfo;
fs_sd->sd_flags = flags;
}
void
smb_fssd_term(smb_fssd_t *fs_sd)
{
assert(fs_sd);
acl_free(fs_sd->sd_zdacl);
acl_free(fs_sd->sd_zsacl);
bzero(fs_sd, sizeof (smb_fssd_t));
}