int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port) { int match; int index; char* common_name = NULL; int common_name_length = 0; char** alt_names = NULL; int alt_names_count = 0; int* alt_names_lengths = NULL; BOOL certificate_status; BOOL hostname_match = FALSE; BOOL verification_status = FALSE; rdpCertificateData* certificate_data; if (tls->settings->ExternalCertificateManagement) { BIO* bio; int status; int length; int offset; BYTE* pemCert; freerdp* instance = (freerdp*) tls->settings->instance; /** * Don't manage certificates internally, leave it up entirely to the external client implementation */ bio = BIO_new(BIO_s_mem()); if (!bio) { WLog_ERR(TAG, "BIO_new() failure"); return -1; } status = PEM_write_bio_X509(bio, cert->px509); if (status < 0) { WLog_ERR(TAG, "PEM_write_bio_X509 failure: %d", status); return -1; } offset = 0; length = 2048; pemCert = (BYTE*) malloc(length + 1); if (!pemCert) { WLog_ERR(TAG, "error allocating pemCert"); return -1; } status = BIO_read(bio, pemCert, length); if (status < 0) { WLog_ERR(TAG, "failed to read certificate"); return -1; } offset += status; while (offset >= length) { int new_len; BYTE* new_cert; new_len = length * 2; new_cert = (BYTE*) realloc(pemCert, new_len + 1); if (!new_cert) return -1; length = new_len; pemCert = new_cert; status = BIO_read(bio, &pemCert[offset], length); if (status < 0) break; offset += status; } if (status < 0) { WLog_ERR(TAG, "failed to read certificate"); return -1; } length = offset; pemCert[length] = '\0'; status = -1; if (instance->VerifyX509Certificate) status = instance->VerifyX509Certificate(instance, pemCert, length, hostname, port, tls->isGatewayTransport); else WLog_ERR(TAG, "No VerifyX509Certificate callback registered!"); free(pemCert); BIO_free(bio); if (status < 0) { WLog_ERR(TAG, "VerifyX509Certificate failed: (length = %d) status: [%d] %s", length, status, pemCert); return -1; } return (status == 0) ? 0 : 1; } /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->IgnoreCertificate) return 1; /* success! */ /* if user explicitly specified a certificate name, use it instead of the hostname */ if (tls->settings->CertificateName) hostname = tls->settings->CertificateName; /* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */ certificate_status = x509_verify_certificate(cert, tls->certificate_store->path); /* verify certificate name match */ certificate_data = crypto_get_certificate_data(cert->px509, hostname, port); /* extra common name and alternative names */ common_name = crypto_cert_subject_common_name(cert->px509, &common_name_length); alt_names = crypto_cert_subject_alt_name(cert->px509, &alt_names_count, &alt_names_lengths); /* compare against common name */ if (common_name) { if (tls_match_hostname(common_name, common_name_length, hostname)) hostname_match = TRUE; } /* compare against alternative names */ if (alt_names) { for (index = 0; index < alt_names_count; index++) { if (tls_match_hostname(alt_names[index], alt_names_lengths[index], hostname)) { hostname_match = TRUE; break; } } } /* if the certificate is valid and the certificate name matches, verification succeeds */ if (certificate_status && hostname_match) verification_status = TRUE; /* success! */ /* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */ if (!certificate_status || !hostname_match) { char* issuer; char* subject; char* fingerprint; freerdp* instance = (freerdp*) tls->settings->instance; DWORD accept_certificate = 0; issuer = crypto_cert_issuer(cert->px509); subject = crypto_cert_subject(cert->px509); fingerprint = crypto_cert_fingerprint(cert->px509); /* search for matching entry in known_hosts file */ match = certificate_data_match(tls->certificate_store, certificate_data); if (match == 1) { /* no entry was found in known_hosts file, prompt user for manual verification */ if (!hostname_match) tls_print_certificate_name_mismatch_error( hostname, port, common_name, alt_names, alt_names_count); /* Automatically accept certificate on first use */ if (tls->settings->AutoAcceptCertificate) { WLog_INFO(TAG, "No certificate stored, automatically accepting."); accept_certificate = 1; } else if (instance->VerifyCertificate) { accept_certificate = instance->VerifyCertificate( instance, common_name, subject, issuer, fingerprint, !hostname_match); } switch (accept_certificate) { case 1: /* user accepted certificate, add entry in known_hosts file */ verification_status = certificate_data_print(tls->certificate_store, certificate_data); break; case 2: /* user did accept temporaty, do not add to known hosts file */ verification_status = TRUE; break; default: /* user did not accept, abort and do not add entry in known_hosts file */ verification_status = FALSE; /* failure! */ break; } } else if (match == -1) { char* old_subject = NULL; char* old_issuer = NULL; char* old_fingerprint = NULL; /* entry was found in known_hosts file, but fingerprint does not match. ask user to use it */ tls_print_certificate_error(hostname, port, fingerprint, tls->certificate_store->file); if (!certificate_get_stored_data(tls->certificate_store, certificate_data, &old_subject, &old_issuer, &old_fingerprint)) WLog_WARN(TAG, "Failed to get certificate entry for %s:%d", hostname, port); if (instance->VerifyChangedCertificate) { accept_certificate = instance->VerifyChangedCertificate( instance, common_name, subject, issuer, fingerprint, old_subject, old_issuer, old_fingerprint); } free(old_subject); free(old_issuer); free(old_fingerprint); switch (accept_certificate) { case 1: /* user accepted certificate, add entry in known_hosts file */ verification_status = certificate_data_replace(tls->certificate_store, certificate_data); break; case 2: /* user did accept temporaty, do not add to known hosts file */ verification_status = TRUE; break; default: /* user did not accept, abort and do not add entry in known_hosts file */ verification_status = FALSE; /* failure! */ break; } } else if (match == 0) verification_status = TRUE; /* success! */ free(issuer); free(subject); free(fingerprint); } certificate_data_free(certificate_data); free(common_name); if (alt_names) crypto_cert_subject_alt_name_free(alt_names_count, alt_names_lengths, alt_names); return (verification_status == 0) ? 0 : 1; }
int tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port) { int match; int index; char* common_name = NULL; int common_name_length = 0; char** dns_names = 0; int dns_names_count = 0; int* dns_names_lengths = NULL; BOOL certificate_status; BOOL hostname_match = FALSE; BOOL verification_status = FALSE; rdpCertificateData* certificate_data; freerdp* instance = (freerdp*) tls->settings->instance; DWORD length; BYTE* pemCert; if (!tls_extract_pem(cert, &pemCert, &length)) return -1; /* Check, if we already accepted this key. */ if (is_accepted(tls, pemCert, length)) { free(pemCert); return 1; } if (tls->settings->ExternalCertificateManagement) { int status = -1; if (instance->VerifyX509Certificate) status = instance->VerifyX509Certificate(instance, pemCert, length, hostname, port, tls->isGatewayTransport | is_redirected(tls) ? 2 : 0); else WLog_ERR(TAG, "No VerifyX509Certificate callback registered!"); if (status > 0) { accept_cert(tls, pemCert, length); } else if (status < 0) { WLog_ERR(TAG, "VerifyX509Certificate failed: (length = %d) status: [%d] %s", length, status, pemCert); free(pemCert); return -1; } else free(pemCert); return (status == 0) ? 0 : 1; } /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->IgnoreCertificate) { free(pemCert); return 1; /* success! */ } if (!tls->isGatewayTransport && tls->settings->AuthenticationLevel == 0) { free(pemCert); return 1; /* success! */ } /* if user explicitly specified a certificate name, use it instead of the hostname */ if (!tls->isGatewayTransport && tls->settings->CertificateName) hostname = tls->settings->CertificateName; /* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */ certificate_status = x509_verify_certificate(cert, tls->certificate_store->path); /* verify certificate name match */ certificate_data = crypto_get_certificate_data(cert->px509, hostname, port); /* extra common name and alternative names */ common_name = crypto_cert_subject_common_name(cert->px509, &common_name_length); dns_names = crypto_cert_get_dns_names(cert->px509, &dns_names_count, &dns_names_lengths); /* compare against common name */ if (common_name) { if (tls_match_hostname(common_name, common_name_length, hostname)) hostname_match = TRUE; } /* compare against alternative names */ if (dns_names) { for (index = 0; index < dns_names_count; index++) { if (tls_match_hostname(dns_names[index], dns_names_lengths[index], hostname)) { hostname_match = TRUE; break; } } } /* if the certificate is valid and the certificate name matches, verification succeeds */ if (certificate_status && hostname_match) verification_status = TRUE; /* success! */ /* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */ if (!certificate_status || !hostname_match) { char* issuer; char* subject; char* fingerprint; DWORD accept_certificate = 0; issuer = crypto_cert_issuer(cert->px509); subject = crypto_cert_subject(cert->px509); fingerprint = crypto_cert_fingerprint(cert->px509); /* search for matching entry in known_hosts file */ match = certificate_data_match(tls->certificate_store, certificate_data); if (match == 1) { /* no entry was found in known_hosts file, prompt user for manual verification */ if (!hostname_match) tls_print_certificate_name_mismatch_error( hostname, port, common_name, dns_names, dns_names_count); /* Automatically accept certificate on first use */ if (tls->settings->AutoAcceptCertificate) { WLog_INFO(TAG, "No certificate stored, automatically accepting."); accept_certificate = 1; } else if (instance->VerifyCertificate) { accept_certificate = instance->VerifyCertificate( instance, common_name, subject, issuer, fingerprint, !hostname_match); } switch (accept_certificate) { case 1: /* user accepted certificate, add entry in known_hosts file */ verification_status = certificate_data_print(tls->certificate_store, certificate_data); break; case 2: /* user did accept temporaty, do not add to known hosts file */ verification_status = TRUE; break; default: /* user did not accept, abort and do not add entry in known_hosts file */ verification_status = FALSE; /* failure! */ break; } } else if (match == -1) { char* old_subject = NULL; char* old_issuer = NULL; char* old_fingerprint = NULL; /* entry was found in known_hosts file, but fingerprint does not match. ask user to use it */ tls_print_certificate_error(hostname, port, fingerprint, tls->certificate_store->file); if (!certificate_get_stored_data(tls->certificate_store, certificate_data, &old_subject, &old_issuer, &old_fingerprint)) WLog_WARN(TAG, "Failed to get certificate entry for %s:%d", hostname, port); if (instance->VerifyChangedCertificate) { accept_certificate = instance->VerifyChangedCertificate( instance, common_name, subject, issuer, fingerprint, old_subject, old_issuer, old_fingerprint); } free(old_subject); free(old_issuer); free(old_fingerprint); switch (accept_certificate) { case 1: /* user accepted certificate, add entry in known_hosts file */ verification_status = certificate_data_replace(tls->certificate_store, certificate_data); break; case 2: /* user did accept temporaty, do not add to known hosts file */ verification_status = TRUE; break; default: /* user did not accept, abort and do not add entry in known_hosts file */ verification_status = FALSE; /* failure! */ break; } } else if (match == 0) verification_status = TRUE; /* success! */ free(issuer); free(subject); free(fingerprint); } certificate_data_free(certificate_data); free(common_name); if (dns_names) crypto_cert_dns_names_free(dns_names_count, dns_names_lengths, dns_names); if (verification_status > 0) { accept_cert(tls, pemCert, length); } else { free(pemCert); } return (verification_status == 0) ? 0 : 1; }
boolean tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname) { int match; int index; char* common_name = NULL; int common_name_length = 0; char** alt_names = NULL; int alt_names_count = 0; int* alt_names_lengths = NULL; boolean certificate_status; boolean hostname_match = false; boolean verification_status = false; rdpCertificateData* certificate_data; /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->ignore_certificate) return true; /* success! */ /* if user explicitly specified a certificate name, use it instead of the hostname */ if (tls->settings->certificate_name) hostname = tls->settings->certificate_name; /* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */ certificate_status = x509_verify_certificate(cert, tls->certificate_store->path); /* verify certificate name match */ certificate_data = crypto_get_certificate_data(cert->px509, hostname); /* extra common name and alternative names */ common_name = crypto_cert_subject_common_name(cert->px509, &common_name_length); alt_names = crypto_cert_subject_alt_name(cert->px509, &alt_names_count, &alt_names_lengths); /* compare against common name */ if (common_name != NULL) { if (strlen(hostname) == common_name_length) { if (memcmp((void*) hostname, (void*) common_name, common_name_length) == 0) hostname_match = true; } } /* compare against alternative names */ if (alt_names != NULL) { for (index = 0; index < alt_names_count; index++) { if (strlen(hostname) == alt_names_lengths[index]) { if (memcmp((void*) hostname, (void*) alt_names[index], alt_names_lengths[index]) == 0) hostname_match = true; } } } /* if the certificate is valid and the certificate name matches, verification succeeds */ if (certificate_status && hostname_match) { if (common_name) { xfree(common_name); common_name = NULL; } verification_status = true; /* success! */ } /* if the certificate is valid but the certificate name does not match, warn user, do not accept */ if (certificate_status && !hostname_match) tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count); /* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */ if (!certificate_status) { char* issuer; char* subject; char* fingerprint; freerdp* instance = (freerdp*) tls->settings->instance; boolean accept_certificate = false; issuer = crypto_cert_issuer(cert->px509); subject = crypto_cert_subject(cert->px509); fingerprint = crypto_cert_fingerprint(cert->px509); /* search for matching entry in known_hosts file */ match = certificate_data_match(tls->certificate_store, certificate_data); if (match == 1) { /* no entry was found in known_hosts file, prompt user for manual verification */ if (!hostname_match) tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count); if (instance->VerifyCertificate) accept_certificate = instance->VerifyCertificate(instance, subject, issuer, fingerprint); if (!accept_certificate) { /* user did not accept, abort and do not add entry in known_hosts file */ verification_status = false; /* failure! */ } else { /* user accepted certificate, add entry in known_hosts file */ certificate_data_print(tls->certificate_store, certificate_data); verification_status = true; /* success! */ } } else if (match == -1) { /* entry was found in known_hosts file, but fingerprint does not match. ask user to use it */ tls_print_certificate_error(hostname, fingerprint); if (instance->VerifyChangedCertificate) accept_certificate = instance->VerifyChangedCertificate(instance, subject, issuer, fingerprint, ""); if (!accept_certificate) { /* user did not accept, abort and do not change known_hosts file */ verification_status = false; /* failure! */ } else { /* user accepted new certificate, add replace fingerprint for this host in known_hosts file */ certificate_data_replace(tls->certificate_store, certificate_data); verification_status = true; /* success! */ } } else if (match == 0) { verification_status = true; /* success! */ } xfree(issuer); xfree(subject); xfree(fingerprint); } if (certificate_data) { xfree(certificate_data->fingerprint); xfree(certificate_data->hostname); xfree(certificate_data); } return verification_status; }
BOOL tls_verify_certificate(rdpTls* tls, CryptoCert cert, char* hostname, int port) { int match; int index; char* common_name = NULL; int common_name_length = 0; char** alt_names = NULL; int alt_names_count = 0; int* alt_names_lengths = NULL; BOOL certificate_status; BOOL hostname_match = FALSE; BOOL verification_status = FALSE; rdpCertificateData* certificate_data; if (tls->settings->ExternalCertificateManagement) { BIO* bio; int status; int length; int offset; BYTE* pemCert; freerdp* instance = (freerdp*) tls->settings->instance; /** * Don't manage certificates internally, leave it up entirely to the external client implementation */ bio = BIO_new(BIO_s_mem()); if (!bio) { fprintf(stderr, "tls_verify_certificate: BIO_new() failure\n"); return FALSE; } status = PEM_write_bio_X509(bio, cert->px509); if (status < 0) { fprintf(stderr, "tls_verify_certificate: PEM_write_bio_X509 failure: %d\n", status); return FALSE; } offset = 0; length = 2048; pemCert = (BYTE*) malloc(length + 1); status = BIO_read(bio, pemCert, length); if (status < 0) { fprintf(stderr, "tls_verify_certificate: failed to read certificate\n"); return FALSE; } offset += status; while (offset >= length) { length *= 2; pemCert = (BYTE*) realloc(pemCert, length + 1); status = BIO_read(bio, &pemCert[offset], length); if (status < 0) break; offset += status; } if (status < 0) { fprintf(stderr, "tls_verify_certificate: failed to read certificate\n"); return FALSE; } length = offset; pemCert[length] = '\0'; status = -1; if (instance->VerifyX509Certificate) { status = instance->VerifyX509Certificate(instance, pemCert, length, hostname, port, 0); } fprintf(stderr, "VerifyX509Certificate: (length = %d) status: %d\n%s\n", length, status, pemCert); free(pemCert); BIO_free(bio); return (status < 0) ? FALSE : TRUE; } /* ignore certificate verification if user explicitly required it (discouraged) */ if (tls->settings->IgnoreCertificate) return TRUE; /* success! */ /* if user explicitly specified a certificate name, use it instead of the hostname */ if (tls->settings->CertificateName) hostname = tls->settings->CertificateName; /* attempt verification using OpenSSL and the ~/.freerdp/certs certificate store */ certificate_status = x509_verify_certificate(cert, tls->certificate_store->path); /* verify certificate name match */ certificate_data = crypto_get_certificate_data(cert->px509, hostname); /* extra common name and alternative names */ common_name = crypto_cert_subject_common_name(cert->px509, &common_name_length); alt_names = crypto_cert_subject_alt_name(cert->px509, &alt_names_count, &alt_names_lengths); /* compare against common name */ if (common_name != NULL) { if (tls_match_hostname(common_name, common_name_length, hostname)) hostname_match = TRUE; } /* compare against alternative names */ if (alt_names != NULL) { for (index = 0; index < alt_names_count; index++) { if (tls_match_hostname(alt_names[index], alt_names_lengths[index], hostname)) { hostname_match = TRUE; break; } } } /* if the certificate is valid and the certificate name matches, verification succeeds */ if (certificate_status && hostname_match) { if (common_name) { free(common_name); common_name = NULL; } verification_status = TRUE; /* success! */ } /* if the certificate is valid but the certificate name does not match, warn user, do not accept */ if (certificate_status && !hostname_match) tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count); /* verification could not succeed with OpenSSL, use known_hosts file and prompt user for manual verification */ if (!certificate_status) { char* issuer; char* subject; char* fingerprint; freerdp* instance = (freerdp*) tls->settings->instance; BOOL accept_certificate = FALSE; issuer = crypto_cert_issuer(cert->px509); subject = crypto_cert_subject(cert->px509); fingerprint = crypto_cert_fingerprint(cert->px509); /* search for matching entry in known_hosts file */ match = certificate_data_match(tls->certificate_store, certificate_data); if (match == 1) { /* no entry was found in known_hosts file, prompt user for manual verification */ if (!hostname_match) tls_print_certificate_name_mismatch_error(hostname, common_name, alt_names, alt_names_count); if (instance->VerifyCertificate) accept_certificate = instance->VerifyCertificate(instance, subject, issuer, fingerprint); if (!accept_certificate) { /* user did not accept, abort and do not add entry in known_hosts file */ verification_status = FALSE; /* failure! */ } else { /* user accepted certificate, add entry in known_hosts file */ certificate_data_print(tls->certificate_store, certificate_data); verification_status = TRUE; /* success! */ } } else if (match == -1) { /* entry was found in known_hosts file, but fingerprint does not match. ask user to use it */ tls_print_certificate_error(hostname, fingerprint, tls->certificate_store->file); if (instance->VerifyChangedCertificate) accept_certificate = instance->VerifyChangedCertificate(instance, subject, issuer, fingerprint, ""); if (!accept_certificate) { /* user did not accept, abort and do not change known_hosts file */ verification_status = FALSE; /* failure! */ } else { /* user accepted new certificate, add replace fingerprint for this host in known_hosts file */ certificate_data_replace(tls->certificate_store, certificate_data); verification_status = TRUE; /* success! */ } } else if (match == 0) { verification_status = TRUE; /* success! */ } free(issuer); free(subject); free(fingerprint); } if (certificate_data) { free(certificate_data->fingerprint); free(certificate_data->hostname); free(certificate_data); } #ifndef _WIN32 if (common_name) free(common_name); #endif if (alt_names) crypto_cert_subject_alt_name_free(alt_names_count, alt_names_lengths, alt_names); return verification_status; }