#include <smbsrv/smb_kproto.h>
static void smb_encode_sacl(mbuf_chain_t *, smb_acl_t *);
static void smb_encode_dacl(mbuf_chain_t *, smb_acl_t *);
static smb_acl_t *smb_decode_acl(mbuf_chain_t *, uint32_t);
smb_sdrc_t
smb_nt_transact_query_security_info(struct smb_request *sr, struct smb_xa *xa)
{
smb_sd_t sd;
uint32_t secinfo;
uint32_t sdlen;
uint32_t status;
smb_error_t err;
if (smb_mbc_decodef(&xa->req_param_mb, "w2.l",
&sr->smb_fid, &secinfo) != 0) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
return (SDRC_ERROR);
}
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
return (SDRC_ERROR);
}
if ((sr->fid_ofile->f_node == NULL) ||
(sr->fid_ofile->f_ftype != SMB_FTYPE_DISK)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
ERRDOS, ERROR_ACCESS_DENIED);
return (SDRC_ERROR);
}
sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
if (sr->tid_tree->t_acltype != ACE_T) {
secinfo &= ~SMB_SACL_SECINFO;
}
status = smb_sd_read(sr, &sd, secinfo);
if (status != NT_STATUS_SUCCESS) {
smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
}
sdlen = smb_sd_len(&sd, secinfo);
if (sdlen == 0) {
smb_sd_term(&sd);
smbsr_error(sr, NT_STATUS_INVALID_SECURITY_DESCR, 0, 0);
return (SDRC_ERROR);
}
if (sdlen > xa->smb_mdrcnt) {
(void) smb_mbc_encodef(&xa->rep_param_mb, "l", sdlen);
err.status = NT_STATUS_BUFFER_TOO_SMALL;
err.errcls = ERRDOS;
err.errcode = ERROR_INSUFFICIENT_BUFFER;
smbsr_set_error(sr, &err);
smb_sd_term(&sd);
return (SDRC_SUCCESS);
}
smb_encode_sd(&xa->rep_data_mb, &sd, secinfo);
(void) smb_mbc_encodef(&xa->rep_param_mb, "l", sdlen);
smb_sd_term(&sd);
return (SDRC_SUCCESS);
}
smb_sdrc_t
smb_nt_transact_set_security_info(struct smb_request *sr, struct smb_xa *xa)
{
smb_sd_t sd;
uint32_t secinfo;
uint32_t status;
if (smb_mbc_decodef(&xa->req_param_mb, "w2.l",
&sr->smb_fid, &secinfo) != 0) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
return (SDRC_ERROR);
}
smbsr_lookup_file(sr);
if (sr->fid_ofile == NULL) {
smbsr_error(sr, NT_STATUS_INVALID_HANDLE, ERRDOS, ERRbadfid);
return (SDRC_ERROR);
}
if ((sr->fid_ofile->f_node == NULL) ||
(sr->fid_ofile->f_ftype != SMB_FTYPE_DISK)) {
smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 0, 0);
return (SDRC_ERROR);
}
sr->user_cr = smb_ofile_getcred(sr->fid_ofile);
if (SMB_TREE_IS_READONLY(sr)) {
smbsr_error(sr, NT_STATUS_MEDIA_WRITE_PROTECTED, 0, 0);
return (SDRC_ERROR);
}
if (sr->tid_tree->t_acltype != ACE_T) {
secinfo &= ~SMB_SACL_SECINFO;
}
if ((secinfo & SMB_ALL_SECINFO) == 0) {
return (NT_STATUS_SUCCESS);
}
status = smb_decode_sd(&xa->req_data_mb, &sd);
if (status != NT_STATUS_SUCCESS) {
smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
}
if (((secinfo & SMB_OWNER_SECINFO) && (sd.sd_owner == NULL)) ||
((secinfo & SMB_GROUP_SECINFO) && (sd.sd_group == NULL))) {
smbsr_error(sr, NT_STATUS_INVALID_PARAMETER, 0, 0);
return (SDRC_ERROR);
}
if (!smb_node_is_system(sr->fid_ofile->f_node))
status = smb_sd_write(sr, &sd, secinfo);
smb_sd_term(&sd);
if (status != NT_STATUS_SUCCESS) {
smbsr_error(sr, status, 0, 0);
return (SDRC_ERROR);
}
return (SDRC_SUCCESS);
}
void
smb_encode_sd(mbuf_chain_t *mbc, smb_sd_t *sd, uint32_t secinfo)
{
uint32_t offset = SMB_SD_HDRSIZE;
(void) smb_mbc_encodef(mbc, "b.w",
sd->sd_revision, sd->sd_control | SE_SELF_RELATIVE);
if (secinfo & SMB_OWNER_SECINFO) {
ASSERT(sd->sd_owner);
(void) smb_mbc_encodef(mbc, "l", offset);
offset += smb_sid_len(sd->sd_owner);
} else {
(void) smb_mbc_encodef(mbc, "l", 0);
}
if (secinfo & SMB_GROUP_SECINFO) {
ASSERT(sd->sd_group);
(void) smb_mbc_encodef(mbc, "l", offset);
offset += smb_sid_len(sd->sd_group);
} else {
(void) smb_mbc_encodef(mbc, "l", 0);
}
if ((secinfo & SMB_SACL_SECINFO) && (sd->sd_sacl)) {
(void) smb_mbc_encodef(mbc, "l", offset);
offset += smb_acl_len(sd->sd_sacl);
} else {
(void) smb_mbc_encodef(mbc, "l", 0);
}
if ((secinfo & SMB_DACL_SECINFO) && (sd->sd_dacl))
(void) smb_mbc_encodef(mbc, "l", offset);
else
(void) smb_mbc_encodef(mbc, "l", 0);
if (secinfo & SMB_OWNER_SECINFO)
smb_encode_sid(mbc, sd->sd_owner);
if (secinfo & SMB_GROUP_SECINFO)
smb_encode_sid(mbc, sd->sd_group);
if (secinfo & SMB_SACL_SECINFO)
smb_encode_sacl(mbc, sd->sd_sacl);
if (secinfo & SMB_DACL_SECINFO)
smb_encode_dacl(mbc, sd->sd_dacl);
}
void
smb_encode_sid(mbuf_chain_t *mbc, smb_sid_t *sid)
{
int i;
(void) smb_mbc_encodef(mbc, "bb",
sid->sid_revision, sid->sid_subauthcnt);
for (i = 0; i < NT_SID_AUTH_MAX; i++) {
(void) smb_mbc_encodef(mbc, "b",
sid->sid_authority[i]);
}
for (i = 0; i < sid->sid_subauthcnt; i++) {
(void) smb_mbc_encodef(mbc, "l",
sid->sid_subauth[i]);
}
}
static void
smb_encode_sacl(mbuf_chain_t *mbc, smb_acl_t *acl)
{
smb_ace_t *ace;
int i;
if (acl == NULL)
return;
(void) smb_mbc_encodef(mbc, "b.ww2.", acl->sl_revision,
acl->sl_bsize, acl->sl_acecnt);
for (i = 0, ace = acl->sl_aces; i < acl->sl_acecnt; i++, ace++) {
(void) smb_mbc_encodef(mbc, "bbwl",
ace->se_hdr.se_type, ace->se_hdr.se_flags,
ace->se_hdr.se_bsize, ace->se_mask);
smb_encode_sid(mbc, ace->se_sid);
}
}
static void
smb_encode_dacl(mbuf_chain_t *mbc, smb_acl_t *acl)
{
smb_ace_t *ace;
if (acl == NULL)
return;
(void) smb_mbc_encodef(mbc, "b.ww2.", acl->sl_revision,
acl->sl_bsize, acl->sl_acecnt);
ace = list_head(&acl->sl_sorted);
while (ace) {
(void) smb_mbc_encodef(mbc, "bbwl",
ace->se_hdr.se_type, ace->se_hdr.se_flags,
ace->se_hdr.se_bsize, ace->se_mask);
smb_encode_sid(mbc, ace->se_sid);
ace = list_next(&acl->sl_sorted, ace);
}
}
uint32_t
smb_decode_sd(mbuf_chain_t *mbc, smb_sd_t *sd)
{
struct mbuf_chain sdbuf;
uint32_t owner_offs;
uint32_t group_offs;
uint32_t sacl_offs;
uint32_t dacl_offs;
int rc;
smb_sd_init(sd, SECURITY_DESCRIPTOR_REVISION);
(void) MBC_SHADOW_CHAIN(&sdbuf, mbc,
mbc->chain_offset,
mbc->max_bytes - mbc->chain_offset);
rc = smb_mbc_decodef(&sdbuf, "b.wllll",
&sd->sd_revision, &sd->sd_control,
&owner_offs, &group_offs, &sacl_offs, &dacl_offs);
sd->sd_control &= ~SE_SELF_RELATIVE;
if (rc != 0)
goto decode_error;
if (owner_offs != 0) {
if (owner_offs < SMB_SD_HDRSIZE)
goto decode_error;
sd->sd_owner = smb_decode_sid(mbc, owner_offs);
if (sd->sd_owner == NULL)
goto decode_error;
}
if (group_offs != 0) {
if (group_offs < SMB_SD_HDRSIZE)
goto decode_error;
sd->sd_group = smb_decode_sid(mbc, group_offs);
if (sd->sd_group == NULL)
goto decode_error;
}
if (sacl_offs != 0) {
if ((sd->sd_control & SE_SACL_PRESENT) == 0)
goto decode_error;
if (sacl_offs < SMB_SD_HDRSIZE)
goto decode_error;
sd->sd_sacl = smb_decode_acl(mbc, sacl_offs);
if (sd->sd_sacl == NULL)
goto decode_error;
}
if (dacl_offs != 0) {
if ((sd->sd_control & SE_DACL_PRESENT) == 0)
goto decode_error;
if (dacl_offs < SMB_SD_HDRSIZE)
goto decode_error;
sd->sd_dacl = smb_decode_acl(mbc, dacl_offs);
if (sd->sd_dacl == NULL)
goto decode_error;
}
return (NT_STATUS_SUCCESS);
decode_error:
smb_sd_term(sd);
return (NT_STATUS_INVALID_SECURITY_DESCR);
}
smb_sid_t *
smb_decode_sid(mbuf_chain_t *mbc, uint32_t offset)
{
uint8_t revision;
uint8_t subauth_cnt;
struct mbuf_chain sidbuf;
smb_sid_t *sid;
int sidlen;
int bytes_left;
int i;
offset += mbc->chain_offset;
bytes_left = mbc->max_bytes - offset;
if (bytes_left < (int)sizeof (smb_sid_t))
return (NULL);
if (MBC_SHADOW_CHAIN(&sidbuf, mbc, offset, bytes_left) != 0)
return (NULL);
if (smb_mbc_decodef(&sidbuf, "bb", &revision, &subauth_cnt))
return (NULL);
sidlen = sizeof (smb_sid_t) - sizeof (uint32_t) +
(subauth_cnt * sizeof (uint32_t));
sid = kmem_alloc(sidlen, KM_SLEEP);
sid->sid_revision = revision;
sid->sid_subauthcnt = subauth_cnt;
for (i = 0; i < NT_SID_AUTH_MAX; i++) {
if (smb_mbc_decodef(&sidbuf, "b", &sid->sid_authority[i]))
goto decode_err;
}
for (i = 0; i < sid->sid_subauthcnt; i++) {
if (smb_mbc_decodef(&sidbuf, "l", &sid->sid_subauth[i]))
goto decode_err;
}
return (sid);
decode_err:
kmem_free(sid, sidlen);
return (NULL);
}
static smb_acl_t *
smb_decode_acl(mbuf_chain_t *mbc, uint32_t offset)
{
struct mbuf_chain aclbuf;
smb_acl_t *acl;
smb_ace_t *ace;
uint8_t revision;
uint16_t size;
uint16_t acecnt;
int bytes_left;
uint32_t sid_offs = offset;
int sidlen;
int i;
offset += mbc->chain_offset;
bytes_left = mbc->max_bytes - offset;
if (bytes_left < SMB_ACL_HDRSIZE)
return (NULL);
if (MBC_SHADOW_CHAIN(&aclbuf, mbc, offset, bytes_left) != 0)
return (NULL);
if (smb_mbc_decodef(&aclbuf, "b.ww2.", &revision, &size, &acecnt))
return (NULL);
if (size == 0)
return (NULL);
acl = smb_acl_alloc(revision, size, acecnt);
sid_offs += SMB_ACL_HDRSIZE;
for (i = 0, ace = acl->sl_aces; i < acl->sl_acecnt; i++, ace++) {
if (smb_mbc_decodef(&aclbuf, "bbwl",
&ace->se_hdr.se_type, &ace->se_hdr.se_flags,
&ace->se_hdr.se_bsize, &ace->se_mask))
goto decode_error;
sid_offs += SMB_ACE_HDRSIZE + sizeof (ace->se_mask);
ace->se_sid = smb_decode_sid(mbc, sid_offs);
if (ace->se_sid == NULL)
goto decode_error;
sidlen = ace->se_hdr.se_bsize -
(SMB_ACE_HDRSIZE + sizeof (ace->se_mask));
aclbuf.chain_offset += sidlen;
sid_offs += sidlen;
}
return (acl);
decode_error:
smb_acl_free(acl);
return (NULL);
}