Print this page
NEX-19225 SMB client 2.1 hits redzone panic
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
NEX-14666 Need to provide SMB 2.1 Client
NEX-17187 panic in smbfs_acl_store
NEX-17231 smbfs create xattr files finds wrong file
NEX-17224 smbfs lookup EINVAL should be ENOENT
NEX-17260 SMB1 client fails to list directory after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Saso Kiselkov <saso.kiselkov@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
and: (cleanup)
NEX-16818 Add fksmbcl development tool
NEX-17264 SMB client test tp_smbutil_013 fails after NEX-14666
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Matt Barden <matt.barden@nexenta.com>
and: (fix ref leaks)

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c
          +++ new/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c
↓ open down ↓ 13 lines elided ↑ open up ↑
  14   14   * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
       24 + * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  24   25   */
  25   26  
  26   27  /*
  27   28   * Support for SMB "signing" (message integrity)
  28   29   */
  29   30  
  30   31  #include <sys/param.h>
  31   32  #include <sys/systm.h>
  32   33  #include <sys/conf.h>
  33   34  #include <sys/proc.h>
  34   35  #include <sys/fcntl.h>
  35   36  #include <sys/socket.h>
  36   37  #include <sys/md4.h>
  37   38  #include <sys/md5.h>
  38   39  #include <sys/des.h>
  39   40  #include <sys/kmem.h>
  40      -#include <sys/crypto/api.h>
  41      -#include <sys/crypto/common.h>
  42   41  #include <sys/cmn_err.h>
  43   42  #include <sys/stream.h>
  44   43  #include <sys/strsun.h>
  45   44  #include <sys/sdt.h>
  46   45  
  47   46  #include <netsmb/smb_osdep.h>
  48   47  #include <netsmb/smb.h>
  49   48  #include <netsmb/smb_conn.h>
  50   49  #include <netsmb/smb_subr.h>
  51   50  #include <netsmb/smb_dev.h>
  52   51  #include <netsmb/smb_rq.h>
       52 +#include <netsmb/smb_signing.h>
  53   53  
  54   54  #ifdef DEBUG
  55   55  /*
  56   56   * Set this to a small number to debug sequence numbers
  57   57   * that seem to get out of step.
  58   58   */
  59   59  int nsmb_signing_fudge = 0;
  60   60  #endif
  61   61  
  62      -/* Mechanism definitions */
  63      -static  crypto_mechanism_t crypto_mech_md5 = { CRYPTO_MECH_INVALID };
  64      -
  65      -void
  66      -smb_crypto_mech_init(void)
       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)
  67   68  {
  68      -        crypto_mech_md5.cm_type = crypto_mech2id(SUN_CKM_MD5);
  69      -}
       69 +        int rc;
  70   70  
       71 +        ASSERT(vcp->vc_ssnkey != NULL);
       72 +        ASSERT(vcp->vc_mackey == NULL);
  71   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 +        }
  72   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 +
  73   95  #define SMBSIGLEN       8       /* SMB signature length */
  74   96  #define SMBSIGOFF       14      /* SMB signature offset */
  75   97  
  76   98  /*
  77   99   * Compute HMAC-MD5 of packet data, using the stored MAC key.
  78  100   *
  79  101   * See similar code for the server side:
  80  102   * uts/common/fs/smbsrv/smb_signing.c : smb_sign_calc
  81  103   */
  82  104  static int
  83  105  smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp,
  84  106          uint32_t seqno, uchar_t *signature)
  85  107  {
  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;
      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 +
  92  114          /*
  93  115           * This union is a little bit of trickery to:
  94  116           * (1) get the sequence number int aligned, and
  95  117           * (2) reduce the number of digest calls, at the
  96  118           * cost of a copying 32 bytes instead of 8.
  97  119           * Both sides of this union are 2+32 bytes.
  98  120           */
  99  121          union {
 100  122                  struct {
 101  123                          uint8_t skip[2]; /* not used - just alignment */
 102  124                          uint8_t raw[SMB_HDRLEN];  /* header length (32) */
 103  125                  } r;
 104  126                  struct {
 105  127                          uint8_t skip[2]; /* not used - just alignment */
 106  128                          uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
 107  129                          uint32_t sig[2]; /* MAC signature, aligned! */
 108  130                          uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
 109  131                  } s;
 110  132          } smbhdr;
 111  133  
 112      -        ASSERT(mp != NULL);
 113      -        ASSERT(MBLKL(mp) >= SMB_HDRLEN);
 114      -        ASSERT(vcp->vc_mackey != NULL);
      134 +        if (vcp->vc_mackey == NULL)
      135 +                return (-1);
 115  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 +
 116  149          /*
 117      -         * Make an aligned copy of the SMB header
 118      -         * and fill in the sequence number.
      150 +         * Make an aligned copy of the SMB header,
      151 +         * fill in the sequence number, and digest.
 119  152           */
 120      -        bcopy(mp->b_rptr, smbhdr.r.raw, SMB_HDRLEN);
      153 +        size = SMB_HDRLEN;
      154 +        bcopy(m->b_rptr, smbhdr.r.raw, size);
 121  155          smbhdr.s.sig[0] = htolel(seqno);
 122  156          smbhdr.s.sig[1] = 0;
 123  157  
      158 +        rc = smb_md5_update(ctx, &smbhdr.r.raw, size);
      159 +        if (rc != 0)
      160 +                return (rc);
      161 +
 124  162          /*
 125      -         * Compute the MAC: MD5(concat(Key, message))
      163 +         * Digest the rest of the SMB header packet, starting at
      164 +         * the data just after the SMB header.
 126  165           */
 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);
      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;
 134  171  
 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  172          /* 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);
      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);
 166  185  
 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  186          /*
 179  187           * Finally, store the signature.
 180  188           * (first 8 bytes of the mac)
 181  189           */
 182      -        if (signature)
 183      -                bcopy(mac, signature, SMBSIGLEN);
      190 +        if (signature != NULL)
      191 +                bcopy(digest, signature, SMBSIGLEN);
 184  192  
 185  193          return (0);
 186  194  }
 187  195  
 188  196  /*
 189  197   * Sign a request with HMAC-MD5.
 190  198   */
 191  199  void
 192  200  smb_rq_sign(struct smb_rq *rqp)
 193  201  {
 194  202          struct smb_vc *vcp = rqp->sr_vc;
 195  203          mblk_t *mp = rqp->sr_rq.mb_top;
 196  204          uint8_t *sigloc;
 197  205          int status;
 198  206  
 199  207          /*
 200      -         * Our mblk allocation ensures this,
 201      -         * but just in case...
      208 +         * smb_rq_new() ensures this,
      209 +         * but just in case..
 202  210           */
 203      -        if (MBLKL(mp) < SMB_HDRLEN) {
 204      -                if (!pullupmsg(mp, SMB_HDRLEN))
 205      -                        return;
 206      -        }
      211 +        ASSERT(MBLKL(mp) >= SMB_HDRLEN);
 207  212          sigloc = mp->b_rptr + SMBSIGOFF;
 208  213  
 209  214          if (vcp->vc_mackey == NULL) {
 210  215                  /*
 211  216                   * Signing is required, but we have no key yet
 212  217                   * fill in with the magic fake signing value.
 213  218                   * This happens with SPNEGO, NTLMSSP, ...
 214  219                   */
 215  220                  bcopy("BSRSPLY", sigloc, 8);
 216  221                  return;
 217  222          }
 218  223  
 219  224          /*
 220  225           * This will compute the MAC and store it
 221  226           * directly into the message at sigloc.
 222  227           */
 223  228          status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc);
 224      -        if (status != CRYPTO_SUCCESS) {
      229 +        if (status != 0) {
 225  230                  SMBSDEBUG("Crypto error %d", status);
 226  231                  bzero(sigloc, SMBSIGLEN);
 227  232          }
 228  233  }
 229  234  
 230  235  /*
 231  236   * Verify reply signature.
 232  237   */
 233  238  int
 234  239  smb_rq_verify(struct smb_rq *rqp)
