#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/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_dev.h>
#include <netsmb/smb_rq.h>
#ifdef DEBUG
int nsmb_signing_fudge = 0;
#endif
int
smb_sign_init(smb_vc_t *vcp)
{
int rc;
ASSERT(vcp->vc_ssnkey != NULL);
ASSERT(vcp->vc_mackey == NULL);
rc = nsmb_md5_getmech(&vcp->vc_signmech);
if (rc != 0) {
cmn_err(CE_NOTE, "smb can't get signing mechanism");
return (EAUTH);
}
vcp->vc_mackeylen = vcp->vc_ssnkeylen;
vcp->vc_mackey = kmem_zalloc(vcp->vc_mackeylen, KM_SLEEP);
bcopy(vcp->vc_ssnkey, vcp->vc_mackey, vcp->vc_mackeylen);
vcp->vc_next_seq = 2;
return (0);
}
#define SMBSIGLEN 8
#define SMBSIGOFF 14
static int
smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp,
uint32_t seqno, uchar_t *signature)
{
uchar_t digest[MD5_DIGEST_LENGTH];
smb_sign_ctx_t ctx = 0;
mblk_t *m = mp;
int size;
int rc;
union {
struct {
uint8_t skip[2];
uint8_t raw[SMB_HDRLEN];
} r;
struct {
uint8_t skip[2];
uint8_t hdr[SMBSIGOFF];
uint32_t sig[2];
uint16_t ids[5];
} s;
} smbhdr;
if (vcp->vc_mackey == NULL)
return (-1);
if ((rc = nsmb_md5_init(&ctx, &vcp->vc_signmech)) != 0)
return (rc);
rc = nsmb_md5_update(ctx, vcp->vc_mackey, vcp->vc_mackeylen);
if (rc != 0)
return (rc);
ASSERT(m != NULL);
ASSERT(MBLKL(m) >= SMB_HDRLEN);
size = SMB_HDRLEN;
bcopy(m->b_rptr, smbhdr.r.raw, size);
smbhdr.s.sig[0] = htolel(seqno);
smbhdr.s.sig[1] = 0;
rc = nsmb_md5_update(ctx, &smbhdr.r.raw, size);
if (rc != 0)
return (rc);
size = MBLKL(m) - SMB_HDRLEN;
rc = nsmb_md5_update(ctx, m->b_rptr + SMB_HDRLEN, size);
if (rc != 0)
return (rc);
m = m->b_cont;
while (m != NULL) {
size = MBLKL(m);
if (size > 0) {
rc = nsmb_md5_update(ctx, m->b_rptr, size);
if (rc != 0)
return (rc);
}
m = m->b_cont;
}
rc = nsmb_md5_final(ctx, digest);
if (rc != 0)
return (rc);
if (signature != NULL)
bcopy(digest, signature, SMBSIGLEN);
return (0);
}
void
smb_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 status;
ASSERT(MBLKL(mp) >= SMB_HDRLEN);
sigloc = mp->b_rptr + SMBSIGOFF;
if (vcp->vc_mackey == NULL) {
bcopy("BSRSPLY", sigloc, 8);
return;
}
status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc);
if (status != 0) {
SMBSDEBUG("Crypto error %d", status);
bzero(sigloc, SMBSIGLEN);
}
}
int
smb_rq_verify(struct smb_rq *rqp)
{
struct smb_vc *vcp = rqp->sr_vc;
mblk_t *mp = rqp->sr_rp.md_top;
uint8_t sigbuf[SMBSIGLEN];
uint8_t *sigloc;
int fudge, rsn, status;
if (vcp->vc_mackey == NULL) {
SMBSDEBUG("no mac key\n");
return (0);
}
if (mp == NULL) {
SMBSDEBUG("empty reply\n");
return (0);
}
ASSERT(MBLKL(mp) >= SMB_HDRLEN);
sigloc = mp->b_rptr + SMBSIGOFF;
rsn = rqp->sr_rseqno;
status = smb_compute_MAC(vcp, mp, rsn, sigbuf);
if (status != 0) {
SMBSDEBUG("Crypto error %d", status);
return (EBADRPC);
}
if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
return (0);
SMBERROR("BAD signature, Server=%s MID=0x%x Seq=%d\n",
vcp->vc_srvname, rqp->sr_mid, rsn);
#ifdef DEBUG
for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
(void) smb_compute_MAC(vcp, mp, rsn + fudge, sigbuf);
if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
break;
(void) smb_compute_MAC(vcp, mp, rsn - fudge, sigbuf);
if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
fudge = -fudge;
break;
}
}
if (fudge <= nsmb_signing_fudge) {
SMBERROR("MID=0x%x, Seq=%d, but %d would have worked\n",
rqp->sr_mid, rsn, rsn + fudge);
}
#endif
return (EBADRPC);
}