diff options
-rw-r--r-- | sys/netsmb/smb_conn.c | 5 | ||||
-rw-r--r-- | sys/netsmb/smb_conn.h | 3 | ||||
-rw-r--r-- | sys/netsmb/smb_crypt.c | 193 | ||||
-rw-r--r-- | sys/netsmb/smb_iod.c | 2 | ||||
-rw-r--r-- | sys/netsmb/smb_rq.c | 24 | ||||
-rw-r--r-- | sys/netsmb/smb_rq.h | 6 | ||||
-rw-r--r-- | sys/netsmb/smb_smb.c | 25 | ||||
-rw-r--r-- | sys/netsmb/smb_subr.h | 3 |
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_ */ |