parent
ebaaf466bf
commit
c9b63f7f9b
|
@ -723,9 +723,22 @@ ssl_do_connect (server * serv)
|
||||||
switch (verify_error)
|
switch (verify_error)
|
||||||
{
|
{
|
||||||
case X509_V_OK:
|
case X509_V_OK:
|
||||||
|
{
|
||||||
|
X509 *cert = SSL_get_peer_certificate (serv->ssl);
|
||||||
|
int hostname_err;
|
||||||
|
if ((hostname_err = _SSL_check_hostname(cert, serv->hostname)) != 0)
|
||||||
|
{
|
||||||
|
snprintf (buf, sizeof (buf), "* Verify E: Failed to validate hostname? (%d)%s",
|
||||||
|
hostname_err, serv->accept_invalid_cert ? " -- Ignored" : "");
|
||||||
|
if (serv->accept_invalid_cert)
|
||||||
|
EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL, 0);
|
||||||
|
else
|
||||||
|
goto conn_fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
/* snprintf (buf, sizeof (buf), "* Verify OK (?)"); */
|
/* snprintf (buf, sizeof (buf), "* Verify OK (?)"); */
|
||||||
/* EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL, 0); */
|
/* EMIT_SIGNAL (XP_TE_SSLMESSAGE, serv->server_session, buf, NULL, NULL, NULL, 0); */
|
||||||
break;
|
|
||||||
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
|
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
|
||||||
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
|
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
|
||||||
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
|
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
|
||||||
|
@ -744,6 +757,7 @@ ssl_do_connect (server * serv)
|
||||||
snprintf (buf, sizeof (buf), "%s.? (%d)",
|
snprintf (buf, sizeof (buf), "%s.? (%d)",
|
||||||
X509_verify_cert_error_string (verify_error),
|
X509_verify_cert_error_string (verify_error),
|
||||||
verify_error);
|
verify_error);
|
||||||
|
conn_fail:
|
||||||
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL, NULL,
|
EMIT_SIGNAL (XP_TE_CONNFAIL, serv->server_session, buf, NULL, NULL,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
|
|
||||||
|
|
203
src/common/ssl.c
203
src/common/ssl.c
|
@ -25,6 +25,7 @@
|
||||||
#include "inet.h" /* make it first to avoid macro redefinitions */
|
#include "inet.h" /* make it first to avoid macro redefinitions */
|
||||||
#include <openssl/ssl.h> /* SSL_() */
|
#include <openssl/ssl.h> /* SSL_() */
|
||||||
#include <openssl/err.h> /* ERR_() */
|
#include <openssl/err.h> /* ERR_() */
|
||||||
|
#include <openssl/x509v3.h>
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#include <openssl/rand.h> /* RAND_seed() */
|
#include <openssl/rand.h> /* RAND_seed() */
|
||||||
#endif
|
#endif
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
/* If openssl was built without ec */
|
/* If openssl was built without ec */
|
||||||
|
@ -339,3 +341,204 @@ _SSL_close (SSL * ssl)
|
||||||
SSL_free (ssl);
|
SSL_free (ssl);
|
||||||
ERR_remove_state (0); /* free state buffer */
|
ERR_remove_state (0); /* free state buffer */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hostname validation code based on OpenBSD's libtls. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
_SSL_match_hostname (const char *cert_hostname, const char *hostname)
|
||||||
|
{
|
||||||
|
const char *cert_domain, *domain, *next_dot;
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp (cert_hostname, hostname) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Wildcard match? */
|
||||||
|
if (cert_hostname[0] == '*')
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Valid wildcards:
|
||||||
|
* - "*.domain.tld"
|
||||||
|
* - "*.sub.domain.tld"
|
||||||
|
* - etc.
|
||||||
|
* Reject "*.tld".
|
||||||
|
* No attempt to prevent the use of eg. "*.co.uk".
|
||||||
|
*/
|
||||||
|
cert_domain = &cert_hostname[1];
|
||||||
|
/* Disallow "*" */
|
||||||
|
if (cert_domain[0] == '\0')
|
||||||
|
return -1;
|
||||||
|
/* Disallow "*foo" */
|
||||||
|
if (cert_domain[0] != '.')
|
||||||
|
return -1;
|
||||||
|
/* Disallow "*.." */
|
||||||
|
if (cert_domain[1] == '.')
|
||||||
|
return -1;
|
||||||
|
next_dot = strchr (&cert_domain[1], '.');
|
||||||
|
/* Disallow "*.bar" */
|
||||||
|
if (next_dot == NULL)
|
||||||
|
return -1;
|
||||||
|
/* Disallow "*.bar.." */
|
||||||
|
if (next_dot[1] == '.')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
domain = strchr (hostname, '.');
|
||||||
|
|
||||||
|
/* No wildcard match against a hostname with no domain part. */
|
||||||
|
if (domain == NULL || strlen(domain) == 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp (cert_domain, domain) == 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_SSL_check_subject_altname (X509 *cert, const char *host)
|
||||||
|
{
|
||||||
|
STACK_OF(GENERAL_NAME) *altname_stack = NULL;
|
||||||
|
GInetAddress *addr;
|
||||||
|
GSocketFamily family;
|
||||||
|
int type = GEN_DNS;
|
||||||
|
int count, i;
|
||||||
|
int rv = -1;
|
||||||
|
|
||||||
|
altname_stack = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
|
||||||
|
if (altname_stack == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
addr = g_inet_address_new_from_string (host);
|
||||||
|
if (addr != NULL)
|
||||||
|
{
|
||||||
|
family = g_inet_address_get_family (addr);
|
||||||
|
if (family == G_SOCKET_FAMILY_IPV4 || family == G_SOCKET_FAMILY_IPV6)
|
||||||
|
type = GEN_IPADD;
|
||||||
|
}
|
||||||
|
|
||||||
|
count = sk_GENERAL_NAME_num(altname_stack);
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
GENERAL_NAME *altname;
|
||||||
|
|
||||||
|
altname = sk_GENERAL_NAME_value (altname_stack, i);
|
||||||
|
|
||||||
|
if (altname->type != type)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (type == GEN_DNS)
|
||||||
|
{
|
||||||
|
unsigned char *data;
|
||||||
|
int format;
|
||||||
|
|
||||||
|
format = ASN1_STRING_type (altname->d.dNSName);
|
||||||
|
if (format == V_ASN1_IA5STRING)
|
||||||
|
{
|
||||||
|
data = ASN1_STRING_data (altname->d.dNSName);
|
||||||
|
|
||||||
|
if (ASN1_STRING_length (altname->d.dNSName) != (int)strlen(data))
|
||||||
|
{
|
||||||
|
g_warning("NUL byte in subjectAltName, probably a malicious certificate.\n");
|
||||||
|
rv = -2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_SSL_match_hostname (data, host) == 0)
|
||||||
|
{
|
||||||
|
rv = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
g_warning ("unhandled subjectAltName dNSName encoding (%d)\n", format);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (type == GEN_IPADD)
|
||||||
|
{
|
||||||
|
unsigned char *data;
|
||||||
|
const guint8 *addr_bytes;
|
||||||
|
int datalen, addr_len;
|
||||||
|
|
||||||
|
datalen = ASN1_STRING_length (altname->d.iPAddress);
|
||||||
|
data = ASN1_STRING_data (altname->d.iPAddress);
|
||||||
|
|
||||||
|
addr_bytes = g_inet_address_to_bytes (addr);
|
||||||
|
addr_len = (int)g_inet_address_get_native_size (addr);
|
||||||
|
|
||||||
|
if (datalen == addr_len && memcmp (data, addr_bytes, addr_len) == 0)
|
||||||
|
{
|
||||||
|
rv = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr != NULL)
|
||||||
|
g_object_unref (addr);
|
||||||
|
sk_GENERAL_NAME_free (altname_stack);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_SSL_check_common_name (X509 *cert, const char *host)
|
||||||
|
{
|
||||||
|
X509_NAME *name;
|
||||||
|
char *common_name = NULL;
|
||||||
|
int common_name_len;
|
||||||
|
int rv = -1;
|
||||||
|
GInetAddress *addr;
|
||||||
|
|
||||||
|
name = X509_get_subject_name (cert);
|
||||||
|
if (name == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
common_name_len = X509_NAME_get_text_by_NID (name, NID_commonName, NULL, 0);
|
||||||
|
if (common_name_len < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
common_name = calloc (common_name_len + 1, 1);
|
||||||
|
if (common_name == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
X509_NAME_get_text_by_NID (name, NID_commonName, common_name, common_name_len + 1);
|
||||||
|
|
||||||
|
/* NUL bytes in CN? */
|
||||||
|
if (common_name_len != (int)strlen(common_name))
|
||||||
|
{
|
||||||
|
g_warning ("NUL byte in Common Name field, probably a malicious certificate.\n");
|
||||||
|
rv = -2;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((addr = g_inet_address_new_from_string (host)) != NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We don't want to attempt wildcard matching against IP
|
||||||
|
* addresses, so perform a simple comparison here.
|
||||||
|
*/
|
||||||
|
if (g_strcmp0 (common_name, host) == 0)
|
||||||
|
rv = 0;
|
||||||
|
else
|
||||||
|
rv = -1;
|
||||||
|
|
||||||
|
g_object_unref (addr);
|
||||||
|
}
|
||||||
|
else if (_SSL_match_hostname (common_name, host) == 0)
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(common_name);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_SSL_check_hostname (X509 *cert, const char *host)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = _SSL_check_subject_altname (cert, host);
|
||||||
|
if (rv == 0 || rv == -2)
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
return _SSL_check_common_name (cert, host);
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ char *_SSL_set_verify (SSL_CTX *ctx, void *(verify_callback), char *cacert);
|
||||||
int SSL_get_fd(SSL *);
|
int SSL_get_fd(SSL *);
|
||||||
*/
|
*/
|
||||||
void _SSL_close (SSL * ssl);
|
void _SSL_close (SSL * ssl);
|
||||||
|
int _SSL_check_hostname(X509 *cert, const char *host);
|
||||||
int _SSL_get_cert_info (struct cert_info *cert_info, SSL * ssl);
|
int _SSL_get_cert_info (struct cert_info *cert_info, SSL * ssl);
|
||||||
struct chiper_info *_SSL_get_cipher_info (SSL * ssl);
|
struct chiper_info *_SSL_get_cipher_info (SSL * ssl);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue