summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netsmb/smb_conn.c5
-rw-r--r--sys/netsmb/smb_conn.h3
-rw-r--r--sys/netsmb/smb_crypt.c193
-rw-r--r--sys/netsmb/smb_iod.c2
-rw-r--r--sys/netsmb/smb_rq.c24
-rw-r--r--sys/netsmb/smb_rq.h6
-rw-r--r--sys/netsmb/smb_smb.c25
-rw-r--r--sys/netsmb/smb_subr.h3
8 files changed, 249 insertions, 12 deletions
diff --git a/sys/netsmb/smb_conn.c b/sys/netsmb/smb_conn.c
index 23eb4be..7ea63ee 100644
--- a/sys/netsmb/smb_conn.c
+++ b/sys/netsmb/smb_conn.c
@@ -405,6 +405,9 @@ smb_vc_create(struct smb_vcspec *vcspec,
vcp->vc_mode = vcspec->rights & SMBM_MASK;
vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE);
vcp->vc_tdesc = &smb_tran_nbtcp_desc;
+ vcp->vc_seqno = 0;
+ vcp->vc_mackey = NULL;
+ vcp->vc_mackeylen = 0;
if (uid == SMBM_ANY_OWNER)
uid = realuid;
@@ -474,6 +477,8 @@ smb_vc_free(struct smb_connobj *cp)
SMB_STRFREE(vcp->vc_srvname);
SMB_STRFREE(vcp->vc_pass);
SMB_STRFREE(vcp->vc_domain);
+ if (vcp->vc_mackey)
+ free(vcp->vc_mackey, M_SMBTEMP);
if (vcp->vc_paddr)
free(vcp->vc_paddr, M_SONAME);
if (vcp->vc_laddr)
diff --git a/sys/netsmb/smb_conn.h b/sys/netsmb/smb_conn.h
index 70f43e1..4092dcb 100644
--- a/sys/netsmb/smb_conn.h
+++ b/sys/netsmb/smb_conn.h
@@ -268,6 +268,9 @@ struct smb_vc {
int vc_wxmax; /* max writex data size */
struct smbiod * vc_iod;
struct smb_slock vc_stlock;
+ u_int32_t vc_seqno; /* my next sequence number */
+ u_int8_t *vc_mackey; /* MAC key */
+ int vc_mackeylen; /* length of MAC key */
};
#define vc_maxmux vc_sopt.sv_maxmux
diff --git a/sys/netsmb/smb_crypt.c b/sys/netsmb/smb_crypt.c
index 6695d57..9d01217 100644
--- a/sys/netsmb/smb_crypt.c
+++ b/sys/netsmb/smb_crypt.c
@@ -2,6 +2,9 @@
* Copyright (c) 2000-2001, Boris Popov
* All rights reserved.
*
+ * Copyright (c) 2003, 2004 Tim J. Robbins.
+ * All rights reserved.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -43,13 +46,17 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
-
+#include <sys/endian.h>
+#include <sys/mbuf.h>
+#include <sys/mchain.h>
#include <sys/md4.h>
+#include <sys/md5.h>
#include <sys/iconv.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 "opt_netsmb.h"
@@ -146,3 +153,187 @@ smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN)
#endif
}
+/*
+ * Calculate message authentication code (MAC) key for virtual circuit.
+ */
+int
+smb_calcmackey(struct smb_vc *vcp)
+{
+#ifdef NETSMBCRYPTO
+ const char *pwd;
+ u_int16_t *unipwd;
+ int len;
+ MD4_CTX md4;
+ u_char S16[16], S21[21];
+
+ KASSERT(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE,
+ ("signatures not enabled"));
+
+ if (vcp->vc_mackey != NULL) {
+ free(vcp->vc_mackey, M_SMBTEMP);
+ vcp->vc_mackey = NULL;
+ vcp->vc_mackeylen = 0;
+ vcp->vc_seqno = 0;
+ }
+
+ /*
+ * The partial MAC key is the concatenation of the 16 byte session
+ * key and the 24 byte challenge response.
+ */
+ vcp->vc_mackeylen = 16 + 24;
+ vcp->vc_mackey = malloc(vcp->vc_mackeylen, M_SMBTEMP, M_WAITOK);
+
+ /*
+ * Calculate session key:
+ * MD4(MD4(U(PN)))
+ */
+ pwd = smb_vc_getpass(vcp);
+ len = strlen(pwd);
+ unipwd = malloc((len + 1) * sizeof(u_int16_t), M_SMBTEMP, M_WAITOK);
+ smb_strtouni(unipwd, pwd);
+ MD4Init(&md4);
+ MD4Update(&md4, (u_char *)unipwd, len * sizeof(u_int16_t));
+ MD4Final(S16, &md4);
+ MD4Init(&md4);
+ MD4Update(&md4, S16, 16);
+ MD4Final(vcp->vc_mackey, &md4);
+ free(unipwd, M_SMBTEMP);
+
+ /*
+ * Calculate response to challenge:
+ * Ex(concat(MD4(U(pass)), zeros(5)), C8)
+ */
+ bzero(S21, 21);
+ bcopy(S16, S21, 16);
+ smb_E(S21, vcp->vc_ch, vcp->vc_mackey + 16);
+ smb_E(S21 + 7, vcp->vc_ch, vcp->vc_mackey + 24);
+ smb_E(S21 + 14, vcp->vc_ch, vcp->vc_mackey + 32);
+
+ return (0);
+#else
+ panic("smb_calcmackey: encryption not available");
+ return (0);
+#endif /* NETSMBCRYPTO */
+}
+
+/*
+ * Sign request with MAC.
+ */
+int
+smb_rq_sign(struct smb_rq *rqp)
+{
+#ifdef NETSMBCRYPTO
+ struct smb_vc *vcp = rqp->sr_vc;
+ struct mbchain *mbp;
+ struct mbuf *mb;
+ MD5_CTX md5;
+ u_char digest[16];
+
+ KASSERT(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE,
+ ("signatures not enabled"));
+
+ if (vcp->vc_mackey == NULL)
+ /* XXX Should assert that cmd == SMB_COM_NEGOTIATE. */
+ return (0);
+
+ /*
+ * This is a bit of a kludge. If the request is non-TRANSACTION,
+ * or it is the first request of a transaction, give it the next
+ * sequence number, and expect the reply to have the sequence number
+ * following that one. Otherwise, it is a secondary request in
+ * a transaction, and it gets the same sequence numbers as the
+ * primary request.
+ */
+ 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 {
+ /*
+ * Sequence numbers are already in the struct because
+ * smb_t2_request_int() uses the same one for all the
+ * requests in the transaction.
+ * (At least we hope so.)
+ */
+ 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"));
+ }
+
+ /* Initialize sec. signature field to sequence number + zeros. */
+ *(u_int32_t *)rqp->sr_rqsig = htole32(rqp->sr_seqno);
+ *(u_int32_t *)(rqp->sr_rqsig + 4) = 0;
+
+ /*
+ * Compute HMAC-MD5 of packet data, keyed by MAC key.
+ * Store the first 8 bytes in the sec. signature field.
+ */
+ smb_rq_getrequest(rqp, &mbp);
+ MD5Init(&md5);
+ MD5Update(&md5, vcp->vc_mackey, vcp->vc_mackeylen);
+ for (mb = mbp->mb_top; mb != NULL; mb = mb->m_next)
+ MD5Update(&md5, mtod(mb, void *), mb->m_len);
+ MD5Final(digest, &md5);
+ bcopy(digest, rqp->sr_rqsig, 8);
+
+ return (0);
+#else
+ panic("smb_rq_sign: encryption not available");
+ return (0);
+#endif /* NETSMBCRYPTO */
+}
+
+/*
+ * Verify reply signature.
+ */
+int
+smb_rq_verify(struct smb_rq *rqp)
+{
+#ifdef NETSMBCRYPTO
+ struct smb_vc *vcp = rqp->sr_vc;
+ struct mdchain *mdp;
+ u_char sigbuf[8];
+ MD5_CTX md5;
+ u_char digest[16];
+ struct mbuf *mb;
+
+ KASSERT(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE,
+ ("signatures not enabled"));
+
+ if (vcp->vc_mackey == NULL)
+ /* XXX Should check that this is a SMB_COM_NEGOTIATE reply. */
+ return (0);
+
+ /*
+ * Compute HMAC-MD5 of packet data, keyed by MAC key.
+ * We play games to pretend the security signature field
+ * contains their sequence number, to avoid modifying
+ * the packet itself.
+ */
+ smb_rq_getreply(rqp, &mdp);
+ mb = mdp->md_top;
+ KASSERT(mb->m_len >= SMB_HDRLEN, ("forgot to m_pullup"));
+ MD5Init(&md5);
+ MD5Update(&md5, vcp->vc_mackey, vcp->vc_mackeylen);
+ MD5Update(&md5, mtod(mb, void *), 14);
+ *(u_int32_t *)sigbuf = htole32(rqp->sr_rseqno);
+ *(u_int32_t *)(sigbuf + 4) = 0;
+ MD5Update(&md5, sigbuf, 8);
+ MD5Update(&md5, mtod(mb, u_char *) + 22, mb->m_len - 22);
+ for (mb = mb->m_next; mb != NULL; mb = mb->m_next)
+ MD5Update(&md5, mtod(mb, void *), mb->m_len);
+ MD5Final(digest, &md5);
+
+ /*
+ * Now verify the signature.
+ */
+ if (bcmp(mtod(mdp->md_top, u_char *) + 14, digest, 8) != 0)
+ return (EAUTH);
+
+ return (0);
+#else
+ panic("smb_rq_verify: encryption not available");
+ return (0);
+#endif /* NETSMBCRYPTO */
+}
diff --git a/sys/netsmb/smb_iod.c b/sys/netsmb/smb_iod.c
index ae9c457..e2abac5 100644
--- a/sys/netsmb/smb_iod.c
+++ b/sys/netsmb/smb_iod.c
@@ -247,6 +247,8 @@ smb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp)
*rqp->sr_rqtid = htole16(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
*rqp->sr_rquid = htole16(vcp ? vcp->vc_smbuid : 0);
mb_fixhdr(&rqp->sr_rq);
+ if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)
+ smb_rq_sign(rqp);
}
if (rqp->sr_sendcnt++ > 5) {
rqp->sr_flags |= SMBR_RESTART;
diff --git a/sys/netsmb/smb_rq.c b/sys/netsmb/smb_rq.c
index c507cac..03c7eb6 100644
--- a/sys/netsmb/smb_rq.c
+++ b/sys/netsmb/smb_rq.c
@@ -114,6 +114,7 @@ smb_rq_new(struct smb_rq *rqp, u_char cmd)
struct smb_vc *vcp = rqp->sr_vc;
struct mbchain *mbp = &rqp->sr_rq;
int error;
+ u_int16_t flags2;
rqp->sr_sendcnt = 0;
mb_done(mbp);
@@ -125,11 +126,20 @@ smb_rq_new(struct smb_rq *rqp, u_char cmd)
mb_put_uint8(mbp, cmd);
mb_put_uint32le(mbp, 0); /* DosError */
mb_put_uint8(mbp, vcp->vc_hflags);
+ flags2 = vcp->vc_hflags2;
if (cmd == SMB_COM_TRANSACTION || cmd == SMB_COM_TRANSACTION_SECONDARY)
- mb_put_uint16le(mbp, (vcp->vc_hflags2 & ~SMB_FLAGS2_UNICODE));
- else
- mb_put_uint16le(mbp, vcp->vc_hflags2);
- mb_put_mem(mbp, tzero, 12, MB_MSYSTEM);
+ flags2 &= ~SMB_FLAGS2_UNICODE;
+ if (cmd == SMB_COM_NEGOTIATE)
+ flags2 &= ~SMB_FLAGS2_SECURITY_SIGNATURE;
+ mb_put_uint16le(mbp, flags2);
+ if ((flags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0) {
+ mb_put_mem(mbp, tzero, 12, MB_MSYSTEM);
+ rqp->sr_rqsig = NULL;
+ } else {
+ mb_put_uint16le(mbp, 0 /*scred->sc_p->p_pid >> 16*/);
+ rqp->sr_rqsig = (u_int8_t *)mb_reserve(mbp, 8);
+ mb_put_uint16le(mbp, 0);
+ }
rqp->sr_rqtid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t));
mb_put_uint16le(mbp, 1 /*scred->sc_p->p_pid & 0xffff*/);
rqp->sr_rquid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t));
@@ -349,6 +359,10 @@ smb_rq_reply(struct smb_rq *rqp)
error = md_get_uint16le(mdp, &rqp->sr_rpuid);
error = md_get_uint16le(mdp, &rqp->sr_rpmid);
+ if (error == 0 &&
+ (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE))
+ error = smb_rq_verify(rqp);
+
SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n",
rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid,
rqp->sr_errclass, rqp->sr_serror);
@@ -575,6 +589,7 @@ smb_t2_request_int(struct smb_t2rq *t2p)
return error;
rqp->sr_flags |= SMBR_MULTIPACKET;
t2p->t2_rq = rqp;
+ rqp->sr_t2 = t2p;
mbp = &rqp->sr_rq;
smb_rq_wstart(rqp);
mb_put_uint16le(mbp, totpcount);
@@ -649,6 +664,7 @@ smb_t2_request_int(struct smb_t2rq *t2p)
if (error)
goto bad;
while (leftpcount || leftdcount) {
+ t2p->t2_flags |= SMBT2_SECONDARY;
error = smb_rq_new(rqp, t2p->t_name ?
SMB_COM_TRANSACTION_SECONDARY : SMB_COM_TRANSACTION2_SECONDARY);
if (error)
diff --git a/sys/netsmb/smb_rq.h b/sys/netsmb/smb_rq.h
index 7d99fea..8cdc1f6 100644
--- a/sys/netsmb/smb_rq.h
+++ b/sys/netsmb/smb_rq.h
@@ -54,6 +54,7 @@
#define SMBT2_ALLOCED 0x0004
#define SMBT2_RESTART 0x0008
#define SMBT2_NORESTART 0x0010
+#define SMBT2_SECONDARY 0x0020 /* secondary request */
#define SMBRQ_SLOCK(rqp) smb_sl_lock(&(rqp)->sr_slock)
#define SMBRQ_SUNLOCK(rqp) smb_sl_unlock(&(rqp)->sr_slock)
@@ -75,6 +76,8 @@ struct smb_rq {
struct smb_vc * sr_vc;
struct smb_share* sr_share;
u_short sr_mid;
+ u_int32_t sr_seqno;
+ u_int32_t sr_rseqno;
struct mbchain sr_rq;
u_int8_t sr_rqflags;
u_int16_t sr_rqflags2;
@@ -91,6 +94,7 @@ struct smb_rq {
int sr_sendcnt;
struct timespec sr_timesent;
int sr_lerror;
+ u_int8_t * sr_rqsig;
u_int16_t * sr_rqtid;
u_int16_t * sr_rquid;
u_int8_t sr_errclass;
@@ -103,7 +107,7 @@ struct smb_rq {
u_int16_t sr_rpuid;
u_int16_t sr_rpmid;
struct smb_slock sr_slock; /* short term locks */
-/* struct smb_t2rq*sr_t2;*/
+ struct smb_t2rq * sr_t2;
TAILQ_ENTRY(smb_rq) sr_link;
};
diff --git a/sys/netsmb/smb_smb.c b/sys/netsmb/smb_smb.c
index b38f8e8..c7d2a07 100644
--- a/sys/netsmb/smb_smb.c
+++ b/sys/netsmb/smb_smb.c
@@ -55,6 +55,8 @@ __FBSDID("$FreeBSD$");
#include <netsmb/smb_conn.h>
#include <netsmb/smb_tran.h>
+#include "opt_netsmb.h"
+
struct smb_dialect {
int d_id;
const char * d_name;
@@ -80,24 +82,29 @@ smb_vc_maxread(struct smb_vc *vcp)
/*
* Specs say up to 64k data bytes, but Windows traffic
* uses 60k... no doubt for some good reason.
+ *
+ * Don't exceed the server's buffer size if signatures
+ * are enabled otherwise Windows 2003 chokes. Allow space
+ * for the SMB header & a little bit extra.
*/
- if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX)
+ if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_READX) &&
+ (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0)
return (60*1024);
else
- return (vcp->vc_sopt.sv_maxtx);
+ return (vcp->vc_sopt.sv_maxtx - SMB_HDRLEN - 64);
}
static u_int32_t
smb_vc_maxwrite(struct smb_vc *vcp)
{
/*
- * Specs say up to 64k data bytes, but Windows traffic
- * uses 60k... probably for some good reason.
+ * See comment above.
*/
- if (vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX)
+ if ((vcp->vc_sopt.sv_caps & SMB_CAP_LARGE_WRITEX) &&
+ (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0)
return (60*1024);
else
- return (vcp->vc_sopt.sv_maxtx);
+ return (vcp->vc_sopt.sv_maxtx - SMB_HDRLEN - 64);
}
static int
@@ -190,6 +197,10 @@ smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
vcp->vc_chlen = sblen;
vcp->obj.co_flags |= SMBV_ENCRYPT;
}
+#ifdef NETSMBCRYPTO
+ if (sp->sv_sm & SMB_SM_SIGS_REQUIRE)
+ vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
+#endif
vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
sp->sv_maxtx < 4096 &&
@@ -385,6 +396,8 @@ again:
smb_rq_bend(rqp);
if (ntencpass)
free(ntencpass, M_SMBTEMP);
+ if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)
+ smb_calcmackey(vcp);
error = smb_rq_simple(rqp);
SMBSDEBUG("%d\n", error);
if (error) {
diff --git a/sys/netsmb/smb_subr.h b/sys/netsmb/smb_subr.h
index 276190f..62c084a 100644
--- a/sys/netsmb/smb_subr.h
+++ b/sys/netsmb/smb_subr.h
@@ -114,6 +114,7 @@ void smb_strfree(char *s);
void smb_memfree(void *s);
void *smb_zmalloc(unsigned long size, struct malloc_type *type, int flags);
+int smb_calcmackey(struct smb_vc *vcp);
int smb_encrypt(const u_char *apwd, u_char *C8, u_char *RN);
int smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN);
int smb_maperror(int eclass, int eno);
@@ -123,5 +124,7 @@ int smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp,
const char *src, int caseopt);
int smb_put_string(struct smb_rq *rqp, const char *src);
int smb_put_asunistring(struct smb_rq *rqp, const char *src);
+int smb_rq_sign(struct smb_rq *rqp);
+int smb_rq_verify(struct smb_rq *rqp);
#endif /* !_NETSMB_SMB_SUBR_H_ */
OpenPOWER on IntegriCloud