Print this page
NEX-15724 SMB3 clients fail to connect using FQDN
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15724 SMB3 clients fail to connect using FQDN
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-10231 SMB logon fails in fksmbd
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
NEX-5273 SMB 3 Encryption
Reviewed by: Gordon Ross <gordon.ross@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
NEX-5560 smb2 should use 64-bit server-global uids
Reviewed by: Gordon Ross <gwr@nexenta.com>
NEX-3728 SMB1 signing should use KCF like SMB2/3
Reviewed by: Matt Barden <matt.barden@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
NEX-3610 CLONE NEX-3591 SMB3 signing
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
NEX-3080 SMB1 signing problem with Kerberos auth.
Reviewed by: Bayard Bell <bayard.bell@nexenta.com>
Reviewed by: Dan Fields <dan.fields@nexenta.com>
Reviewed by: Kevin Crowe <kevin.crowe@nexenta.com>
Reviewed by: Matt Barden <Matt.Barden@nexenta.com>
NEX-2869 SMB2 signing fails for multi-user clients like Citrix RDS
SMB-55 SMB2 signing (syslog noise)
SMB-55 SMB2 signing
        
*** 18,28 ****
   *
   * CDDL HEADER END
   */
  /*
   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
!  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
   */
  /*
   * These routines provide the SMB MAC signing for the SMB2 server.
   * The routines calculate the signature of a SMB message in an mbuf chain.
   *
--- 18,28 ----
   *
   * CDDL HEADER END
   */
  /*
   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
!  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
   */
  /*
   * These routines provide the SMB MAC signing for the SMB2 server.
   * The routines calculate the signature of a SMB message in an mbuf chain.
   *
*** 37,131 ****
   * -------------+---------------+-------------+----------------
   * Disabled     | Fail          | Not Signed  | Not Signed
   */
  
  #include <sys/uio.h>
! #include <smbsrv/smb_kproto.h>
! #include <smbsrv/smb_signing.h>
  #include <sys/isa_defs.h>
  #include <sys/byteorder.h>
  #include <sys/cmn_err.h>
  
  #define SMB2_SIG_OFFS   48
  #define SMB2_SIG_SIZE   16
  
  /*
   * Called during session destroy.
   */
  static void
  smb2_sign_fini(smb_session_t *s)
  {
!         smb_sign_mech_t *mech;
  
          if ((mech = s->sign_mech) != NULL) {
                  kmem_free(mech, sizeof (*mech));
                  s->sign_mech = NULL;
          }
  }
  
  /*
   * smb2_sign_begin
   *
   * Get the mechanism info.
   * Intializes MAC key based on the user session key and store it in
   * the signing structure.  This begins signing on this session.
   */
! int
  smb2_sign_begin(smb_request_t *sr, smb_token_t *token)
  {
          smb_session_t *s = sr->session;
          smb_user_t *u = sr->uid_user;
          struct smb_key *sign_key = &u->u_sign_key;
-         smb_sign_mech_t *mech;
-         int rc;
  
          /*
           * We should normally have a session key here because
           * our caller filters out Anonymous and Guest logons.
           * However, buggy clients could get us here without a
           * session key, in which case we'll fail later when a
           * request that requires signing can't be checked.
           */
!         if (token->tkn_ssnkey.val == NULL || token->tkn_ssnkey.len == 0)
!                 return (0);
  
          /*
-          * Session-level initialization (once per session)
-          * Get mech handle, sign_fini function.
-          */
-         smb_rwx_rwenter(&s->s_lock, RW_WRITER);
-         if (s->sign_mech == NULL) {
-                 mech = kmem_zalloc(sizeof (*mech), KM_SLEEP);
-                 rc = smb2_hmac_getmech(mech);
-                 if (rc != 0) {
-                         kmem_free(mech, sizeof (*mech));
-                         smb_rwx_rwexit(&s->s_lock);
-                         return (rc);
-                 }
-                 s->sign_mech = mech;
-                 s->sign_fini = smb2_sign_fini;
-         }
-         smb_rwx_rwexit(&s->s_lock);
- 
-         /*
           * Compute and store the signing key, which lives in
           * the user structure.
           */
!         sign_key->len = SMB2_SIG_SIZE;
! 
          /*
           * For SMB2, the signing key is just the first 16 bytes
           * of the session key (truncated or padded with zeros).
           * [MS-SMB2] 3.2.5.3.1
           */
          bcopy(token->tkn_ssnkey.val, sign_key->key,
              MIN(token->tkn_ssnkey.len, sign_key->len));
  
          mutex_enter(&u->u_mutex);
!         if (s->secmode & SMB2_NEGOTIATE_SIGNING_ENABLED)
                  u->u_sign_flags |= SMB_SIGNING_ENABLED;
!         if (s->secmode & SMB2_NEGOTIATE_SIGNING_REQUIRED)
                  u->u_sign_flags |=
                      SMB_SIGNING_ENABLED | SMB_SIGNING_CHECK;
          mutex_exit(&u->u_mutex);
  
          /*
--- 37,224 ----
   * -------------+---------------+-------------+----------------
   * Disabled     | Fail          | Not Signed  | Not Signed
   */
  
  #include <sys/uio.h>
! #include <smbsrv/smb2_kproto.h>
! #include <smbsrv/smb_kcrypt.h>
  #include <sys/isa_defs.h>
  #include <sys/byteorder.h>
  #include <sys/cmn_err.h>
  
  #define SMB2_SIG_OFFS   48
  #define SMB2_SIG_SIZE   16
  
+ typedef struct mac_ops {
+         int (*mac_init)(smb_sign_ctx_t *, smb_crypto_mech_t *,
+                         uint8_t *, size_t);
+         int (*mac_update)(smb_sign_ctx_t, uint8_t *, size_t);
+         int (*mac_final)(smb_sign_ctx_t, uint8_t *);
+ } mac_ops_t;
+ 
+ static int smb2_sign_calc_common(smb_request_t *, struct mbuf_chain *,
+     uint8_t *, mac_ops_t *);
+ 
  /*
+  * SMB2 wrapper functions
+  */
+ 
+ static mac_ops_t
+ smb2_sign_ops = {
+         smb2_hmac_init,
+         smb2_hmac_update,
+         smb2_hmac_final
+ };
+ 
+ static int
+ smb2_sign_calc(smb_request_t *sr,
+     struct mbuf_chain *mbc,
+     uint8_t *digest16)
+ {
+         int rv;
+ 
+         rv = smb2_sign_calc_common(sr, mbc, digest16, &smb2_sign_ops);
+ 
+         return (rv);
+ }
+ 
+ /*
   * Called during session destroy.
   */
  static void
  smb2_sign_fini(smb_session_t *s)
  {
!         smb_crypto_mech_t *mech;
  
          if ((mech = s->sign_mech) != NULL) {
                  kmem_free(mech, sizeof (*mech));
                  s->sign_mech = NULL;
          }
  }
  
  /*
+  * SMB3 wrapper functions
+  */
+ 
+ static struct mac_ops
+ smb3_sign_ops = {
+         smb3_cmac_init,
+         smb3_cmac_update,
+         smb3_cmac_final
+ };
+ 
+ static int
+ smb3_sign_calc(smb_request_t *sr,
+     struct mbuf_chain *mbc,
+     uint8_t *digest16)
+ {
+         int rv;
+ 
+         rv = smb2_sign_calc_common(sr, mbc, digest16, &smb3_sign_ops);
+ 
+         return (rv);
+ }
+ 
+ /*
+  * Input to KDF for SigningKey.
+  * See comment for smb3_do_kdf for content.
+  */
+ static uint8_t sign_kdf_input[29] = {
+         0, 0, 0, 1, 'S', 'M', 'B', '2',
+         'A', 'E', 'S', 'C', 'M', 'A', 'C', 0,
+         0, 'S', 'm', 'b', 'S', 'i', 'g', 'n',
+         0, 0, 0, 0, 0x80 };
+ 
+ void
+ smb2_sign_init_mech(smb_session_t *s)
+ {
+         smb_crypto_mech_t *mech;
+         int (*get_mech)(smb_crypto_mech_t *);
+         int (*sign_calc)(smb_request_t *, struct mbuf_chain *, uint8_t *);
+         int rc;
+ 
+         if (s->sign_mech != NULL)
+                 return;
+ 
+         if (s->dialect >= SMB_VERS_3_0) {
+                 get_mech = smb3_cmac_getmech;
+                 sign_calc = smb3_sign_calc;
+         } else {
+                 get_mech = smb2_hmac_getmech;
+                 sign_calc = smb2_sign_calc;
+         }
+ 
+         mech = kmem_zalloc(sizeof (*mech), KM_SLEEP);
+         rc = get_mech(mech);
+         if (rc != 0) {
+                 kmem_free(mech, sizeof (*mech));
+                 return;
+         }
+         s->sign_mech = mech;
+         s->sign_calc = sign_calc;
+         s->sign_fini = smb2_sign_fini;
+ }
+ 
+ /*
   * smb2_sign_begin
+  * Handles both SMB2 & SMB3
   *
   * Get the mechanism info.
   * Intializes MAC key based on the user session key and store it in
   * the signing structure.  This begins signing on this session.
   */
! void
  smb2_sign_begin(smb_request_t *sr, smb_token_t *token)
  {
          smb_session_t *s = sr->session;
          smb_user_t *u = sr->uid_user;
          struct smb_key *sign_key = &u->u_sign_key;
  
+         sign_key->len = 0;
+ 
          /*
           * We should normally have a session key here because
           * our caller filters out Anonymous and Guest logons.
           * However, buggy clients could get us here without a
           * session key, in which case we'll fail later when a
           * request that requires signing can't be checked.
+          * Also, don't bother initializing if we don't have a mechanism.
           */
!         if (token->tkn_ssnkey.val == NULL || token->tkn_ssnkey.len == 0 ||
!             s->sign_mech == NULL)
!                 return;
  
          /*
           * Compute and store the signing key, which lives in
           * the user structure.
           */
!         if (s->dialect >= SMB_VERS_3_0) {
                  /*
+                  * For SMB3, the signing key is a "KDF" hash of the
+                  * session key.
+                  */
+                 if (smb3_do_kdf(sign_key->key, sign_kdf_input,
+                     sizeof (sign_kdf_input), token->tkn_ssnkey.val,
+                     token->tkn_ssnkey.len) != 0)
+                         return;
+                 sign_key->len = SMB3_KEYLEN;
+         } else {
+                 /*
                   * For SMB2, the signing key is just the first 16 bytes
                   * of the session key (truncated or padded with zeros).
                   * [MS-SMB2] 3.2.5.3.1
                   */
+                 sign_key->len = SMB2_KEYLEN;
                  bcopy(token->tkn_ssnkey.val, sign_key->key,
                      MIN(token->tkn_ssnkey.len, sign_key->len));
+         }
  
          mutex_enter(&u->u_mutex);
!         if ((s->srv_secmode & SMB2_NEGOTIATE_SIGNING_ENABLED) != 0)
                  u->u_sign_flags |= SMB_SIGNING_ENABLED;
!         if ((s->srv_secmode & SMB2_NEGOTIATE_SIGNING_REQUIRED) != 0 ||
!             (s->cli_secmode & SMB2_NEGOTIATE_SIGNING_REQUIRED) != 0)
                  u->u_sign_flags |=
                      SMB_SIGNING_ENABLED | SMB_SIGNING_CHECK;
          mutex_exit(&u->u_mutex);
  
          /*
*** 134,162 ****
           * SMB2_FLAGS_SIGNED (and not signed) but the response
           * is is supposed to be signed. [MS-SMB2] 3.3.5.5
           */
          if (u->u_sign_flags & SMB_SIGNING_ENABLED)
                  sr->smb2_hdr_flags |= SMB2_FLAGS_SIGNED;
- 
-         return (0);
  }
  
  /*
!  * smb2_sign_calc
   *
   * Calculates MAC signature for the given buffer and returns
   * it in the mac_sign parameter.
   *
!  * The signature is in the last 16 bytes of the SMB2 header.
!  * The signature algorighm is to compute HMAC SHA256 over the
!  * entire command, with the signature field set to zeros.
   *
   * Return 0 if  success else -1
   */
  static int
! smb2_sign_calc(smb_request_t *sr, struct mbuf_chain *mbc,
!     uint8_t *digest)
  {
          uint8_t tmp_hdr[SMB2_HDR_SIZE];
          smb_sign_ctx_t ctx = 0;
          smb_session_t *s = sr->session;
          smb_user_t *u = sr->uid_user;
--- 227,253 ----
           * SMB2_FLAGS_SIGNED (and not signed) but the response
           * is is supposed to be signed. [MS-SMB2] 3.3.5.5
           */
          if (u->u_sign_flags & SMB_SIGNING_ENABLED)
                  sr->smb2_hdr_flags |= SMB2_FLAGS_SIGNED;
  }
  
  /*
!  * smb2_sign_calc_common
   *
   * Calculates MAC signature for the given buffer and returns
   * it in the mac_sign parameter.
   *
!  * The signature algorithm is to compute HMAC SHA256 or AES_CMAC
!  * over the entire command, with the signature field set to zeros.
   *
   * Return 0 if  success else -1
   */
+ 
  static int
! smb2_sign_calc_common(smb_request_t *sr, struct mbuf_chain *mbc,
!     uint8_t *digest, mac_ops_t *ops)
  {
          uint8_t tmp_hdr[SMB2_HDR_SIZE];
          smb_sign_ctx_t ctx = 0;
          smb_session_t *s = sr->session;
          smb_user_t *u = sr->uid_user;
*** 165,175 ****
          int offset, resid, tlen, rc;
  
          if (s->sign_mech == NULL || sign_key->len == 0)
                  return (-1);
  
!         rc = smb2_hmac_init(&ctx, s->sign_mech, sign_key->key, sign_key->len);
          if (rc != 0)
                  return (rc);
  
          /*
           * Work with a copy of the SMB2 header so we can
--- 256,267 ----
          int offset, resid, tlen, rc;
  
          if (s->sign_mech == NULL || sign_key->len == 0)
                  return (-1);
  
!         /* smb2_hmac_init or smb3_cmac_init */
!         rc = ops->mac_init(&ctx, s->sign_mech, sign_key->key, sign_key->len);
          if (rc != 0)
                  return (rc);
  
          /*
           * Work with a copy of the SMB2 header so we can
*** 180,190 ****
          offset = mbc->chain_offset;
          resid = mbc->max_bytes - offset;
          if (smb_mbc_peek(mbc, offset, "#c", tlen, tmp_hdr) != 0)
                  return (-1);
          bzero(tmp_hdr + SMB2_SIG_OFFS, SMB2_SIG_SIZE);
!         if ((rc = smb2_hmac_update(ctx, tmp_hdr, tlen)) != 0)
                  return (rc);
          offset += tlen;
          resid -= tlen;
  
          /*
--- 272,283 ----
          offset = mbc->chain_offset;
          resid = mbc->max_bytes - offset;
          if (smb_mbc_peek(mbc, offset, "#c", tlen, tmp_hdr) != 0)
                  return (-1);
          bzero(tmp_hdr + SMB2_SIG_OFFS, SMB2_SIG_SIZE);
!         /* smb2_hmac_update or smb3_cmac_update */
!         if ((rc = ops->mac_update(ctx, tmp_hdr, tlen)) != 0)
                  return (rc);
          offset += tlen;
          resid -= tlen;
  
          /*
*** 208,218 ****
           * (typically SMB2_HDR_SIZE)
           */
          tlen = mbuf->m_len - offset;
          if (tlen > resid)
                  tlen = resid;
!         rc = smb2_hmac_update(ctx, (uint8_t *)mbuf->m_data + offset, tlen);
          if (rc != 0)
                  return (rc);
          resid -= tlen;
  
          /*
--- 301,312 ----
           * (typically SMB2_HDR_SIZE)
           */
          tlen = mbuf->m_len - offset;
          if (tlen > resid)
                  tlen = resid;
!         /* smb2_hmac_update or smb3_cmac_update */
!         rc = ops->mac_update(ctx, (uint8_t *)mbuf->m_data + offset, tlen);
          if (rc != 0)
                  return (rc);
          resid -= tlen;
  
          /*
*** 223,243 ****
                  if (mbuf == NULL)
                          return (-1);
                  tlen = mbuf->m_len;
                  if (tlen > resid)
                          tlen = resid;
!                 rc = smb2_hmac_update(ctx, (uint8_t *)mbuf->m_data, tlen);
                  if (rc != 0)
                          return (rc);
                  resid -= tlen;
          }
  
          /*
           * Note: digest is _always_ SMB2_SIG_SIZE,
           * even if the mech uses a longer one.
           */
!         if ((rc = smb2_hmac_final(ctx, digest)) != 0)
                  return (rc);
  
          return (0);
  }
  
--- 317,340 ----
                  if (mbuf == NULL)
                          return (-1);
                  tlen = mbuf->m_len;
                  if (tlen > resid)
                          tlen = resid;
!                 rc = ops->mac_update(ctx, (uint8_t *)mbuf->m_data, tlen);
                  if (rc != 0)
                          return (rc);
                  resid -= tlen;
          }
  
          /*
+          * smb2_hmac_final or smb3_cmac_final
           * Note: digest is _always_ SMB2_SIG_SIZE,
           * even if the mech uses a longer one.
+          *
+          * smb2_hmac_update or smb3_cmac_update
           */
