mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-03-16 02:30:19 +00:00
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 <openssl/ssl.h> /* SSL_() */
|
||||
#include <openssl/err.h> /* ERR_() */
|
||||
#include <openssl/x509v3.h>
|
||||
#ifdef WIN32
|
||||
#include <openssl/rand.h> /* RAND_seed() */
|
||||
#endif
|
||||
@@ -35,6 +36,7 @@
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
#include <gio/gio.h>
|
||||
#include "util.h"
|
||||
|
||||
/* If openssl was built without ec */
|
||||
@@ -339,3 +341,204 @@ _SSL_close (SSL * ssl)
|
||||
SSL_free (ssl);
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user