From ee9d8b18c681da4d1e51720233fd68df563cb0ae Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 1 Dec 2016 16:32:27 -0800 Subject: [PATCH] smbfs - Fix mount_smbfs authentication error (but 'ls' still broken) * Fixes an authentication error with mount_smbfs. Most windows file servers require a later crypto rev and man-in-the-middle protection. * Note however that while mounting works, and files can be copied by name, 'ls' currently returns empty and the mount appears to get stuck, so more work is needed. --- sys/netproto/smb/smb.h | 2 + sys/netproto/smb/smb_conn.c | 5 + sys/netproto/smb/smb_conn.h | 3 + sys/netproto/smb/smb_crypt.c | 193 +++++++++++++++++++++++++++++++++++ sys/netproto/smb/smb_iod.c | 2 + sys/netproto/smb/smb_rq.c | 23 ++++- sys/netproto/smb/smb_rq.h | 6 +- sys/netproto/smb/smb_smb.c | 8 ++ sys/netproto/smb/smb_subr.h | 3 + 9 files changed, 242 insertions(+), 3 deletions(-) diff --git a/sys/netproto/smb/smb.h b/sys/netproto/smb/smb.h index 27c817f479..1620f94bc9 100644 --- a/sys/netproto/smb/smb.h +++ b/sys/netproto/smb/smb.h @@ -99,6 +99,8 @@ enum smb_dialects { */ #define SMB_SM_USER 0x01 /* server in the user security mode */ #define SMB_SM_ENCRYPT 0x02 /* use challenge/response */ +#define SMB_SM_SIGS 0x04 +#define SMB_SM_SIGS_REQUIRE 0x08 /* * NTLM capabilities diff --git a/sys/netproto/smb/smb_conn.c b/sys/netproto/smb/smb_conn.c index 3ec6a748cf..ab301e4bba 100644 --- a/sys/netproto/smb/smb_conn.c +++ b/sys/netproto/smb/smb_conn.c @@ -445,6 +445,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; @@ -523,6 +526,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) + kfree(vcp->vc_mackey, M_SMBTEMP); if (vcp->vc_paddr) kfree(vcp->vc_paddr, M_SONAME); if (vcp->vc_laddr) diff --git a/sys/netproto/smb/smb_conn.h b/sys/netproto/smb/smb_conn.h index 4561ec5545..e5837356bc 100644 --- a/sys/netproto/smb/smb_conn.h +++ b/sys/netproto/smb/smb_conn.h @@ -271,6 +271,9 @@ struct smb_vc { int vc_txmax; /* max tx/rx packet 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/netproto/smb/smb_crypt.c b/sys/netproto/smb/smb_crypt.c index 22d6eae46d..2379d23eac 100644 --- a/sys/netproto/smb/smb_crypt.c +++ b/sys/netproto/smb/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,12 +46,18 @@ #include #include +#include +#include +#include + #include +#include #include #include "smb.h" #include "smb_conn.h" #include "smb_subr.h" +#include "smb_rq.h" #include "smb_dev.h" #include "opt_netsmb.h" @@ -144,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) { + kfree(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 = kmalloc(vcp->vc_mackeylen, M_SMBTEMP, M_WAITOK); + + /* + * Calculate session key: + * MD4(MD4(U(PN))) + */ + pwd = smb_vc_getpass(vcp); + len = strlen(pwd); + unipwd = kmalloc((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); + kfree(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/netproto/smb/smb_iod.c b/sys/netproto/smb/smb_iod.c index 136048ee8d..ebc6f9c3c5 100644 --- a/sys/netproto/smb/smb_iod.c +++ b/sys/netproto/smb/smb_iod.c @@ -260,6 +260,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/netproto/smb/smb_rq.c b/sys/netproto/smb/smb_rq.c index 6dc9adac41..57f60054d5 100644 --- a/sys/netproto/smb/smb_rq.c +++ b/sys/netproto/smb/smb_rq.c @@ -109,6 +109,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); @@ -120,8 +121,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); - mb_put_uint16le(mbp, vcp->vc_hflags2); - mb_put_mem(mbp, tzero, 12, MB_MSYSTEM); + flags2 = vcp->vc_hflags2; + if (cmd == SMB_COM_TRANSACTION || cmd == SMB_COM_TRANSACTION_SECONDARY) + 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)); @@ -343,6 +356,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); @@ -569,6 +586,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); @@ -643,6 +661,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/netproto/smb/smb_rq.h b/sys/netproto/smb/smb_rq.h index 9972a10476..a7c0ae8699 100644 --- a/sys/netproto/smb/smb_rq.h +++ b/sys/netproto/smb/smb_rq.h @@ -55,6 +55,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) @@ -77,6 +78,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; @@ -93,6 +96,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; @@ -105,7 +109,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/netproto/smb/smb_smb.c b/sys/netproto/smb/smb_smb.c index e1c7e71e14..2bec1f527d 100644 --- a/sys/netproto/smb/smb_smb.c +++ b/sys/netproto/smb/smb_smb.c @@ -53,6 +53,8 @@ #include "smb_conn.h" #include "smb_tran.h" +#include "opt_netsmb.h" + struct smb_dialect { int d_id; const char * d_name; @@ -162,6 +164,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 && @@ -319,6 +325,8 @@ smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) smb_rq_bend(rqp); if (ntencpass) kfree(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/netproto/smb/smb_subr.h b/sys/netproto/smb/smb_subr.h index 145bb7075e..ba22cdde8e 100644 --- a/sys/netproto/smb/smb_subr.h +++ b/sys/netproto/smb/smb_subr.h @@ -119,6 +119,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); @@ -128,6 +129,8 @@ 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); /* * Compatibilty with 5.x functions -- 2.41.0