!         if ((rc = ops->mac_final(ctx, digest)) != 0)
                  return (rc);
  
          return (0);
  }
  
*** 258,286 ****
  smb2_sign_check_request(smb_request_t *sr)
  {
          uint8_t req_sig[SMB2_SIG_SIZE];
          uint8_t vfy_sig[SMB2_SIG_SIZE];
          struct mbuf_chain *mbc = &sr->smb_data;
          smb_user_t *u = sr->uid_user;
          int sig_off;
  
          /*
           * Don't check commands with a zero session ID.
           * [MS-SMB2] 3.3.4.1.1
           */
!         if (sr->smb_uid == 0 || u == NULL)
                  return (0);
  
          /* Get the request signature. */
          sig_off = sr->smb2_cmd_hdr + SMB2_SIG_OFFS;
          if (smb_mbc_peek(mbc, sig_off, "#c", SMB2_SIG_SIZE, req_sig) != 0)
                  return (-1);
  
          /*
           * Compute the correct signature and compare.
           */
!         if (smb2_sign_calc(sr, mbc, vfy_sig) != 0)
                  return (-1);
          if (memcmp(vfy_sig, req_sig, SMB2_SIG_SIZE) != 0) {
                  cmn_err(CE_NOTE, "smb2_sign_check_request: bad signature");
                  return (-1);
          }
--- 355,389 ----
  smb2_sign_check_request(smb_request_t *sr)
  {
          uint8_t req_sig[SMB2_SIG_SIZE];
          uint8_t vfy_sig[SMB2_SIG_SIZE];
          struct mbuf_chain *mbc = &sr->smb_data;
+         smb_session_t *s = sr->session;
          smb_user_t *u = sr->uid_user;
          int sig_off;
  
          /*
           * Don't check commands with a zero session ID.
           * [MS-SMB2] 3.3.4.1.1
           */
!         if (sr->smb2_ssnid == 0 || u == NULL)
                  return (0);
  
+         /* In case _sign_begin failed. */
+         if (s->sign_calc == NULL)
+                 return (-1);
+ 
          /* Get the request signature. */
          sig_off = sr->smb2_cmd_hdr + SMB2_SIG_OFFS;
          if (smb_mbc_peek(mbc, sig_off, "#c", SMB2_SIG_SIZE, req_sig) != 0)
                  return (-1);
  
          /*
           * Compute the correct signature and compare.
+          * smb2_sign_calc() or smb3_sign_calc()
           */
!         if (s->sign_calc(sr, mbc, vfy_sig) != 0)
                  return (-1);
          if (memcmp(vfy_sig, req_sig, SMB2_SIG_SIZE) != 0) {
                  cmn_err(CE_NOTE, "smb2_sign_check_request: bad signature");
                  return (-1);
          }
*** 298,325 ****
  void
  smb2_sign_reply(smb_request_t *sr)
  {
          uint8_t reply_sig[SMB2_SIG_SIZE];
          struct mbuf_chain tmp_mbc;
          smb_user_t *u = sr->uid_user;
          int hdr_off, msg_len;
  
          if (u == NULL)
                  return;
  
          msg_len = sr->reply.chain_offset - sr->smb2_reply_hdr;
          (void) MBC_SHADOW_CHAIN(&tmp_mbc, &sr->reply,
              sr->smb2_reply_hdr, msg_len);
  
          /*
           * Calculate the MAC signature for this reply.
           */
!         if (smb2_sign_calc(sr, &tmp_mbc, reply_sig) != 0)
                  return;
  
          /*
           * Poke the signature into the response.
           */
          hdr_off = sr->smb2_reply_hdr + SMB2_SIG_OFFS;
          (void) smb_mbc_poke(&sr->reply, hdr_off, "#c",
              SMB2_SIG_SIZE, reply_sig);
  }
--- 401,492 ----
  void
  smb2_sign_reply(smb_request_t *sr)
  {
          uint8_t reply_sig[SMB2_SIG_SIZE];
          struct mbuf_chain tmp_mbc;
+         smb_session_t *s = sr->session;
          smb_user_t *u = sr->uid_user;
          int hdr_off, msg_len;
  
          if (u == NULL)
                  return;
+         if (s->sign_calc == NULL)
+                 return;
  
          msg_len = sr->reply.chain_offset - sr->smb2_reply_hdr;
          (void) MBC_SHADOW_CHAIN(&tmp_mbc, &sr->reply,
              sr->smb2_reply_hdr, msg_len);
  
          /*
           * Calculate the MAC signature for this reply.
+          * smb2_sign_calc() or smb3_sign_calc()
           */
!         if (s->sign_calc(sr, &tmp_mbc, reply_sig) != 0)
                  return;
  
          /*
           * Poke the signature into the response.
           */
          hdr_off = sr->smb2_reply_hdr + SMB2_SIG_OFFS;
          (void) smb_mbc_poke(&sr->reply, hdr_off, "#c",
              SMB2_SIG_SIZE, reply_sig);
+ }
+ 
+ /*
+  * Derive SMB3 key as described in [MS-SMB2] 3.1.4.2
+  * and [NIST SP800-108]
+  *
+  * r = 32, L = 128, PRF = HMAC-SHA256, key = (session key)
+  *
+  * Note that these describe pre-3.1.1 inputs.
+  *
+  * Session.SigningKey for binding a session:
+  * - Session.SessionKey as K1
+  * - label = SMB2AESCMAC (size 12)
+  * - context = SmbSign (size 8)
+  * Channel.SigningKey for for all other requests
+  * - if SMB2_SESSION_FLAG_BINDING, GSS key (in Session.SessionKey?) as K1;
+  * - otherwise, Session.SessionKey as K1
+  * - label = SMB2AESCMAC (size 12)
+  * - context = SmbSign (size 8)
+  * Session.ApplicationKey for ... (not sure what yet)
+  * - Session.SessionKey as K1
+  * - label = SMB2APP (size 8)
+  * - context = SmbRpc (size 7)
+  * Session.EncryptionKey for encrypting server messages
+  * - Session.SessionKey as K1
+  * - label = "SMB2AESCCM" (size 11)
+  * - context = "ServerOut" (size 10)
+  * Session.DecryptionKey for decrypting client requests
+  * - Session.SessionKey as K1
+  * - label = "SMB2AESCCM" (size 11)
+  * - context = "ServerIn " (size 10) (Note the space)
+  */
+ 
+ int
+ smb3_do_kdf(void *outbuf, void *input, size_t input_len,
+     uint8_t *key, uint32_t key_len)
+ {
+         uint8_t digest32[SHA256_DIGEST_LENGTH];
+         smb_crypto_mech_t mech;
+         smb_sign_ctx_t hctx = 0;
+         int rc;
+ 
+         bzero(&mech, sizeof (mech));
+         if ((rc = smb2_hmac_getmech(&mech)) != 0)
+                 return (rc);
+ 
+         /* Limit the SessionKey input to its maximum size (16 bytes) */
+         rc = smb2_hmac_init(&hctx, &mech, key, MIN(key_len, SMB2_KEYLEN));
+         if (rc != 0)
+                 return (rc);
+ 
+         if ((rc = smb2_hmac_update(hctx, input, input_len)) != 0)
+                 return (rc);
+ 
+         if ((rc = smb2_hmac_final(hctx, digest32)) != 0)
+                 return (rc);
+ 
+         /* Output is first 16 bytes of digest. */
+         bcopy(digest32, outbuf, SMB3_KEYLEN);
+         return (0);
  }