#include <sys/param.h>
#include <sys/random.h>
#include <sys/sunddi.h>
#include <sys/sysmacros.h>
#include <sys/sdt.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb.h>
#include <netsmb/smb2.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/mchain.h>
#include <netsmb/nsmb_kcrypt.h>
#define NEG_CTX_MAX_COUNT (16)
#define NEG_CTX_MAX_DATALEN (256)
enum smb2_neg_ctx_type {
SMB2_PREAUTH_INTEGRITY_CAPS = 1,
SMB2_ENCRYPTION_CAPS = 2,
SMB2_COMPRESSION_CAPS = 3,
SMB2_NETNAME_NEGOTIATE_CONTEXT_ID = 5
};
typedef struct smb2_negotiate_ctx {
uint16_t type;
uint16_t datalen;
} smb2_neg_ctx_t;
#define MAX_HASHID_NUM (1)
#define MAX_CIPHER_NUM (4)
#define SMB31_PREAUTH_CTX_SALT_LEN 32
typedef struct smb2_preauth_integrity_caps {
uint16_t picap_hash_count;
uint16_t picap_salt_len;
uint16_t picap_hash_id;
uint8_t picap_salt[SMB31_PREAUTH_CTX_SALT_LEN];
} smb2_preauth_caps_t;
typedef struct smb2_encryption_caps {
uint16_t encap_cipher_count;
uint16_t encap_cipher_ids[MAX_CIPHER_NUM];
} smb2_encrypt_caps_t;
typedef struct smb2_preauth_neg_ctx {
smb2_neg_ctx_t neg_ctx;
smb2_preauth_caps_t preauth_caps;
} smb2_preauth_neg_ctx_t;
typedef struct smb2_encrypt_neg_ctx {
smb2_neg_ctx_t neg_ctx;
smb2_encrypt_caps_t encrypt_caps;
} smb2_encrypt_neg_ctx_t;
typedef struct smb2_neg_ctxs {
uint32_t offset;
uint16_t count;
smb2_preauth_neg_ctx_t preauth_ctx;
smb2_encrypt_neg_ctx_t encrypt_ctx;
} smb2_neg_ctxs_t;
int nsmb_ciphers_enabled = 0xf;
int
smb3_negctxs_encode(struct smb_vc *vcp, struct mbchain *mbp,
uint32_t *negctx_off_p, uint16_t *negctx_cnt_p)
{
uint8_t salt[SMB31_PREAUTH_CTX_SALT_LEN];
size_t saltlen = sizeof (salt);
int start;
uint16_t *ccnt_p;
uint16_t *len_p;
uint16_t len;
uint16_t ctx_cnt = 0;
uint16_t ccnt = 0;
(void) random_get_pseudo_bytes(salt, saltlen);
mb_put_align8(mbp);
*negctx_off_p = htolel(mbp->mb_count);
mb_put_uint16le(mbp, SMB2_PREAUTH_INTEGRITY_CAPS);
len_p = mb_reserve(mbp, 2);
mb_put_uint32le(mbp, 0);
start = mbp->mb_count;
mb_put_uint16le(mbp, 1);
mb_put_uint16le(mbp, saltlen);
mb_put_uint16le(mbp, SMB3_HASH_SHA512);
mb_put_mem(mbp, salt, saltlen, MB_MSYSTEM);
len = mbp->mb_count - start;
*len_p = htoles(len);
ctx_cnt++;
mb_put_align8(mbp);
mb_put_uint16le(mbp, SMB2_ENCRYPTION_CAPS);
len_p = mb_reserve(mbp, 2);
mb_put_uint32le(mbp, 0);
start = mbp->mb_count;
ccnt_p = mb_reserve(mbp, 2);
if (nsmb_ciphers_enabled & (1 << (SMB3_CIPHER_AES256_GCM - 1))) {
mb_put_uint16le(mbp, SMB3_CIPHER_AES256_GCM);
ccnt++;
}
if (nsmb_ciphers_enabled & (1 << (SMB3_CIPHER_AES256_CCM - 1))) {
mb_put_uint16le(mbp, SMB3_CIPHER_AES256_CCM);
ccnt++;
}
if (nsmb_ciphers_enabled & (1 << (SMB3_CIPHER_AES128_GCM - 1))) {
mb_put_uint16le(mbp, SMB3_CIPHER_AES128_GCM);
ccnt++;
}
if (nsmb_ciphers_enabled & (1 << (SMB3_CIPHER_AES128_CCM - 1))) {
mb_put_uint16le(mbp, SMB3_CIPHER_AES128_CCM);
ccnt++;
}
*ccnt_p = htoles(ccnt);
len = mbp->mb_count - start;
*len_p = htoles(len);
ctx_cnt++;
*negctx_cnt_p = htoles(ctx_cnt);
return (0);
}
int
smb3_negctxs_decode(struct smb_vc *vcp, struct mdchain *mdp,
uint16_t negctx_cnt)
{
smb2_neg_ctxs_t negctxs_stor;
smb2_neg_ctxs_t *neg_ctxs = &negctxs_stor;
smb2_preauth_caps_t *picap = &neg_ctxs->preauth_ctx.preauth_caps;
smb2_encrypt_caps_t *encap = &neg_ctxs->encrypt_ctx.encrypt_caps;
uint16_t cipher = 0;
int found_preauth_ctx = 0;
int found_encrypt_ctx = 0;
int err = 0;
int rc;
bzero(neg_ctxs, sizeof (*neg_ctxs));
neg_ctxs->offset = md_tell(mdp);
neg_ctxs->count = negctx_cnt;
if (neg_ctxs->count < 1 || neg_ctxs->count > NEG_CTX_MAX_COUNT) {
err = EINVAL;
goto errout;
}
for (int i = 0; i < neg_ctxs->count; i++) {
smb2_neg_ctx_t neg_ctx;
uint32_t ctx_start;
uint32_t ctx_end_off;
uint32_t ctx_next_off;
ctx_start = md_tell(mdp);
if ((ctx_start % 8) != 0) {
err = EINVAL;
goto errout;
}
if (md_get_uint16le(mdp, &neg_ctx.type) != 0 ||
md_get_uint16le(mdp, &neg_ctx.datalen) != 0 ||
md_get_uint32le(mdp, NULL) != 0) {
err = EINVAL;
goto errout;
}
DTRACE_PROBE1(neg_ctx, smb2_neg_ctx_t *, &neg_ctx);
if (neg_ctx.datalen > NEG_CTX_MAX_DATALEN) {
err = EINVAL;
goto errout;
}
ctx_end_off = ctx_start + 8 + neg_ctx.datalen;
ctx_next_off = P2ROUNDUP(ctx_end_off, 8);
switch (neg_ctx.type) {
case SMB2_PREAUTH_INTEGRITY_CAPS:
memcpy(&neg_ctxs->preauth_ctx.neg_ctx, &neg_ctx,
sizeof (neg_ctx));
if (found_preauth_ctx++ != 0) {
err = EINVAL;
goto errout;
}
rc = md_get_uint16le(mdp, &picap->picap_hash_count);
if (rc != 0) {
err = EINVAL;
goto errout;
}
rc = md_get_uint16le(mdp, &picap->picap_salt_len);
if (rc != 0) {
err = EINVAL;
goto errout;
}
if (picap->picap_hash_count != 1) {
err = EINVAL;
goto errout;
}
rc = md_get_uint16le(mdp, &picap->picap_hash_id);
if (rc != 0) {
err = EINVAL;
goto errout;
}
rc = md_get_mem(mdp, NULL,
picap->picap_salt_len, MB_MSYSTEM);
if (rc != 0) {
err = EINVAL;
goto errout;
}
break;
case SMB2_ENCRYPTION_CAPS:
memcpy(&neg_ctxs->encrypt_ctx.neg_ctx, &neg_ctx,
sizeof (neg_ctx));
if (found_encrypt_ctx++ != 0) {
err = EINVAL;
goto errout;
}
rc = md_get_uint16le(mdp, &encap->encap_cipher_count);
if (rc != 0 || encap->encap_cipher_count != 1) {
err = EINVAL;
goto errout;
}
rc = md_get_uint16le(mdp,
&encap->encap_cipher_ids[0]);
if (rc != 0) {
err = EINVAL;
goto errout;
}
cipher = encap->encap_cipher_ids[0];
break;
default:
SMBSDEBUG("Unknown neg. ctx. type: 0x%x",
neg_ctx.type);
break;
}
if ((i + 1) < neg_ctxs->count) {
int skip = ctx_next_off - md_tell(mdp);
if (skip < 0) {
err = EINVAL;
goto errout;
}
if (skip > 0) {
(void) md_get_mem(mdp, NULL, skip, MB_MSYSTEM);
}
}
}
if (err != 0)
goto errout;
DTRACE_PROBE1(decoded, smb2_neg_ctxs_t *, neg_ctxs);
if (found_preauth_ctx != 1 || picap->picap_hash_count != 1) {
err = EINVAL;
goto errout;
}
if (picap->picap_hash_id != SMB3_HASH_SHA512) {
err = EAUTH;
goto errout;
}
vcp->vc3_preauth_hashid = SMB3_HASH_SHA512;
if (found_encrypt_ctx == 0) {
vcp->vc3_enc_cipherid = SMB3_CIPHER_NONE;
} else {
switch (cipher) {
case SMB3_CIPHER_AES256_GCM:
case SMB3_CIPHER_AES128_GCM:
vcp->vc3_enc_cipherid = cipher;
break;
case SMB3_CIPHER_AES256_CCM:
case SMB3_CIPHER_AES128_CCM:
vcp->vc3_enc_cipherid = cipher;
break;
default:
vcp->vc3_enc_cipherid = SMB3_CIPHER_NONE;
break;
}
}
errout:
return (err);
}