diff --git a/src/openssl/tls-internal.h b/src/openssl/tls-internal.h index f13c8602..59b5b292 100644 --- a/src/openssl/tls-internal.h +++ b/src/openssl/tls-internal.h @@ -34,6 +34,10 @@ #ifndef _GETDNS_TLS_INTERNAL_H #define _GETDNS_TLS_INTERNAL_H +#include +#include +#include + #include "getdns/getdns.h" #ifndef HAVE_DECL_SSL_CTX_SET1_CURVES_LIST @@ -64,4 +68,12 @@ typedef struct _getdns_tls_x509 X509* ssl; } _getdns_tls_x509; +typedef struct _getdns_tls_hmac +{ + HMAC_CTX *ctx; +#ifndef HAVE_HMAC_CTX_NEW + HMAC_CTX ctx_space; +#endif +} _getdns_tls_hmac; + #endif /* _GETDNS_TLS_INTERNAL_H */ diff --git a/src/openssl/tls.c b/src/openssl/tls.c index 36a0f9a3..611fbf3b 100644 --- a/src/openssl/tls.c +++ b/src/openssl/tls.c @@ -609,4 +609,126 @@ int _getdns_tls_x509_to_der(_getdns_tls_x509* cert, uint8_t** buf) return i2d_X509(cert->ssl, buf); } +unsigned char* _getdns_tls_hmac_hash(int algorithm, const void* key, size_t key_size, const void* data, size_t data_size, size_t* output_size) +{ + const EVP_MD* digester; + unsigned char* res; + unsigned int md_len; + + switch (algorithm) { +#ifdef HAVE_EVP_MD5 + case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break; +#endif +#ifdef HAVE_EVP_SHA1 + case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break; +#endif +#ifdef HAVE_EVP_SHA224 + case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break; +#endif +#ifdef HAVE_EVP_SHA256 + case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break; +#endif +#ifdef HAVE_EVP_SHA384 + case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break; +#endif +#ifdef HAVE_EVP_SHA512 + case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break; +#endif + default : return NULL; + } + + res = (unsigned char*) malloc(EVP_MAX_MD_SIZE); + if (!res) + return NULL; + + (void) HMAC(digester, key, key_size, data, data_size, res, &md_len); + + if (output_size) + *output_size = md_len; + return res; +} + +_getdns_tls_hmac* _getdns_tls_hmac_new(int algorithm, const void* key, size_t key_size) +{ + const EVP_MD *digester; + _getdns_tls_hmac* res; + + switch (algorithm) { +#ifdef HAVE_EVP_MD5 + case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break; +#endif +#ifdef HAVE_EVP_SHA1 + case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break; +#endif +#ifdef HAVE_EVP_SHA224 + case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break; +#endif +#ifdef HAVE_EVP_SHA256 + case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break; +#endif +#ifdef HAVE_EVP_SHA384 + case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break; +#endif +#ifdef HAVE_EVP_SHA512 + case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break; +#endif + default : return NULL; + } + + if (!(res = malloc(sizeof(struct _getdns_tls_hmac)))) + return NULL; + +#ifdef HAVE_HMAC_CTX_NEW + res->ctx = HMAC_CTX_new(); + if (!res->ctx) { + free(res); + return NULL; + } +#else + res->ctx = &res->ctx_space; + HMAC_CTX_init(res->ctx); +#endif + if (!HMAC_Init_ex(res->ctx, key, key_size, digester, NULL)) { +#ifdef HAVE_HMAC_CTX_NEW + HMAC_CTX_free(res->ctx); +#endif + free(res); + return NULL; + } + + return res; +} + +getdns_return_t _getdns_tls_hmac_add(_getdns_tls_hmac* h, const void* data, size_t data_size) +{ + if (!h || !h->ctx || !data) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (!HMAC_Update(h->ctx, data, data_size)) + return GETDNS_RETURN_GENERIC_ERROR; + else + return GETDNS_RETURN_GOOD; +} + +unsigned char* _getdns_tls_hmac_end(_getdns_tls_hmac* h, size_t* output_size) +{ + unsigned char* res; + unsigned int md_len; + + res = (unsigned char*) malloc(EVP_MAX_MD_SIZE); + if (!res) + return NULL; + + (void) HMAC_Final(h->ctx, res, &md_len); + +#ifdef HAVE_HMAC_CTX_NEW + HMAC_CTX_free(h->ctx); +#endif + free(h); + + if (output_size) + *output_size = md_len; + return res; +} + /* tls.c */ diff --git a/src/request-internal.c b/src/request-internal.c index 89476717..1b2a7cb4 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -401,9 +401,8 @@ _getdns_network_req_add_tsig(getdns_network_req *req) gldns_buffer gbuf; uint16_t arcount; const getdns_tsig_info *tsig_info; - uint8_t md_buf[EVP_MAX_MD_SIZE]; - unsigned int md_len = EVP_MAX_MD_SIZE; - const EVP_MD *digester; + unsigned char* md_buf; + size_t md_len; /* Should only be called when in stub mode */ assert(req->query); @@ -436,31 +435,9 @@ _getdns_network_req_add_tsig(getdns_network_req *req) gldns_buffer_write_u16(&gbuf, 0); /* Error */ gldns_buffer_write_u16(&gbuf, 0); /* Other len */ - switch (upstream->tsig_alg) { -#ifdef HAVE_EVP_MD5 - case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break; -#endif -#ifdef HAVE_EVP_SHA1 - case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break; -#endif -#ifdef HAVE_EVP_SHA224 - case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break; -#endif -#ifdef HAVE_EVP_SHA256 - case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break; -#endif -#ifdef HAVE_EVP_SHA384 - case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break; -#endif -#ifdef HAVE_EVP_SHA512 - case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break; -#endif - default : return req->response - req->query; - } - - (void) HMAC(digester, upstream->tsig_key, upstream->tsig_size, - (void *)req->query, gldns_buffer_current(&gbuf) - req->query, - md_buf, &md_len); + md_buf = _getdns_tls_hmac_hash(upstream->tsig_alg, upstream->tsig_key, upstream->tsig_size, (void *)req->query, gldns_buffer_current(&gbuf) - req->query, &md_len); + if (!md_buf) + return req->response - req->query; gldns_buffer_rewind(&gbuf); gldns_buffer_write(&gbuf, @@ -480,6 +457,8 @@ _getdns_network_req_add_tsig(getdns_network_req *req) gldns_buffer_write_u16(&gbuf, 0); /* Error */ gldns_buffer_write_u16(&gbuf, 0); /* Other len */ + free(md_buf); + if (gldns_buffer_position(&gbuf) > gldns_buffer_limit(&gbuf)) return req->response - req->query; @@ -506,14 +485,10 @@ _getdns_network_validate_tsig(getdns_network_req *req) const uint8_t *response_mac; uint16_t response_mac_len; uint8_t other_len; - uint8_t result_mac[EVP_MAX_MD_SIZE]; - unsigned int result_mac_len = EVP_MAX_MD_SIZE; + unsigned char *result_mac; + size_t result_mac_len; uint16_t original_id; - const EVP_MD *digester; - HMAC_CTX *ctx; -#ifndef HAVE_HMAC_CTX_NEW - HMAC_CTX ctx_space; -#endif + _getdns_tls_hmac *hmac; DEBUG_STUB("%s %-35s: Validate TSIG\n", STUB_DEBUG_TSIG, __FUNC__); for ( rr = _getdns_rr_iter_init(&rr_spc, req->query, @@ -620,39 +595,16 @@ _getdns_network_validate_tsig(getdns_network_req *req) gldns_read_uint16(req->response + 10) - 1); gldns_write_uint16(req->response, original_id); - switch (req->upstream->tsig_alg) { -#ifdef HAVE_EVP_MD5 - case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break; -#endif -#ifdef HAVE_EVP_SHA1 - case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break; -#endif -#ifdef HAVE_EVP_SHA224 - case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break; -#endif -#ifdef HAVE_EVP_SHA256 - case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break; -#endif -#ifdef HAVE_EVP_SHA384 - case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break; -#endif -#ifdef HAVE_EVP_SHA512 - case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break; -#endif - default : return; - } -#ifdef HAVE_HMAC_CTX_NEW - ctx = HMAC_CTX_new(); -#else - ctx = &ctx_space; - HMAC_CTX_init(ctx); -#endif - (void) HMAC_Init_ex(ctx, req->upstream->tsig_key, - req->upstream->tsig_size, digester, NULL); - (void) HMAC_Update(ctx, request_mac - 2, request_mac_len + 2); - (void) HMAC_Update(ctx, req->response, rr->pos - req->response); - (void) HMAC_Update(ctx, tsig_vars, gldns_buffer_position(&gbuf)); - HMAC_Final(ctx, result_mac, &result_mac_len); + hmac = _getdns_tls_hmac_new(req->upstream->tsig_alg, req->upstream->tsig_key, req->upstream->tsig_size); + if (!hmac) + return; + + _getdns_tls_hmac_add(hmac, request_mac - 2, request_mac_len + 2); + _getdns_tls_hmac_add(hmac, req->response, rr->pos - req->response); + _getdns_tls_hmac_add(hmac, tsig_vars, gldns_buffer_position(&gbuf)); + result_mac = _getdns_tls_hmac_end(hmac, &result_mac_len); + if (!result_mac) + return; DEBUG_STUB("%s %-35s: Result MAC length: %d\n", STUB_DEBUG_TSIG, __FUNC__, (int)(result_mac_len)); @@ -660,11 +612,6 @@ _getdns_network_validate_tsig(getdns_network_req *req) memcmp(result_mac, response_mac, result_mac_len) == 0) req->tsig_status = GETDNS_DNSSEC_SECURE; -#ifdef HAVE_HMAC_CTX_FREE - HMAC_CTX_free(ctx); -#else - HMAC_CTX_cleanup(ctx); -#endif gldns_write_uint16(req->response, gldns_read_uint16(req->query)); gldns_write_uint16(req->response + 10, gldns_read_uint16(req->response + 10) + 1); diff --git a/src/tls.h b/src/tls.h index cc654430..a70ea1bb 100644 --- a/src/tls.h +++ b/src/tls.h @@ -316,4 +316,48 @@ int _getdns_tls_x509_to_der(_getdns_tls_x509* cert, uint8_t** buf); */ getdns_return_t _getdns_tls_get_api_information(getdns_dict* dict); +/** + * Return buffer with HMAC hash. + * + * @param algorithm hash algorithm to use (GETDNS_HMAC_?). + * @param key the key. + * @param key_size the key size. + * @param data the data to hash. + * @param data_size the data size. + * @param output_size the output size will be written here if not NULL. + * @return output malloc'd buffer with output, NULL on error. + */ +unsigned char* _getdns_tls_hmac_hash(int algorithm, const void* key, size_t key_size, const void* data, size_t data_size, size_t* output_size); + +/** + * Return a new HMAC handle. + * + * @param algorithm hash algorithm to use (GETDNS_HMAC_?). + * @param key the key. + * @param key_size the key size. + * @return HMAC handle or NULL on error. + */ +_getdns_tls_hmac* _getdns_tls_hmac_new(int algorithm, const void* key, size_t key_size); + +/** + * Add data to a HMAC. + * + * @param h the HMAC. + * @param data the data to add. + * @param data_size the size of data to add. + * @return GETDNS_RETURN_GOOD if added. + * @return GETDNS_RETURN_INVALID_PARAMETER if h is null or has no HMAC. + * @return GETDNS_RETURN_GENERIC_ERROR on error. + */ +getdns_return_t _getdns_tls_hmac_add(_getdns_tls_hmac* h, const void* data, size_t data_size); + +/** + * Return the HMAC digest and free the handle. + * + * @param h the HMAC. + * @param output_size the output size will be written here if not NULL. + * @return output malloc'd buffer with output, NULL on error. + */ +unsigned char* _getdns_tls_hmac_end(_getdns_tls_hmac* h, size_t* output_size); + #endif /* _GETDNS_TLS_H */