1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  25  */
  26 
  27 /*
  28  * Support for SMB "signing" (message integrity)
  29  */
  30 
  31 #include <sys/param.h>
  32 #include <sys/systm.h>
  33 #include <sys/conf.h>
  34 #include <sys/proc.h>
  35 #include <sys/fcntl.h>
  36 #include <sys/socket.h>
  37 #include <sys/md4.h>
  38 #include <sys/md5.h>
  39 #include <sys/des.h>
  40 #include <sys/kmem.h>
  41 #include <sys/cmn_err.h>
  42 #include <sys/stream.h>
  43 #include <sys/strsun.h>
  44 #include <sys/sdt.h>
  45 
  46 #include <netsmb/smb_osdep.h>
  47 #include <netsmb/smb.h>
  48 #include <netsmb/smb_conn.h>
  49 #include <netsmb/smb_subr.h>
  50 #include <netsmb/smb_dev.h>
  51 #include <netsmb/smb_rq.h>
  52 #include <netsmb/smb_signing.h>
  53 
  54 #ifdef DEBUG
  55 /*
  56  * Set this to a small number to debug sequence numbers
  57  * that seem to get out of step.
  58  */
  59 int nsmb_signing_fudge = 0;
  60 #endif
  61 
  62 /*
  63  * This is called just after session setup completes,
  64  * at the top of smb_iod_vc_work().  Initialize signing.
  65  */
  66 int
  67 smb_sign_init(smb_vc_t *vcp)
  68 {
  69         int rc;
  70 
  71         ASSERT(vcp->vc_ssnkey != NULL);
  72         ASSERT(vcp->vc_mackey == NULL);
  73 
  74         rc = smb_md5_getmech(&vcp->vc_signmech);
  75         if (rc != 0) {
  76                 cmn_err(CE_NOTE, "smb can't get signing mechanism");
  77                 return (EAUTH);
  78         }
  79 
  80         /*
  81          * Convert the session key to the MAC key.
  82          * SMB1 uses the whole session key.
  83          */
  84         vcp->vc_mackeylen = vcp->vc_ssnkeylen;
  85         vcp->vc_mackey = kmem_zalloc(vcp->vc_mackeylen, KM_SLEEP);
  86         bcopy(vcp->vc_ssnkey, vcp->vc_mackey, vcp->vc_mackeylen);
  87 
  88         /* The initial sequence number is two. */
  89         vcp->vc_next_seq = 2;
  90 
  91         return (0);
  92 }
  93 
  94 
  95 #define SMBSIGLEN       8       /* SMB signature length */
  96 #define SMBSIGOFF       14      /* SMB signature offset */
  97 
  98 /*
  99  * Compute HMAC-MD5 of packet data, using the stored MAC key.
 100  *
 101  * See similar code for the server side:
 102  * uts/common/fs/smbsrv/smb_signing.c : smb_sign_calc
 103  */
 104 static int
 105 smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp,
 106         uint32_t seqno, uchar_t *signature)
 107 {
 108         uchar_t digest[MD5_DIGEST_LENGTH];
 109         smb_sign_ctx_t ctx = 0;
 110         mblk_t *m = mp;
 111         int size;
 112         int rc;
 113 
 114         /*
 115          * This union is a little bit of trickery to:
 116          * (1) get the sequence number int aligned, and
 117          * (2) reduce the number of digest calls, at the
 118          * cost of a copying 32 bytes instead of 8.
 119          * Both sides of this union are 2+32 bytes.
 120          */
 121         union {
 122                 struct {
 123                         uint8_t skip[2]; /* not used - just alignment */
 124                         uint8_t raw[SMB_HDRLEN];  /* header length (32) */
 125                 } r;
 126                 struct {
 127                         uint8_t skip[2]; /* not used - just alignment */
 128                         uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
 129                         uint32_t sig[2]; /* MAC signature, aligned! */
 130                         uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
 131                 } s;
 132         } smbhdr;
 133 
 134         if (vcp->vc_mackey == NULL)
 135                 return (-1);
 136 
 137         if ((rc = smb_md5_init(&ctx, &vcp->vc_signmech)) != 0)
 138                 return (rc);
 139 
 140         /* Digest the MAC Key */
 141         rc = smb_md5_update(ctx, vcp->vc_mackey, vcp->vc_mackeylen);
 142         if (rc != 0)
 143                 return (rc);
 144 
 145         /* Our caller should ensure mp has a contiguous header */
 146         ASSERT(m != NULL);
 147         ASSERT(MBLKL(m) >= SMB_HDRLEN);
 148 
 149         /*
 150          * Make an aligned copy of the SMB header,
 151          * fill in the sequence number, and digest.
 152          */
 153         size = SMB_HDRLEN;
 154         bcopy(m->b_rptr, smbhdr.r.raw, size);
 155         smbhdr.s.sig[0] = htolel(seqno);
 156         smbhdr.s.sig[1] = 0;
 157 
 158         rc = smb_md5_update(ctx, &smbhdr.r.raw, size);
 159         if (rc != 0)
 160                 return (rc);
 161 
 162         /*
 163          * Digest the rest of the SMB header packet, starting at
 164          * the data just after the SMB header.
 165          */
 166         size = MBLKL(m) - SMB_HDRLEN;
 167         rc = smb_md5_update(ctx, m->b_rptr + SMB_HDRLEN, size);
 168         if (rc != 0)
 169                 return (rc);
 170         m = m->b_cont;
 171 
 172         /* Digest rest of the SMB message. */
 173         while (m != NULL) {
 174                 size = MBLKL(m);
 175                 if (size > 0) {
 176                         rc = smb_md5_update(ctx, m->b_rptr, size);
 177                         if (rc != 0)
 178                                 return (rc);
 179                 }
 180                 m = m->b_cont;
 181         }
 182         rc = smb_md5_final(ctx, digest);
 183         if (rc != 0)
 184                 return (rc);
 185 
 186         /*
 187          * Finally, store the signature.
 188          * (first 8 bytes of the mac)
 189          */
 190         if (signature != NULL)
 191                 bcopy(digest, signature, SMBSIGLEN);
 192 
 193         return (0);
 194 }
 195 
 196 /*
 197  * Sign a request with HMAC-MD5.
 198  */
 199 void
 200 smb_rq_sign(struct smb_rq *rqp)
 201 {
 202         struct smb_vc *vcp = rqp->sr_vc;
 203         mblk_t *mp = rqp->sr_rq.mb_top;
 204         uint8_t *sigloc;
 205         int status;
 206 
 207         /*
 208          * smb_rq_new() ensures this,
 209          * but just in case..
 210          */
 211         ASSERT(MBLKL(mp) >= SMB_HDRLEN);
 212         sigloc = mp->b_rptr + SMBSIGOFF;
 213 
 214         if (vcp->vc_mackey == NULL) {
 215                 /*
 216                  * Signing is required, but we have no key yet
 217                  * fill in with the magic fake signing value.
 218                  * This happens with SPNEGO, NTLMSSP, ...
 219                  */
 220                 bcopy("BSRSPLY", sigloc, 8);
 221                 return;
 222         }
 223 
 224         /*
 225          * This will compute the MAC and store it
 226          * directly into the message at sigloc.
 227          */
 228         status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc);
 229         if (status != 0) {
 230                 SMBSDEBUG("Crypto error %d", status);
 231                 bzero(sigloc, SMBSIGLEN);
 232         }
 233 }
 234 
 235 /*
 236  * Verify reply signature.
 237  */
 238 int
 239 smb_rq_verify(struct smb_rq *rqp)
 240 {
 241         struct smb_vc *vcp = rqp->sr_vc;
 242         mblk_t *mp = rqp->sr_rp.md_top;
 243         uint8_t sigbuf[SMBSIGLEN];
 244         uint8_t *sigloc;
 245         int fudge, rsn, status;
 246 
 247         /*
 248          * Note vc_mackey and vc_mackeylen gets filled in by
 249          * smb_usr_iod_work as the connection comes in.
 250          */
 251         if (vcp->vc_mackey == NULL) {
 252                 SMBSDEBUG("no mac key\n");
 253                 return (0);
 254         }
 255 
 256         /*
 257          * Let caller deal with empty reply or short messages by
 258          * returning zero.  Caller will fail later, in parsing.
 259          */
 260         if (mp == NULL) {
 261                 SMBSDEBUG("empty reply\n");
 262                 return (0);
 263         }
 264 
 265         ASSERT(MBLKL(mp) >= SMB_HDRLEN);
 266         sigloc = mp->b_rptr + SMBSIGOFF;
 267 
 268         /*
 269          * Compute the expected signature in sigbuf.
 270          */
 271         rsn = rqp->sr_rseqno;
 272         status = smb_compute_MAC(vcp, mp, rsn, sigbuf);
 273         if (status != 0) {
 274                 SMBSDEBUG("Crypto error %d", status);
 275                 /*
 276                  * If we can't compute a MAC, then there's
 277                  * no point trying other seqno values.
 278                  */
 279                 return (EBADRPC);
 280         }
 281 
 282         /*
 283          * Compare the computed signature with the
 284          * one found in the message (at sigloc)
 285          */
 286         if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
 287                 return (0);
 288 
 289         SMBERROR("BAD signature, Server=%s MID=0x%x Seq=%d\n",
 290             vcp->vc_srvname, rqp->sr_mid, rsn);
 291 
 292 #ifdef DEBUG
 293         /*
 294          * For diag purposes, we check whether the client/server idea
 295          * of the sequence # has gotten a bit out of sync.
 296          */
 297         for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
 298                 (void) smb_compute_MAC(vcp, mp, rsn + fudge, sigbuf);
 299                 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
 300                         break;
 301                 (void) smb_compute_MAC(vcp, mp, rsn - fudge, sigbuf);
 302                 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
 303                         fudge = -fudge;
 304                         break;
 305                 }
 306         }
 307         if (fudge <= nsmb_signing_fudge) {
 308                 SMBERROR("MID=0x%x, Seq=%d, but %d would have worked\n",
 309                     rqp->sr_mid, rsn, rsn + fudge);
 310         }
 311 #endif
 312         return (EBADRPC);
 313 }