#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/md4.h>
#include <sys/md5.h>
#include <sys/des.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/sdt.h>
#include <netsmb/nsmb_kcrypt.h>
#include <netsmb/smb_osdep.h>
#include <netsmb/smb2.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_dev.h>
#include <netsmb/smb_rq.h>
#define SMB2_SIG_OFF 48
#define SMB2_SIG_LEN 16
typedef struct smb_mac_ops {
int (*mac_init)(smb_sign_ctx_t *, smb_crypto_mech_t *,
uint8_t *, size_t);
int (*mac_update)(smb_sign_ctx_t, uint8_t *, size_t);
int (*mac_final)(smb_sign_ctx_t, uint8_t *);
} smb_mac_ops_t;
static smb_mac_ops_t
smb2_sign_ops = {
nsmb_hmac_init,
nsmb_hmac_update,
nsmb_hmac_final
};
static struct smb_mac_ops
smb3_sign_ops = {
nsmb_cmac_init,
nsmb_cmac_update,
nsmb_cmac_final
};
int
smb2_sign_init(smb_vc_t *vcp)
{
uint_t copysize;
int rc;
ASSERT(vcp->vc_ssnkey != NULL);
ASSERT(vcp->vc_mackey == NULL);
if (SMB_DIALECT(vcp) < SMB2_DIALECT_0300)
rc = nsmb_hmac_getmech(&vcp->vc_signmech);
else
rc = nsmb_cmac_getmech(&vcp->vc_signmech);
if (rc != 0)
return (EAUTH);
vcp->vc_mackeylen = SMB2_SIG_LEN;
vcp->vc_mackey = kmem_zalloc(vcp->vc_mackeylen, KM_SLEEP);
if (SMB_DIALECT(vcp) < SMB2_DIALECT_0300) {
copysize = vcp->vc_ssnkeylen;
if (copysize > vcp->vc_mackeylen)
copysize = vcp->vc_mackeylen;
bcopy(vcp->vc_ssnkey, vcp->vc_mackey, copysize);
vcp->vc_sign_ops = &smb2_sign_ops;
} else {
rc = nsmb_kdf(vcp->vc_mackey, SMB3_KEYLEN,
vcp->vc_ssnkey, vcp->vc_ssnkeylen,
(uint8_t *)"SMB2AESCMAC", 12,
(uint8_t *)"SmbSign", 8);
if (rc != 0)
return (EAUTH);
vcp->vc_sign_ops = &smb3_sign_ops;
}
return (0);
}
static int
smb2_compute_MAC(struct smb_vc *vcp, mblk_t *mp, uchar_t *signature)
{
uint8_t tmp_hdr[SMB2_HDR_SIZE];
smb_sign_ctx_t ctx = 0;
smb_mac_ops_t *ops;
mblk_t *m = mp;
int size;
int rc;
if (vcp->vc_mackey == NULL)
return (-1);
if ((ops = vcp->vc_sign_ops) == NULL)
return (-1);
rc = ops->mac_init(&ctx, &vcp->vc_signmech,
vcp->vc_mackey, vcp->vc_mackeylen);
if (rc != 0)
return (rc);
ASSERT(m != NULL);
ASSERT(MBLKL(m) >= SMB2_HDRLEN);
size = SMB2_HDRLEN;
bcopy(m->b_rptr, tmp_hdr, size);
bzero(tmp_hdr + SMB2_SIG_OFF, SMB2_SIG_LEN);
rc = ops->mac_update(ctx, tmp_hdr, size);
if (rc != 0)
return (rc);
size = MBLKL(m) - SMB2_HDRLEN;
rc = ops->mac_update(ctx, m->b_rptr + SMB2_HDRLEN, size);
if (rc != 0)
return (rc);
m = m->b_cont;
while (m != NULL) {
size = MBLKL(m);
if (size > 0) {
rc = ops->mac_update(ctx, m->b_rptr, size);
if (rc != 0)
return (rc);
}
m = m->b_cont;
}
rc = ops->mac_final(ctx, signature);
return (rc);
}
void
smb2_rq_sign(struct smb_rq *rqp)
{
struct smb_vc *vcp = rqp->sr_vc;
mblk_t *mp = rqp->sr_rq.mb_top;
uint8_t *sigloc;
int rc;
ASSERT(MBLKL(mp) >= SMB2_HDRLEN);
sigloc = mp->b_rptr + SMB2_SIG_OFF;
if (vcp->vc_mackey == NULL)
return;
rc = smb2_compute_MAC(vcp, mp, sigloc);
if (rc != 0) {
SMBSDEBUG("Crypto error %d", rc);
bzero(sigloc, SMB2_SIG_LEN);
}
}
int
smb2_rq_verify(struct smb_rq *rqp)
{
struct smb_vc *vcp = rqp->sr_vc;
mblk_t *mp = rqp->sr_rp.md_top;
uint8_t sigbuf[SMB2_SIG_LEN];
uint8_t *sigloc;
int rc;
if (vcp->vc_mackey == NULL) {
SMBSDEBUG("no mac key\n");
return (0);
}
if (mp == NULL) {
SMBSDEBUG("empty reply\n");
return (0);
}
ASSERT(MBLKL(mp) >= SMB2_HDRLEN);
sigloc = mp->b_rptr + SMB2_SIG_OFF;
rc = smb2_compute_MAC(vcp, mp, sigbuf);
if (rc != 0) {
SMBSDEBUG("Crypto error %d", rc);
return (EBADRPC);
}
if (bcmp(sigbuf, sigloc, SMB2_SIG_LEN) == 0)
return (0);
SMBERROR("BAD signature, Server=%s MID=0x%llx\n",
vcp->vc_srvname, (long long)rqp->sr2_messageid);
return (EBADRPC);
}