From: Stefan Metzmacher Date: Fri, 5 Mar 2021 15:19:08 +0000 (+0100) Subject: libcli/smb: add smb2_signing_key_{copy,sign_create,cipher_create}() helpers X-Git-Tag: tevent-0.11.0~1505 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5b648fe96027543503e9ad800370bcfee5ca5ff6;p=thirdparty%2Fsamba.git libcli/smb: add smb2_signing_key_{copy,sign_create,cipher_create}() helpers These will simplify the callers a lot. In important part is to also remember the sign and cipher algo ids. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14512 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- diff --git a/libcli/smb/smb2_constants.h b/libcli/smb/smb2_constants.h index 64d5ec697cb..7d48ad4bb13 100644 --- a/libcli/smb/smb2_constants.h +++ b/libcli/smb/smb2_constants.h @@ -138,10 +138,13 @@ #define SMB2_PREAUTH_INTEGRITY_SHA512 0x0001 #define SMB2_SIGNING_INVALID_ALGO 0xffff /* only used internally */ +#define SMB2_SIGNING_MD5_SMB1 0xfffe /* internally for SMB1 */ #define SMB2_SIGNING_HMAC_SHA256 0x0000 /* default <= 0x210 */ #define SMB2_SIGNING_AES128_CMAC 0x0001 /* default >= 0x224 */ /* Values for the SMB2_ENCRYPTION_CAPABILITIES Context (>= 0x310) */ +#define SMB2_ENCRYPTION_INVALID_ALGO 0xffff /* only used internally */ +#define SMB2_ENCRYPTION_NONE 0x0000 /* only used internally */ #define SMB2_ENCRYPTION_AES128_CCM 0x0001 /* only in dialect >= 0x224 */ #define SMB2_ENCRYPTION_AES128_GCM 0x0002 /* only in dialect >= 0x310 */ #define SMB2_NONCE_HIGH_MAX(nonce_len_bytes) ((uint64_t)(\ diff --git a/libcli/smb/smb2_signing.c b/libcli/smb/smb2_signing.c index 6e1b50ba49a..95091bffb1b 100644 --- a/libcli/smb/smb2_signing.c +++ b/libcli/smb/smb2_signing.c @@ -105,6 +105,191 @@ int smb2_signing_key_destructor(struct smb2_signing_key *key) return 0; } +NTSTATUS smb2_signing_key_copy(TALLOC_CTX *mem_ctx, + const struct smb2_signing_key *src, + struct smb2_signing_key **_dst) +{ + struct smb2_signing_key *dst = NULL; + + dst = talloc_zero(mem_ctx, struct smb2_signing_key); + if (dst == NULL) { + return NT_STATUS_NO_MEMORY; + } + talloc_set_destructor(dst, smb2_signing_key_destructor); + + dst->sign_algo_id = src->sign_algo_id; + dst->cipher_algo_id = src->cipher_algo_id; + + if (src->blob.length == 0) { + *_dst = dst; + return NT_STATUS_OK; + } + + dst->blob = data_blob_talloc_zero(dst, src->blob.length); + if (dst->blob.length == 0) { + TALLOC_FREE(dst); + return NT_STATUS_NO_MEMORY; + } + talloc_keep_secret(dst->blob.data); + memcpy(dst->blob.data, src->blob.data, dst->blob.length); + + *_dst = dst; + return NT_STATUS_OK; +} + +static NTSTATUS smb2_signing_key_create(TALLOC_CTX *mem_ctx, + uint16_t sign_algo_id, + uint16_t cipher_algo_id, + const DATA_BLOB *master_key, + const struct smb2_signing_derivation *d, + struct smb2_signing_key **_key) +{ + struct smb2_signing_key *key = NULL; + size_t in_key_length = 16; + size_t out_key_length = 16; + NTSTATUS status; + + if (sign_algo_id != SMB2_SIGNING_INVALID_ALGO) { + SMB_ASSERT(cipher_algo_id == SMB2_ENCRYPTION_INVALID_ALGO); + } + if (cipher_algo_id != SMB2_ENCRYPTION_INVALID_ALGO) { + SMB_ASSERT(sign_algo_id == SMB2_SIGNING_INVALID_ALGO); + } + + key = talloc_zero(mem_ctx, struct smb2_signing_key); + if (key == NULL) { + return NT_STATUS_NO_MEMORY; + } + talloc_set_destructor(key, smb2_signing_key_destructor); + + key->sign_algo_id = sign_algo_id; + key->cipher_algo_id = cipher_algo_id; + + if (master_key == NULL) { + SMB_ASSERT(d == NULL); + + *_key = key; + return NT_STATUS_OK; + } + + /* + * Per default use the full key. + */ + in_key_length = out_key_length = master_key->length; + switch (sign_algo_id) { + case SMB2_SIGNING_INVALID_ALGO: + /* + * This means we're processing cipher_algo_id below + */ + break; + case SMB2_SIGNING_MD5_SMB1: + SMB_ASSERT(d == NULL); + break; + case SMB2_SIGNING_HMAC_SHA256: + case SMB2_SIGNING_AES128_CMAC: + /* + * signing keys are padded or truncated to + * 16 bytes. + * + * Even with master_key->length = 0, + * we need to use 16 zeros. + */ + in_key_length = out_key_length = 16; + break; + default: + DBG_ERR("sign_algo_id[%u] not supported\n", sign_algo_id); + return NT_STATUS_HMAC_NOT_SUPPORTED; + } + switch (cipher_algo_id) { + case SMB2_ENCRYPTION_INVALID_ALGO: + /* + * This means we're processing sign_algo_id above + */ + break; + case SMB2_ENCRYPTION_NONE: + /* + * No encryption negotiated. + */ + break; + case SMB2_ENCRYPTION_AES128_CCM: + case SMB2_ENCRYPTION_AES128_GCM: + /* + * encryption keys are padded or truncated to + * 16 bytes. + */ + if (master_key->length == 0) { + DBG_ERR("cipher_algo_id[%u] without key\n", + cipher_algo_id); + return NT_STATUS_NO_USER_SESSION_KEY; + } + in_key_length = out_key_length = 16; + break; + default: + DBG_ERR("cipher_algo_id[%u] not supported\n", cipher_algo_id); + return NT_STATUS_FWP_INCOMPATIBLE_CIPHER_CONFIG; + } + + if (out_key_length == 0) { + *_key = key; + return NT_STATUS_OK; + } + + key->blob = data_blob_talloc_zero(key, out_key_length); + if (key->blob.length == 0) { + TALLOC_FREE(key); + return NT_STATUS_NO_MEMORY; + } + talloc_keep_secret(key->blob.data); + memcpy(key->blob.data, + master_key->data, + MIN(key->blob.length, master_key->length)); + + if (d == NULL) { + *_key = key; + return NT_STATUS_OK; + } + + status = smb2_key_derivation(key->blob.data, in_key_length, + d->label.data, d->label.length, + d->context.data, d->context.length, + key->blob.data, out_key_length); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(key); + return status; + } + + *_key = key; + return NT_STATUS_OK; +} + +NTSTATUS smb2_signing_key_sign_create(TALLOC_CTX *mem_ctx, + uint16_t sign_algo_id, + const DATA_BLOB *master_key, + const struct smb2_signing_derivation *d, + struct smb2_signing_key **_key) +{ + return smb2_signing_key_create(mem_ctx, + sign_algo_id, + SMB2_ENCRYPTION_INVALID_ALGO, + master_key, + d, + _key); +} + +NTSTATUS smb2_signing_key_cipher_create(TALLOC_CTX *mem_ctx, + uint16_t cipher_algo_id, + const DATA_BLOB *master_key, + const struct smb2_signing_derivation *d, + struct smb2_signing_key **_key) +{ + return smb2_signing_key_create(mem_ctx, + SMB2_SIGNING_INVALID_ALGO, + cipher_algo_id, + master_key, + d, + _key); +} + bool smb2_signing_key_valid(const struct smb2_signing_key *key) { if (key == NULL) { diff --git a/libcli/smb/smb2_signing.h b/libcli/smb/smb2_signing.h index 0a80467717e..099df051ddd 100644 --- a/libcli/smb/smb2_signing.h +++ b/libcli/smb/smb2_signing.h @@ -45,12 +45,14 @@ void smb2_signing_derivations_fill_const_stack(struct smb2_signing_derivations * struct smb2_signing_key { DATA_BLOB blob; + uint16_t sign_algo_id; union { #ifdef SMB2_SIGNING_KEY_GNUTLS_TYPES gnutls_hmac_hd_t hmac_hnd; #endif void *__hmac_hnd; }; + uint16_t cipher_algo_id; union { #ifdef SMB2_SIGNING_KEY_GNUTLS_TYPES gnutls_aead_cipher_hd_t cipher_hnd; @@ -61,6 +63,20 @@ struct smb2_signing_key { int smb2_signing_key_destructor(struct smb2_signing_key *key); +NTSTATUS smb2_signing_key_copy(TALLOC_CTX *mem_ctx, + const struct smb2_signing_key *src, + struct smb2_signing_key **_dst); +NTSTATUS smb2_signing_key_sign_create(TALLOC_CTX *mem_ctx, + uint16_t sign_algo_id, + const DATA_BLOB *master_key, + const struct smb2_signing_derivation *d, + struct smb2_signing_key **_key); +NTSTATUS smb2_signing_key_cipher_create(TALLOC_CTX *mem_ctx, + uint16_t cipher_algo_id, + const DATA_BLOB *master_key, + const struct smb2_signing_derivation *d, + struct smb2_signing_key **_key); + bool smb2_signing_key_valid(const struct smb2_signing_key *key); NTSTATUS smb2_signing_sign_pdu(struct smb2_signing_key *signing_key,