示例#1
0
文件: tls.c 项目: dcatonR1/FreeRDP
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;
}
示例#2
0
文件: tls.c 项目: Devolutions/FreeRDP
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;
}