smbfs - Fix mount_smbfs authentication error (but 'ls' still broken)
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 2 Dec 2016 00:32:27 +0000 (16:32 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 2 Dec 2016 00:32:27 +0000 (16:32 -0800)
* 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
sys/netproto/smb/smb_conn.c
sys/netproto/smb/smb_conn.h
sys/netproto/smb/smb_crypt.c
sys/netproto/smb/smb_iod.c
sys/netproto/smb/smb_rq.c
sys/netproto/smb/smb_rq.h
sys/netproto/smb/smb_smb.c
sys/netproto/smb/smb_subr.h

index 27c817f..1620f94 100644 (file)
@@ -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
index 3ec6a74..ab301e4 100644 (file)
@@ -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)
index 4561ec5..e583735 100644 (file)
@@ -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
index 22d6eae..2379d23 100644 (file)
@@ -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:
 #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 "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 */
+}
index 136048e..ebc6f9c 100644 (file)
@@ -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;
index 6dc9ada..57f6005 100644 (file)
@@ -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)
index 9972a10..a7c0ae8 100644 (file)
@@ -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;
 };
 
index e1c7e71..2bec1f5 100644 (file)
@@ -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) {
index 145bb70..ba22cdd 100644 (file)
@@ -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