diff --git a/include/freerdp/crypto/certificate.h b/include/freerdp/crypto/certificate.h index d16f90334..26a373db8 100644 --- a/include/freerdp/crypto/certificate.h +++ b/include/freerdp/crypto/certificate.h @@ -65,6 +65,8 @@ extern "C" FREERDP_API char* freerdp_certificate_get_fingerprint(const rdpCertificate* certificate); FREERDP_API char* freerdp_certificate_get_pem(const rdpCertificate* certificate, size_t* pLength); + FREERDP_API char* freerdp_certificate_get_pem_ex(const rdpCertificate* certificate, + size_t* pLength, BOOL withCertChain); FREERDP_API BYTE* freerdp_certificate_get_der(const rdpCertificate* certificate, size_t* pLength); diff --git a/include/freerdp/crypto/certificate_data.h b/include/freerdp/crypto/certificate_data.h index 275d65481..96a620dc8 100644 --- a/include/freerdp/crypto/certificate_data.h +++ b/include/freerdp/crypto/certificate_data.h @@ -60,6 +60,8 @@ extern "C" FREERDP_API UINT16 freerdp_certificate_data_get_port(const rdpCertificateData* cert); FREERDP_API const char* freerdp_certificate_data_get_pem(const rdpCertificateData* cert); + FREERDP_API const char* freerdp_certificate_data_get_pem_ex(const rdpCertificateData* cert, + BOOL withFullChain); FREERDP_API const char* freerdp_certificate_data_get_subject(const rdpCertificateData* cert); FREERDP_API const char* freerdp_certificate_data_get_issuer(const rdpCertificateData* cert); FREERDP_API const char* diff --git a/libfreerdp/crypto/certificate.c b/libfreerdp/crypto/certificate.c index 7bdc1c6f2..ffe281ba5 100644 --- a/libfreerdp/crypto/certificate.c +++ b/libfreerdp/crypto/certificate.c @@ -1425,6 +1425,12 @@ fail: } char* freerdp_certificate_get_pem(const rdpCertificate* cert, size_t* pLength) +{ + return freerdp_certificate_get_pem_ex(cert, pLength, TRUE); +} + +char* freerdp_certificate_get_pem_ex(const rdpCertificate* cert, size_t* pLength, + BOOL withCertChain) { char* pem = NULL; WINPR_ASSERT(cert); @@ -1455,7 +1461,7 @@ char* freerdp_certificate_get_pem(const rdpCertificate* cert, size_t* pLength) goto fail; } - if (cert->chain) + if (cert->chain && withCertChain) { int count = sk_X509_num(cert->chain); for (int x = 0; x < count; x++) diff --git a/libfreerdp/crypto/certificate_data.c b/libfreerdp/crypto/certificate_data.c index 04b5432e5..79b38284f 100644 --- a/libfreerdp/crypto/certificate_data.c +++ b/libfreerdp/crypto/certificate_data.c @@ -48,6 +48,7 @@ struct rdp_certificate_data char* cached_issuer; char* cached_fingerprint; char* cached_pem; + char* cached_pem_chain; }; /* ensure our hostnames (and therefore filenames) always use the same capitalization. @@ -83,10 +84,15 @@ static BOOL freerdp_certificate_data_load_cache(rdpCertificateData* data) data->cached_subject = calloc(1, 1); size_t pemlen = 0; - data->cached_pem = freerdp_certificate_get_pem(data->cert, &pemlen); + data->cached_pem = freerdp_certificate_get_pem_ex(data->cert, &pemlen, FALSE); if (!data->cached_pem) goto fail; + size_t pemchainlen = 0; + data->cached_pem_chain = freerdp_certificate_get_pem_ex(data->cert, &pemchainlen, TRUE); + if (!data->cached_pem_chain) + goto fail; + data->cached_fingerprint = freerdp_certificate_get_fingerprint(data->cert); if (!data->cached_fingerprint) goto fail; @@ -179,6 +185,7 @@ void freerdp_certificate_data_free(rdpCertificateData* data) free(data->cached_issuer); free(data->cached_fingerprint); free(data->cached_pem); + free(data->cached_pem_chain); free(data); } @@ -198,9 +205,16 @@ UINT16 freerdp_certificate_data_get_port(const rdpCertificateData* cert) } const char* freerdp_certificate_data_get_pem(const rdpCertificateData* cert) +{ + return freerdp_certificate_data_get_pem_ex(cert, TRUE); +} + +const char* freerdp_certificate_data_get_pem_ex(const rdpCertificateData* cert, BOOL withFullChain) { if (!cert) return NULL; + if (withFullChain) + return cert->cached_pem_chain; return cert->cached_pem; } diff --git a/libfreerdp/crypto/certificate_store.c b/libfreerdp/crypto/certificate_store.c index bd182b43c..d66e15b02 100644 --- a/libfreerdp/crypto/certificate_store.c +++ b/libfreerdp/crypto/certificate_store.c @@ -116,7 +116,7 @@ BOOL freerdp_certificate_store_save_data(rdpCertificateStore* store, const rdpCe if (!fp) goto fail; - fprintf(fp, "%s", freerdp_certificate_data_get_pem(data)); + fprintf(fp, "%s", freerdp_certificate_data_get_pem_ex(data, FALSE)); rc = TRUE; fail: diff --git a/libfreerdp/crypto/tls.c b/libfreerdp/crypto/tls.c index aae798eed..29e2f98ed 100644 --- a/libfreerdp/crypto/tls.c +++ b/libfreerdp/crypto/tls.c @@ -1303,55 +1303,53 @@ static BOOL is_redirected(rdpTls* tls) return settings->RedirectionFlags != 0; } -static BOOL is_accepted(rdpTls* tls, const BYTE* pem, size_t length) +static BOOL is_accepted(rdpTls* tls, const rdpCertificate* cert) { + WINPR_ASSERT(tls); + WINPR_ASSERT(tls->context); + WINPR_ASSERT(cert); rdpSettings* settings = tls->context->settings; - char* AccpetedKey = NULL; - UINT32 AcceptedKeyLength = 0; + WINPR_ASSERT(settings); + + FreeRDP_Settings_Keys_String keyAccepted; + FreeRDP_Settings_Keys_UInt32 keyLength; if (tls->isGatewayTransport) { - AccpetedKey = settings->GatewayAcceptedCert; - AcceptedKeyLength = settings->GatewayAcceptedCertLength; + keyAccepted = FreeRDP_GatewayAcceptedCert; + keyLength = FreeRDP_GatewayAcceptedCertLength; } else if (is_redirected(tls)) { - AccpetedKey = settings->RedirectionAcceptedCert; - AcceptedKeyLength = settings->RedirectionAcceptedCertLength; + keyAccepted = FreeRDP_RedirectionAcceptedCert; + keyLength = FreeRDP_RedirectionAcceptedCertLength; } else { - AccpetedKey = settings->AcceptedCert; - AcceptedKeyLength = settings->AcceptedCertLength; + keyAccepted = FreeRDP_AcceptedCert; + keyLength = FreeRDP_AcceptedCertLength; } - if (AcceptedKeyLength > 0) + const char* AcceptedKey = freerdp_settings_get_string(settings, keyAccepted); + const UINT32 AcceptedKeyLength = freerdp_settings_get_uint32(settings, keyLength); + + if ((AcceptedKeyLength > 0) && AcceptedKey) { - if (AcceptedKeyLength == length) + BOOL accepted = FALSE; + size_t pemLength = 0; + char* pem = freerdp_certificate_get_pem_ex(cert, &pemLength, FALSE); + if (pem && (AcceptedKeyLength == pemLength)) { - if (memcmp(AccpetedKey, pem, AcceptedKeyLength) == 0) - return TRUE; + if (memcmp(AcceptedKey, pem, AcceptedKeyLength) == 0) + accepted = TRUE; } + free(pem); + if (accepted) + return TRUE; } - if (tls->isGatewayTransport) - { - free(settings->GatewayAcceptedCert); - settings->GatewayAcceptedCert = NULL; - settings->GatewayAcceptedCertLength = 0; - } - else if (is_redirected(tls)) - { - free(settings->RedirectionAcceptedCert); - settings->RedirectionAcceptedCert = NULL; - settings->RedirectionAcceptedCertLength = 0; - } - else - { - free(settings->AcceptedCert); - settings->AcceptedCert = NULL; - settings->AcceptedCertLength = 0; - } + freerdp_settings_set_string(settings, keyAccepted, NULL); + freerdp_settings_set_uint32(settings, keyLength, 0); return FALSE; } @@ -1425,13 +1423,17 @@ static BOOL is_accepted_fingerprint(const rdpCertificate* cert, return rc; } -static BOOL accept_cert(rdpTls* tls, const BYTE* pem, UINT32 length) +static BOOL accept_cert(rdpTls* tls, const rdpCertificate* cert) { WINPR_ASSERT(tls); + WINPR_ASSERT(tls->context); + WINPR_ASSERT(cert); + FreeRDP_Settings_Keys_String id = FreeRDP_AcceptedCert; FreeRDP_Settings_Keys_UInt32 lid = FreeRDP_AcceptedCertLength; rdpSettings* settings = tls->context->settings; + WINPR_ASSERT(settings); if (tls->isGatewayTransport) { @@ -1444,13 +1446,18 @@ static BOOL accept_cert(rdpTls* tls, const BYTE* pem, UINT32 length) lid = FreeRDP_RedirectionAcceptedCertLength; } - if (!freerdp_settings_set_string_len(settings, id, (const char*)pem, length)) - return FALSE; + size_t pemLength = 0; + char* pem = freerdp_certificate_get_pem_ex(cert, &pemLength, FALSE); - return freerdp_settings_set_uint32(settings, lid, length); + BOOL rc = FALSE; + if (freerdp_settings_set_string_len(settings, id, pem, pemLength)) + rc = freerdp_settings_set_uint32(settings, lid, pemLength); + free(pem); + return rc; } -static BOOL tls_extract_pem(const rdpCertificate* cert, BYTE** PublicKey, size_t* PublicKeyLength) +static BOOL tls_extract_full_pem(const rdpCertificate* cert, BYTE** PublicKey, + size_t* PublicKeyLength) { if (!cert || !PublicKey) return FALSE; @@ -1485,11 +1492,11 @@ int tls_verify_certificate(rdpTls* tls, const rdpCertificate* cert, const char* if (freerdp_shall_disconnect_context(instance->context)) return -1; - if (!tls_extract_pem(cert, &pemCert, &length)) + if (!tls_extract_full_pem(cert, &pemCert, &length)) goto end; /* Check, if we already accepted this key. */ - if (is_accepted(tls, pemCert, length)) + if (is_accepted(tls, cert)) { verification_status = 1; goto end; @@ -1520,7 +1527,7 @@ int tls_verify_certificate(rdpTls* tls, const rdpCertificate* cert, const char* WLog_ERR(TAG, "No VerifyX509Certificate callback registered!"); if (verification_status > 0) - accept_cert(tls, pemCert, length); + accept_cert(tls, cert); else if (verification_status < 0) { WLog_ERR(TAG, "VerifyX509Certificate failed: (length = %" PRIuz ") status: [%d] %s", @@ -1780,7 +1787,7 @@ int tls_verify_certificate(rdpTls* tls, const rdpCertificate* cert, const char* } if (verification_status > 0) - accept_cert(tls, pemCert, length); + accept_cert(tls, cert); } end: