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  */
  25 
  26 /*
  27  * Support for SMB "signing" (message integrity)
  28  */
  29 
  30 #include <sys/param.h>
  31 #include <sys/systm.h>
  32 #include <sys/conf.h>
  33 #include <sys/proc.h>
  34 #include <sys/fcntl.h>
  35 #include <sys/socket.h>
  36 #include <sys/md4.h>
  37 #include <sys/md5.h>
  38 #include <sys/des.h>
  39 #include <sys/kmem.h>
  40 #include <sys/crypto/api.h>
  41 #include <sys/crypto/common.h>
  42 #include <sys/cmn_err.h>
  43 #include <sys/stream.h>
  44 #include <sys/strsun.h>
  45 #include <sys/sdt.h>
  46 
  47 #include <netsmb/smb_osdep.h>
  48 #include <netsmb/smb.h>
  49 #include <netsmb/smb_conn.h>
  50 #include <netsmb/smb_subr.h>
  51 #include <netsmb/smb_dev.h>
  52 #include <netsmb/smb_rq.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 /* Mechanism definitions */
  63 static  crypto_mechanism_t crypto_mech_md5 = { CRYPTO_MECH_INVALID };
  64 
  65 void
  66 smb_crypto_mech_init(void)
  67 {
  68         crypto_mech_md5.cm_type = crypto_mech2id(SUN_CKM_MD5);
  69 }
  70 
  71 
  72 
  73 #define SMBSIGLEN       8       /* SMB signature length */
  74 #define SMBSIGOFF       14      /* SMB signature offset */
  75 
  76 /*
  77  * Compute HMAC-MD5 of packet data, using the stored MAC key.
  78  *
  79  * See similar code for the server side:
  80  * uts/common/fs/smbsrv/smb_signing.c : smb_sign_calc
  81  */
  82 static int
  83 smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp,
  84         uint32_t seqno, uchar_t *signature)
  85 {
  86         crypto_context_t crypto_ctx;
  87         crypto_data_t key;
  88         crypto_data_t data;
  89         crypto_data_t digest;
  90         uchar_t mac[16];
  91         int status;
  92         /*
  93          * This union is a little bit of trickery to:
  94          * (1) get the sequence number int aligned, and
  95          * (2) reduce the number of digest calls, at the
  96          * cost of a copying 32 bytes instead of 8.
  97          * Both sides of this union are 2+32 bytes.
  98          */
  99         union {
 100                 struct {
 101                         uint8_t skip[2]; /* not used - just alignment */
 102                         uint8_t raw[SMB_HDRLEN];  /* header length (32) */
 103                 } r;
 104                 struct {
 105                         uint8_t skip[2]; /* not used - just alignment */
 106                         uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
 107                         uint32_t sig[2]; /* MAC signature, aligned! */
 108                         uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
 109                 } s;
 110         } smbhdr;
 111 
 112         ASSERT(mp != NULL);
 113         ASSERT(MBLKL(mp) >= SMB_HDRLEN);
 114         ASSERT(vcp->vc_mackey != NULL);
 115 
 116         /*
 117          * Make an aligned copy of the SMB header
 118          * and fill in the sequence number.
 119          */
 120         bcopy(mp->b_rptr, smbhdr.r.raw, SMB_HDRLEN);
 121         smbhdr.s.sig[0] = htolel(seqno);
 122         smbhdr.s.sig[1] = 0;
 123 
 124         /*
 125          * Compute the MAC: MD5(concat(Key, message))
 126          */
 127         if (crypto_mech_md5.cm_type == CRYPTO_MECH_INVALID) {
 128                 SMBSDEBUG("crypto_mech_md5 invalid\n");
 129                 return (CRYPTO_MECHANISM_INVALID);
 130         }
 131         status = crypto_digest_init(&crypto_mech_md5, &crypto_ctx, 0);
 132         if (status != CRYPTO_SUCCESS)
 133                 return (status);
 134 
 135         /* Digest the MAC Key */
 136         key.cd_format = CRYPTO_DATA_RAW;
 137         key.cd_offset = 0;
 138         key.cd_length = vcp->vc_mackeylen;
 139         key.cd_miscdata = 0;
 140         key.cd_raw.iov_base = (char *)vcp->vc_mackey;
 141         key.cd_raw.iov_len = vcp->vc_mackeylen;
 142         status = crypto_digest_update(crypto_ctx, &key, 0);
 143         if (status != CRYPTO_SUCCESS)
 144                 return (status);
 145 
 146         /* Digest the (copied) SMB header */
 147         data.cd_format = CRYPTO_DATA_RAW;
 148         data.cd_offset = 0;
 149         data.cd_length = SMB_HDRLEN;
 150         data.cd_miscdata = 0;
 151         data.cd_raw.iov_base = (char *)smbhdr.r.raw;
 152         data.cd_raw.iov_len = SMB_HDRLEN;
 153         status = crypto_digest_update(crypto_ctx, &data, 0);
 154         if (status != CRYPTO_SUCCESS)
 155                 return (status);
 156 
 157         /* Digest rest of the SMB message. */
 158         data.cd_format = CRYPTO_DATA_MBLK;
 159         data.cd_offset = SMB_HDRLEN;
 160         data.cd_length = msgdsize(mp) - SMB_HDRLEN;
 161         data.cd_miscdata = 0;
 162         data.cd_mp = mp;
 163         status = crypto_digest_update(crypto_ctx, &data, 0);
 164         if (status != CRYPTO_SUCCESS)
 165                 return (status);
 166 
 167         /* Final */
 168         digest.cd_format = CRYPTO_DATA_RAW;
 169         digest.cd_offset = 0;
 170         digest.cd_length = sizeof (mac);
 171         digest.cd_miscdata = 0;
 172         digest.cd_raw.iov_base = (char *)mac;
 173         digest.cd_raw.iov_len = sizeof (mac);
 174         status = crypto_digest_final(crypto_ctx, &digest, 0);
 175         if (status != CRYPTO_SUCCESS)
 176                 return (status);
 177 
 178         /*
 179          * Finally, store the signature.
 180          * (first 8 bytes of the mac)
 181          */
 182         if (signature)
 183                 bcopy(mac, signature, SMBSIGLEN);
 184 
 185         return (0);
 186 }
 187 
 188 /*
 189  * Sign a request with HMAC-MD5.
 190  */
 191 void
 192 smb_rq_sign(struct smb_rq *rqp)
 193 {
 194         struct smb_vc *vcp = rqp->sr_vc;
 195         mblk_t *mp = rqp->sr_rq.mb_top;
 196         uint8_t *sigloc;
 197         int status;
 198 
 199         /*
 200          * Our mblk allocation ensures this,
 201          * but just in case...
 202          */
 203         if (MBLKL(mp) < SMB_HDRLEN) {
 204                 if (!pullupmsg(mp, SMB_HDRLEN))
 205                         return;
 206         }
 207         sigloc = mp->b_rptr + SMBSIGOFF;
 208 
 209         if (vcp->vc_mackey == NULL) {
 210                 /*
 211                  * Signing is required, but we have no key yet
 212                  * fill in with the magic fake signing value.
 213                  * This happens with SPNEGO, NTLMSSP, ...
 214                  */
 215                 bcopy("BSRSPLY", sigloc, 8);
 216                 return;
 217         }
 218 
 219         /*
 220          * This will compute the MAC and store it
 221          * directly into the message at sigloc.
 222          */
 223         status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc);
 224         if (status != CRYPTO_SUCCESS) {
 225                 SMBSDEBUG("Crypto error %d", status);
 226                 bzero(sigloc, SMBSIGLEN);
 227         }
 228 }
 229 
 230 /*
 231  * Verify reply signature.
 232  */
 233 int
 234 smb_rq_verify(struct smb_rq *rqp)
 235 {
 236         struct smb_vc *vcp = rqp->sr_vc;
 237         mblk_t *mp = rqp->sr_rp.md_top;
 238         uint8_t sigbuf[SMBSIGLEN];
 239         uint8_t *sigloc;
 240         int fudge, rsn, status;
 241 
 242         /*
 243          * Note vc_mackey and vc_mackeylen gets filled in by
 244          * smb_usr_iod_work as the connection comes in.
 245          */
 246         if (vcp->vc_mackey == NULL) {
 247                 SMBSDEBUG("no mac key\n");
 248                 return (0);
 249         }
 250 
 251         /*
 252          * Let caller deal with empty reply or short messages by
 253          * returning zero.  Caller will fail later, in parsing.
 254          */
 255         if (mp == NULL) {
 256                 SMBSDEBUG("empty reply\n");
 257                 return (0);
 258         }
 259         if (MBLKL(mp) < SMB_HDRLEN) {
 260                 if (!pullupmsg(mp, SMB_HDRLEN))
 261                         return (0);
 262         }
 263         sigloc = mp->b_rptr + SMBSIGOFF;
 264 
 265         /*
 266          * Compute the expected signature in sigbuf.
 267          */
 268         rsn = rqp->sr_rseqno;
 269         status = smb_compute_MAC(vcp, mp, rsn, sigbuf);
 270         if (status != CRYPTO_SUCCESS) {
 271                 SMBSDEBUG("Crypto error %d", status);
 272                 /*
 273                  * If we can't compute a MAC, then there's
 274                  * no point trying other seqno values.
 275                  */
 276                 return (EBADRPC);
 277         }
 278 
 279         /*
 280          * Compare the computed signature with the
 281          * one found in the message (at sigloc)
 282          */
 283         if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
 284                 return (0);
 285 
 286         SMBERROR("BAD signature, Server=%s MID=0x%x Seq=%d\n",
 287             vcp->vc_srvname, rqp->sr_mid, rsn);
 288 
 289 #ifdef DEBUG
 290         /*
 291          * For diag purposes, we check whether the client/server idea
 292          * of the sequence # has gotten a bit out of sync.
 293          */
 294         for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
 295                 (void) smb_compute_MAC(vcp, mp, rsn + fudge, sigbuf);
 296                 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
 297                         break;
 298                 (void) smb_compute_MAC(vcp, mp, rsn - fudge, sigbuf);
 299                 if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
 300                         fudge = -fudge;
 301                         break;
 302                 }
 303         }
 304         if (fudge <= nsmb_signing_fudge) {
 305                 SMBERROR("MID=0x%x, Seq=%d, but %d would have worked\n",
 306                     rqp->sr_mid, rsn, rsn + fudge);
 307         }
 308 #endif
 309         return (EBADRPC);
 310 }