↓ open down ↓ 14 lines elided ↑ open up ↑
 249  254          }
 250  255  
 251  256          /*
 252  257           * Let caller deal with empty reply or short messages by
 253  258           * returning zero.  Caller will fail later, in parsing.
 254  259           */
 255  260          if (mp == NULL) {
 256  261                  SMBSDEBUG("empty reply\n");
 257  262                  return (0);
 258  263          }
 259      -        if (MBLKL(mp) < SMB_HDRLEN) {
 260      -                if (!pullupmsg(mp, SMB_HDRLEN))
 261      -                        return (0);
 262      -        }
      264 +
      265 +        ASSERT(MBLKL(mp) >= SMB_HDRLEN);
 263  266          sigloc = mp->b_rptr + SMBSIGOFF;
 264  267  
 265  268          /*
 266  269           * Compute the expected signature in sigbuf.
 267  270           */
 268  271          rsn = rqp->sr_rseqno;
 269  272          status = smb_compute_MAC(vcp, mp, rsn, sigbuf);
 270      -        if (status != CRYPTO_SUCCESS) {
      273 +        if (status != 0) {
 271  274                  SMBSDEBUG("Crypto error %d", status);
 272  275                  /*
 273  276                   * If we can't compute a MAC, then there's
 274  277                   * no point trying other seqno values.
 275  278                   */
 276  279                  return (EBADRPC);
 277  280          }
 278  281  
 279  282          /*
 280  283           * Compare the computed signature with the
↓ open down ↓ 30 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX