#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <libkern/crypto/md5.h>
#include <sys/smb_apple.h>
#include <netsmb/smb.h>
#include <netsmb/smb_conn.h>
#include <netsmb/smb_subr.h>
#include <netsmb/smb_rq.h>
#include <netsmb/smb_dev.h>
#include <netsmb/md4.h>
#include <smbfs/smbfs_subr.h>
#include <netsmb/smb_converter.h>
#include <crypto/des.h>
#define SMBSIGLEN (8)
#define SMBSIGOFF (14)
#define SMBPASTSIG (SMBSIGOFF + SMBSIGLEN)
#define SMBFUDGESIGN 4
#ifdef SMB_DEBUG
#define SSNDEBUG 0
#endif // SMB_DEBUG
static u_char N8[] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
static void
smb_E(const u_char *key, u_char *data, u_char *dest)
{
des_key_schedule *ksp;
u_char kk[8];
kk[0] = key[0] & 0xfe;
kk[1] = key[0] << 7 | (key[1] >> 1 & 0xfe);
kk[2] = key[1] << 6 | (key[2] >> 2 & 0xfe);
kk[3] = key[2] << 5 | (key[3] >> 3 & 0xfe);
kk[4] = key[3] << 4 | (key[4] >> 4 & 0xfe);
kk[5] = key[4] << 3 | (key[5] >> 5 & 0xfe);
kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe);
kk[7] = key[6] << 1;
SMB_MALLOC(ksp, des_key_schedule *, sizeof(des_key_schedule), M_SMBTEMP, M_WAITOK);
des_set_key((des_cblock*)kk, *ksp);
des_ecb_encrypt((des_cblock*)data, (des_cblock*)dest, *ksp, 1);
SMB_FREE(ksp, M_SMBTEMP);
}
int smb_lmresponse(const u_char *apwd, u_char *C8, u_char *RN)
{
u_char *p, *P14, *S21;
SMB_MALLOC(p, u_char *, 14+21, M_SMBTEMP, M_WAITOK);
bzero(p, 14 + 21);
P14 = p;
S21 = p + 14;
bcopy(apwd, P14, MIN(14, strnlen((char *)apwd, SMB_MAXPASSWORDLEN + 1)));
smb_E(P14, N8, S21);
smb_E(P14 + 7, N8, S21 + 8);
smb_E(S21, C8, RN);
smb_E(S21 + 7, C8, RN + 8);
smb_E(S21 + 14, C8, RN + 16);
SMB_FREE(p, M_SMBTEMP);
return 24;
}
static void smb_ntlmhash(const uint8_t *passwd, uint8_t *ntlmHash, size_t ntlmHash_len)
{
uint16_t *unicode_passwd = NULL;
MD4_CTX md4;
size_t len;
bzero(ntlmHash, ntlmHash_len);
len = strnlen((char *)passwd, SMB_MAXPASSWORDLEN + 1);
if (len == 0) {
len = 1;
}
SMB_MALLOC(unicode_passwd, uint16_t *, len * sizeof(uint16_t), M_SMBTEMP, M_WAITOK);
if (unicode_passwd == NULL)
return;
len = smb_strtouni(unicode_passwd, (char *)passwd, len, UTF_PRECOMPOSED);
bzero(&md4, sizeof(md4));
MD4Init(&md4);
MD4Update(&md4, (uint8_t *)unicode_passwd, (unsigned int)len);
MD4Final(ntlmHash, &md4);
SMB_FREE(unicode_passwd, M_SMBTEMP);
#ifdef SSNDEBUG
smb_hexdump(__FUNCTION__, "ntlmHash = ", ntlmHash, 16);
#endif // SSNDEBUG
}
void smb_ntlmresponse(const u_char *apwd, u_char *C8, u_char *RN)
{
u_char S21[SMB_NTLM_LEN];
smb_ntlmhash(apwd, S21, sizeof(S21));
smb_E(S21, C8, RN);
smb_E(S21 + 7, C8, RN + 8);
smb_E(S21 + 14, C8, RN + 16);
}
void smb_reset_sig(struct smb_vc *vcp)
{
if (vcp->vc_mackey != NULL)
SMB_FREE(vcp->vc_mackey, M_SMBTEMP);
vcp->vc_mackey = NULL;
vcp->vc_mackeylen = 0;
vcp->vc_seqno = 0;
}
int
smb_rq_sign(struct smb_rq *rqp)
{
struct smb_vc *vcp = rqp->sr_vc;
struct mbchain *mbp;
mbuf_t mb;
MD5_CTX md5;
u_char digest[16];
KASSERT(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE,
("signatures not enabled"));
if ((vcp->vc_mackey == NULL) || (rqp->sr_cmd == SMB_COM_SESSION_SETUP_ANDX)) {
if (rqp->sr_rqsig)
bcopy("BSRSPLY ", rqp->sr_rqsig, 8);
return (0);
}
if (rqp->sr_t2 == NULL ||
(rqp->sr_t2->t2_flags & SMBT2_SECONDARY) == 0) {
rqp->sr_seqno = vcp->vc_seqno++;
rqp->sr_rseqno = vcp->vc_seqno++;
} else {
KASSERT(rqp->sr_t2 == NULL ||
(rqp->sr_t2->t2_flags & SMBT2_SECONDARY) == 0 ||
rqp->sr_t2->t2_rq == rqp,
("sec t2 rq not using same smb_rq"));
}
if (rqp->sr_rqsig) {
*(uint32_t *)rqp->sr_rqsig = htolel(rqp->sr_seqno);
*(uint32_t *)(rqp->sr_rqsig + 4) = 0;
}
smb_rq_getrequest(rqp, &mbp);
MD5Init(&md5);
MD5Update(&md5, vcp->vc_mackey, vcp->vc_mackeylen);
for (mb = mbp->mb_top; mb != NULL; mb = mbuf_next(mb))
MD5Update(&md5, mbuf_data(mb), (unsigned int)mbuf_len(mb));
MD5Final(digest, &md5);
if (rqp->sr_rqsig)
bcopy(digest, rqp->sr_rqsig, 8);
return (0);
}
static int
smb_verify(struct smb_rq *rqp, uint32_t seqno)
{
struct smb_vc *vcp = rqp->sr_vc;
struct mdchain *mdp;
mbuf_t mb;
u_char sigbuf[SMBSIGLEN];
MD5_CTX md5;
u_char digest[16];
smb_rq_getreply(rqp, &mdp);
mb = mdp->md_top;
KASSERT(mbuf_len(mb) >= SMB_HDRLEN, ("forgot to mbuf_pullup"));
MD5Init(&md5);
MD5Update(&md5, vcp->vc_mackey, vcp->vc_mackeylen);
MD5Update(&md5, mbuf_data(mb), SMBSIGOFF);
*(uint32_t *)sigbuf = htolel(seqno);
*(uint32_t *)(sigbuf + 4) = 0;
MD5Update(&md5, sigbuf, SMBSIGLEN);
MD5Update(&md5, (uint8_t *)mbuf_data(mb) + SMBPASTSIG,
(unsigned int)(mbuf_len(mb) - SMBPASTSIG));
for (mb = mbuf_next(mb); mb != NULL; mb = mbuf_next(mb))
if (mbuf_len(mb))
MD5Update(&md5, mbuf_data(mb), (unsigned int)mbuf_len(mb));
MD5Final(digest, &md5);
return (bcmp((uint8_t *)mbuf_data(mdp->md_top) + SMBSIGOFF, digest, SMBSIGLEN));
}
int
smb_rq_verify(struct smb_rq *rqp)
{
struct smb_vc *vcp = rqp->sr_vc;
int32_t fudge;
if (!(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)) {
SMBWARNING("signatures not enabled!\n");
return (0);
}
if ((vcp->vc_mackey == NULL) || (rqp->sr_cmd == SMB_COM_SESSION_SETUP_ANDX))
return (0);
if ((vcp->vc_flags & SMBV_ANONYMOUS_ACCESS) == SMBV_ANONYMOUS_ACCESS)
return (0);
if (smb_verify(rqp, rqp->sr_rseqno) == 0)
return (0);
if (smbfs_loglevel) {
for (fudge = -SMBFUDGESIGN; fudge <= SMBFUDGESIGN; fudge++)
if (fudge == 0)
continue;
else if (smb_verify(rqp, rqp->sr_rseqno + fudge) == 0)
break;
if (fudge <= SMBFUDGESIGN)
SMBERROR("sr_rseqno=%d, but %d would have worked\n",
rqp->sr_rseqno, rqp->sr_rseqno + fudge);
else
SMBERROR("sr_rseqno=%d\n", rqp->sr_rseqno);
}
return (EAUTH);
}