diff --git a/src/utils/common/tls.c b/src/utils/common/tls.c index 3f2564787029c4def9edbc5e0b222125ff0de93a..f6b34b53c87dde879c0c96c9290a348fc1ac5a52 100644 --- a/src/utils/common/tls.c +++ b/src/utils/common/tls.c @@ -23,6 +23,7 @@ #include "utils/common/tls.h" #include "utils/common/cert.h" #include "utils/common/msg.h" +#include "contrib/base64.h" #include "libknot/errcode.h" void tls_params_init(tls_params_t *params) @@ -99,11 +100,29 @@ void tls_params_clean(tls_params_t *params) memset(params, 0, sizeof(*params)); } -static int compare_pin(gnutls_session_t session, uint8_t *pin, size_t pin_len) +static bool check_pin(const uint8_t *cert_pin, size_t cert_pin_len, const list_t *pins) +{ + if (EMPTY_LIST(*pins)) { + return false; + } + + ptrnode_t *n = NULL; + WALK_LIST(n, *pins) { + uint8_t *pin = (uint8_t *)n->d; + if (pin[0] == cert_pin_len && + memcmp(cert_pin, &pin[1], cert_pin_len) == 0) { + return true; + } + } + + return false; +} + +static int check_certificates(gnutls_session_t session, const list_t *pins) { if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) { DBG("TLS, invalid certificate type\n"); - return -1; + return GNUTLS_E_CERTIFICATE_ERROR; } unsigned cert_list_size; @@ -111,37 +130,68 @@ static int compare_pin(gnutls_session_t session, uint8_t *pin, size_t pin_len) gnutls_certificate_get_peers(session, &cert_list_size); if (cert_list == NULL || cert_list_size == 0) { DBG("TLS, empty certificate list\n"); - return -1; + return GNUTLS_E_CERTIFICATE_ERROR; } + size_t matches = 0; + + DBG("TLS, received certificate hierarchy:\n"); for (int i = 0; i < cert_list_size; i++) { gnutls_x509_crt_t cert; int ret = gnutls_x509_crt_init(&cert); if (ret != GNUTLS_E_SUCCESS) { - return -1; + return ret; } ret = gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER); if (ret != GNUTLS_E_SUCCESS) { gnutls_x509_crt_deinit(cert); - return -1; + return ret; + } + + gnutls_datum_t cert_name = { 0 }; + ret = gnutls_x509_crt_get_dn2(cert, &cert_name); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_x509_crt_deinit(cert); + return ret; } + DBG(" #%i, %s\n", i + 1, cert_name.data); + gnutls_free(cert_name.data); uint8_t cert_pin[CERT_PIN_LEN] = { 0 }; ret = cert_get_pin(cert, cert_pin, sizeof(cert_pin)); if (ret != KNOT_EOK) { gnutls_x509_crt_deinit(cert); - return -1; + return GNUTLS_E_CERTIFICATE_ERROR; + } + + // Check if correspond to a specified PIN. + bool match = check_pin(cert_pin, sizeof(cert_pin), pins); + if (match) { + matches++; } - gnutls_x509_crt_deinit(cert); - if (pin_len == sizeof(cert_pin) && - memcmp(cert_pin, pin, sizeof(cert_pin)) == 0) { - return 0; // PIN matches. + uint8_t *txt_pin; + ret = base64_encode_alloc(cert_pin, sizeof(cert_pin), &txt_pin); + if (ret < 0) { + gnutls_x509_crt_deinit(cert); + return ret; } + DBG(" SHA-256 PIN: %.*s%s\n", ret, txt_pin, match ? ", MATCH" : ""); + free(txt_pin); + + gnutls_x509_crt_deinit(cert); } - return -1; + if (matches > 0) { + return GNUTLS_E_SUCCESS; + } else if (EMPTY_LIST(*pins)) { + DBG("TLS, skipping certificate PIN check\n"); + return GNUTLS_E_SUCCESS; + } else { + DBG("TLS, no certificate PIN match\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } } static bool do_verification(const tls_params_t *params) @@ -154,22 +204,10 @@ static int verify_certificate(gnutls_session_t session) { tls_ctx_t *ctx = gnutls_session_get_ptr(session); - // Check for pinned certificates. - if (!EMPTY_LIST(ctx->params->pins)) { - bool match = false; - ptrnode_t *n = NULL; - WALK_LIST(n, ctx->params->pins) { - uint8_t *pin = (uint8_t *)n->d; - if (compare_pin(session, &pin[1], pin[0]) == 0) { - DBG("TLS, certificate PIN match\n"); - match = true; - break; - } - } - if (!match) { - DBG("TLS, no certificate PIN match\n"); - return GNUTLS_E_CERTIFICATE_ERROR; - } + // Check for pinned certificates and print certificate hierarchy. + int ret = check_certificates(session, &ctx->params->pins); + if (ret != GNUTLS_E_SUCCESS) { + return ret; } if (!do_verification(ctx->params)) { @@ -187,7 +225,7 @@ static int verify_certificate(gnutls_session_t session) size_t data_count = (ctx->params->hostname != NULL) ? 2 : 1; unsigned int status; - int ret = gnutls_certificate_verify_peers(session, data, data_count, &status); + ret = gnutls_certificate_verify_peers(session, data, data_count, &status); if (ret != GNUTLS_E_SUCCESS) { WARN("TLS, failed to verify peer certificate\n"); return GNUTLS_E_CERTIFICATE_ERROR;