libcli/smb: add support for SMB2_SIGNING_AES128_GMAC
authorStefan Metzmacher <metze@samba.org>
Wed, 11 Nov 2020 13:10:01 +0000 (14:10 +0100)
committerStefan Metzmacher <metze@samba.org>
Thu, 15 Jul 2021 00:06:31 +0000 (00:06 +0000)
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
libcli/smb/smb2_negotiate_context.h
libcli/smb/smb2_signing.c
libcli/smb/util.c

index e08bf424d950326e9cdd2f92178f2dfe6014de1d..645fb64a37706a25d38d27a2719aec78bfb1de08 100644 (file)
@@ -57,7 +57,7 @@ struct smb2_negotiate_context *smb2_negotiate_context_find(const struct smb2_neg
 #define WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK    31
 
 struct smb3_signing_capabilities {
-#define SMB3_SIGNING_CAPABILITIES_MAX_ALGOS 2
+#define SMB3_SIGNING_CAPABILITIES_MAX_ALGOS 3
        uint16_t num_algos;
        uint16_t algos[SMB3_SIGNING_CAPABILITIES_MAX_ALGOS];
 };
index 91b10d4191814f50a844f8484b9b5f0faeab1d89..830f3bf1570ce523fc64844764ff199b1e2e6b0e 100644 (file)
@@ -187,6 +187,7 @@ static NTSTATUS smb2_signing_key_create(TALLOC_CTX *mem_ctx,
                break;
        case SMB2_SIGNING_HMAC_SHA256:
        case SMB2_SIGNING_AES128_CMAC:
+       case SMB2_SIGNING_AES128_GMAC:
                /*
                 * signing keys are padded or truncated to
                 * 16 bytes.
@@ -316,6 +317,79 @@ bool smb2_signing_key_valid(const struct smb2_signing_key *key)
        return true;
 }
 
+static NTSTATUS smb2_signing_gmac(gnutls_aead_cipher_hd_t cipher_hnd,
+                                 const uint8_t *iv, size_t iv_size,
+                                 const giovec_t *auth_iov, uint8_t auth_iovcnt,
+                                 uint8_t *tag, size_t _tag_size)
+{
+       size_t tag_size = _tag_size;
+       int rc;
+#if defined(HAVE_GNUTLS_AEAD_CIPHER_ENCRYPTV2)
+
+       rc = gnutls_aead_cipher_encryptv2(cipher_hnd,
+                                         iv, iv_size,
+                                         auth_iov, auth_iovcnt,
+                                         NULL, 0,
+                                         tag, &tag_size);
+       if (rc < 0) {
+               return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+       }
+
+       return NT_STATUS_OK;
+#else /* HAVE_GNUTLS_AEAD_CIPHER_ENCRYPTV2 */
+       TALLOC_CTX *tmp_ctx = NULL;
+       size_t atext_size = 0;
+       uint8_t *atext = NULL;
+       size_t len = 0;
+       size_t i;
+
+       /*
+        * If we come from python bindings, we don't have a stackframe
+        * around, so use the NULL context.
+        *
+        * This is fine as we make sure we free the memory.
+        */
+       if (talloc_stackframe_exists()) {
+               tmp_ctx = talloc_tos();
+       }
+
+       for (i=0; i < auth_iovcnt; i++) {
+               atext_size += auth_iov[i].iov_len;
+       }
+
+       atext = talloc_size(tmp_ctx, atext_size);
+       if (atext == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i = 0; i < auth_iovcnt; i++) {
+               memcpy(atext + len,
+                      auth_iov[i].iov_base,
+                      auth_iov[i].iov_len);
+
+               len += auth_iov[i].iov_len;
+               if (len > atext_size) {
+                       TALLOC_FREE(atext);
+                       return NT_STATUS_INTERNAL_ERROR;
+               }
+       }
+
+       rc = gnutls_aead_cipher_encrypt(cipher_hnd,
+                                       iv, iv_size,
+                                       atext,
+                                       atext_size,
+                                       tag_size,
+                                       NULL, 0,
+                                       tag, &tag_size);
+       TALLOC_FREE(atext);
+       if (rc < 0) {
+               return gnutls_error_to_ntstatus(rc, NT_STATUS_HMAC_NOT_SUPPORTED);
+       }
+
+       return NT_STATUS_OK;
+#endif /* HAVE_GNUTLS_AEAD_CIPHER_ENCRYPTV2 */
+}
+
 static NTSTATUS smb2_signing_calc_signature(struct smb2_signing_key *signing_key,
                                            uint16_t sign_algo_id,
                                            const struct iovec *vector,
@@ -365,6 +439,72 @@ static NTSTATUS smb2_signing_calc_signature(struct smb2_signing_key *signing_key
        }
 
        switch (sign_algo_id) {
+       case SMB2_SIGNING_AES128_GMAC: {
+               gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_128_GCM;
+               uint32_t key_size = gnutls_cipher_get_key_size(algo);
+               uint32_t iv_size = gnutls_cipher_get_iv_size(algo);
+               size_t tag_size = gnutls_cipher_get_tag_size(algo);
+               gnutls_datum_t key = {
+                       .data = signing_key->blob.data,
+                       .size = MIN(signing_key->blob.length, key_size),
+               };
+               uint64_t high_bits = 0;
+               uint8_t iv[AES_BLOCK_SIZE] = {0};
+               giovec_t auth_iov[count+1];
+               size_t auth_iovcnt = 0;
+               NTSTATUS status;
+               int rc;
+
+               high_bits = flags & SMB2_HDR_FLAG_REDIRECT;
+               if (opcode == SMB2_OP_CANCEL) {
+                       high_bits |= SMB2_HDR_FLAG_ASYNC;
+               }
+               SBVAL(iv, 0, msg_id);
+               SBVAL(iv, 8, high_bits);
+
+               if (signing_key->cipher_hnd == NULL) {
+                       rc = gnutls_aead_cipher_init(&signing_key->cipher_hnd,
+                                                    algo,
+                                                    &key);
+                       if (rc < 0) {
+                               return gnutls_error_to_ntstatus(rc,
+                                               NT_STATUS_HMAC_NOT_SUPPORTED);
+                       }
+               }
+
+               SMB_ASSERT(key_size == 16);
+               SMB_ASSERT(iv_size == 12);
+               SMB_ASSERT(tag_size == 16);
+
+               auth_iov[auth_iovcnt++] = (giovec_t) {
+                       .iov_base = discard_const_p(uint8_t, hdr),
+                       .iov_len  = SMB2_HDR_SIGNATURE,
+               };
+               auth_iov[auth_iovcnt++] = (giovec_t) {
+                       .iov_base = discard_const_p(uint8_t, zero_sig),
+                       .iov_len  = 16,
+               };
+               for (i=1; i < count; i++) {
+                       auth_iov[auth_iovcnt++] = (giovec_t) {
+                               .iov_base = discard_const_p(uint8_t, vector[i].iov_base),
+                               .iov_len  = vector[i].iov_len,
+                       };
+               }
+
+               status = smb2_signing_gmac(signing_key->cipher_hnd,
+                                          iv,
+                                          iv_size,
+                                          auth_iov,
+                                          auth_iovcnt,
+                                          signature,
+                                          tag_size);
+               if (!NT_STATUS_IS_OK(status)) {
+                       return status;
+               }
+
+               return NT_STATUS_OK;
+       }       break;
+
        case SMB2_SIGNING_AES128_CMAC:
 #ifdef HAVE_GNUTLS_AES_CMAC
                hmac_algo = GNUTLS_MAC_AES_CMAC_128;
index 4cb47aafd072b78c6250aec972713eba2ea73ec8..061f478c92d947e67cf1389c02369bb4114adf6a 100644 (file)
@@ -466,6 +466,7 @@ enum smb_encryption_setting smb_encryption_setting_translate(const char *str)
 }
 
 static const struct enum_list enum_smb3_signing_algorithms[] = {
+       {SMB2_SIGNING_AES128_GMAC, "aes-128-gmac"},
        {SMB2_SIGNING_AES128_CMAC, "aes-128-cmac"},
        {SMB2_SIGNING_HMAC_SHA256, "hmac-sha-256"},
        {-1, NULL}