aboutsummaryrefslogtreecommitdiffstats
path: root/main/axel/CVE-2020-13614.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/axel/CVE-2020-13614.patch')
-rw-r--r--main/axel/CVE-2020-13614.patch223
1 files changed, 223 insertions, 0 deletions
diff --git a/main/axel/CVE-2020-13614.patch b/main/axel/CVE-2020-13614.patch
new file mode 100644
index 0000000000..8030fb70ad
--- /dev/null
+++ b/main/axel/CVE-2020-13614.patch
@@ -0,0 +1,223 @@
+diff --git a/src/Makefile.am b/src/Makefile.am
+index 728b4f8..4f92b03 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -14,6 +14,7 @@ axel_SOURCES = \
+ search.c \
+ search.h \
+ ssl.c \
++ ssl_verify.c \
+ ssl.h \
+ tcp.c \
+ tcp.h \
+diff --git a/src/ssl.c b/src/ssl.c
+index 6c7ae75..fc1ef05 100644
+--- a/src/ssl.c
++++ b/src/ssl.c
+@@ -71,7 +71,7 @@ ssl_startup(void)
+ SSL *
+ ssl_connect(int fd, char *hostname, char *message)
+ {
+-
++ X509 *server_cert;
+ SSL_CTX *ssl_ctx;
+ SSL *ssl;
+
+@@ -92,9 +92,33 @@ ssl_connect(int fd, char *hostname, char *message)
+ if (err <= 0) {
+ sprintf(message, _("SSL error: %s\n"),
+ ERR_reason_error_string(ERR_get_error()));
++ SSL_CTX_free(ssl_ctx);
++ return NULL;
++ }
++
++ err = SSL_get_verify_result(ssl);
++ if (err != X509_V_OK) {
++ fprintf(stderr, _("SSL error: Certificate error"));
++ SSL_CTX_free(ssl_ctx);
+ return NULL;
+ }
+
++ server_cert = SSL_get_peer_certificate(ssl);
++ if (server_cert == NULL) {
++ fprintf(stderr, _("SSL error: Certificate not found"));
++ SSL_CTX_free(ssl_ctx);
++ return NULL;
++ }
++
++ if (!ssl_validate_hostname(hostname, server_cert)) {
++ fprintf(stderr, _("SSL error: Hostname verification failed"));
++ X509_free(server_cert);
++ SSL_CTX_free(ssl_ctx);
++ return NULL;
++ }
++
++ X509_free(server_cert);
++
+ return ssl;
+ }
+
+diff --git a/src/ssl.h b/src/ssl.h
+index cc00eaf..64fb933 100644
+--- a/src/ssl.h
++++ b/src/ssl.h
+@@ -44,5 +44,6 @@
+ void ssl_init(conf_t *conf);
+ SSL *ssl_connect(int fd, char *hostname, char *message);
+ void ssl_disconnect(SSL *ssl);
++bool ssl_validate_hostname(const char *hostname, const X509 *server_cert);
+
+ #endif /* AXEL_SSL_H */
+diff --git a/src/ssl_verify.c b/src/ssl_verify.c
+new file mode 100644
+index 0000000..8a67a3c
+--- /dev/null
++++ b/src/ssl_verify.c
+@@ -0,0 +1,147 @@
++/*
++ Helper functions to perform basic hostname validation using OpenSSL.
++
++ Author: Alban Diquet
++ Copyright (C) 2012, iSEC Partners.
++
++ Permission is hereby granted, free of charge, to any person obtaining a copy of
++ this software and associated documentation files (the "Software"), to deal in
++ the Software without restriction, including without limitation the rights to
++ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
++ of the Software, and to permit persons to whom the Software is furnished to do
++ so, subject to the following conditions:
++
++ The above copyright notice and this permission notice shall be included in all
++ copies or substantial portions of the Software.
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ SOFTWARE.
++ */
++
++#include "axel.h"
++
++#ifdef HAVE_SSL
++
++#include <openssl/ssl.h>
++#include <openssl/x509v3.h>
++
++#if OPENSSL_VERSION_NUMBER < 0x10101000L
++#define ASN1_STRING_data_compat ASN1_STRING_data
++#else
++#define ASN1_STRING_data_compat ASN1_STRING_get0_data
++#endif
++
++typedef enum {
++ MatchFound,
++ MatchNotFound,
++ NoSANPresent,
++ MalformedCertificate,
++ Error
++} validate_result;
++
++static validate_result
++ssl_matches_common_name(const char *hostname, const X509 *server_cert)
++{
++ int common_name_loc = -1;
++ X509_NAME_ENTRY *common_name_entry = NULL;
++ ASN1_STRING *common_name_asn1 = NULL;
++ char *common_name_str = NULL;
++
++ // Find the position of the CN field in the Subject field of the certificate
++ common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *) server_cert), NID_commonName, -1);
++ if (common_name_loc < 0) {
++ return Error;
++ }
++
++ // Extract the CN field
++ common_name_entry = X509_NAME_get_entry(X509_get_subject_name((X509 *) server_cert), common_name_loc);
++ if (common_name_entry == NULL) {
++ return Error;
++ }
++
++ // Convert the CN field to a C string
++ common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
++ if (common_name_asn1 == NULL) {
++ return Error;
++ }
++ common_name_str = (char *) ASN1_STRING_data_compat(common_name_asn1);
++
++ // Make sure there isn't an embedded NUL character in the CN
++ if ((size_t) ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) {
++ return MalformedCertificate;
++ }
++
++ // Compare expected hostname with the CN
++ if (strcasecmp(hostname, common_name_str) == 0) {
++ return MatchFound;
++ } else {
++ return MatchNotFound;
++ }
++}
++
++static validate_result
++ssl_matches_subject_alternative_name(const char *hostname, const X509 *server_cert)
++{
++ validate_result result = MatchNotFound;
++ int i;
++ int san_names_nb = -1;
++ STACK_OF(GENERAL_NAME) *san_names = NULL;
++
++ // Try to extract the names within the SAN extension from the certificate
++ san_names = X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL);
++ if (san_names == NULL) {
++ return NoSANPresent;
++ }
++ san_names_nb = sk_GENERAL_NAME_num(san_names);
++
++ // Check each name within the extension
++ for (i = 0; i < san_names_nb; i++) {
++ const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i);
++
++ if (current_name->type == GEN_DNS) {
++ // Current name is a DNS name, let's check it
++ char *dns_name = (char *) ASN1_STRING_data_compat(current_name->d.dNSName);
++
++ // Make sure there isn't an embedded NUL character in the DNS name
++ if ((size_t) ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) {
++ result = MalformedCertificate;
++ break;
++ } else {
++ // Compare expected hostname with the DNS name
++ if (strcasecmp(hostname, dns_name) == 0) {
++ result = MatchFound;
++ break;
++ }
++ }
++ }
++ }
++ sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free);
++
++ return result;
++}
++
++bool
++ssl_validate_hostname(const char *hostname, const X509 *server_cert)
++{
++ validate_result result;
++
++ if ((hostname == NULL) || (server_cert == NULL)) {
++ return false;
++ }
++
++ // First try the Subject Alternative Names extension
++ result = ssl_matches_subject_alternative_name(hostname, server_cert);
++ if (result == NoSANPresent) {
++ // Extension was not found: try the Common Name
++ result = ssl_matches_common_name(hostname, server_cert);
++ }
++
++ return result == MatchFound;
++}
++
++#endif /* HAVE_SSL */