2525#include "inet.h" /* make it first to avoid macro redefinitions */
2626#include <openssl/ssl.h> /* SSL_() */
2727#include <openssl/err.h> /* ERR_() */
28+ #include <openssl/x509v3.h>
2829#ifdef WIN32
2930#include <openssl/rand.h> /* RAND_seed() */
3031#endif
3536
3637#include <glib.h>
3738#include <glib/gprintf.h>
39+ #include <gio/gio.h>
3840#include "util.h"
3941
4042/* If openssl was built without ec */
@@ -339,3 +341,204 @@ _SSL_close (SSL * ssl)
339341 SSL_free (ssl );
340342 ERR_remove_state (0 ); /* free state buffer */
341343}
344+
345+ /* Hostname validation code based on OpenBSD's libtls. */
346+
347+ static int
348+ _SSL_match_hostname (const char * cert_hostname , const char * hostname )
349+ {
350+ const char * cert_domain , * domain , * next_dot ;
351+
352+ if (g_ascii_strcasecmp (cert_hostname , hostname ) == 0 )
353+ return 0 ;
354+
355+ /* Wildcard match? */
356+ if (cert_hostname [0 ] == '*' )
357+ {
358+ /*
359+ * Valid wildcards:
360+ * - "*.domain.tld"
361+ * - "*.sub.domain.tld"
362+ * - etc.
363+ * Reject "*.tld".
364+ * No attempt to prevent the use of eg. "*.co.uk".
365+ */
366+ cert_domain = & cert_hostname [1 ];
367+ /* Disallow "*" */
368+ if (cert_domain [0 ] == '\0' )
369+ return -1 ;
370+ /* Disallow "*foo" */
371+ if (cert_domain [0 ] != '.' )
372+ return -1 ;
373+ /* Disallow "*.." */
374+ if (cert_domain [1 ] == '.' )
375+ return -1 ;
376+ next_dot = strchr (& cert_domain [1 ], '.' );
377+ /* Disallow "*.bar" */
378+ if (next_dot == NULL )
379+ return -1 ;
380+ /* Disallow "*.bar.." */
381+ if (next_dot [1 ] == '.' )
382+ return -1 ;
383+
384+ domain = strchr (hostname , '.' );
385+
386+ /* No wildcard match against a hostname with no domain part. */
387+ if (domain == NULL || strlen (domain ) == 1 )
388+ return -1 ;
389+
390+ if (g_ascii_strcasecmp (cert_domain , domain ) == 0 )
391+ return 0 ;
392+ }
393+
394+ return -1 ;
395+ }
396+
397+ static int
398+ _SSL_check_subject_altname (X509 * cert , const char * host )
399+ {
400+ STACK_OF (GENERAL_NAME ) * altname_stack = NULL ;
401+ GInetAddress * addr ;
402+ GSocketFamily family ;
403+ int type = GEN_DNS ;
404+ int count , i ;
405+ int rv = -1 ;
406+
407+ altname_stack = X509_get_ext_d2i (cert , NID_subject_alt_name , NULL , NULL );
408+ if (altname_stack == NULL )
409+ return -1 ;
410+
411+ addr = g_inet_address_new_from_string (host );
412+ if (addr != NULL )
413+ {
414+ family = g_inet_address_get_family (addr );
415+ if (family == G_SOCKET_FAMILY_IPV4 || family == G_SOCKET_FAMILY_IPV6 )
416+ type = GEN_IPADD ;
417+ }
418+
419+ count = sk_GENERAL_NAME_num (altname_stack );
420+ for (i = 0 ; i < count ; i ++ )
421+ {
422+ GENERAL_NAME * altname ;
423+
424+ altname = sk_GENERAL_NAME_value (altname_stack , i );
425+
426+ if (altname -> type != type )
427+ continue ;
428+
429+ if (type == GEN_DNS )
430+ {
431+ unsigned char * data ;
432+ int format ;
433+
434+ format = ASN1_STRING_type (altname -> d .dNSName );
435+ if (format == V_ASN1_IA5STRING )
436+ {
437+ data = ASN1_STRING_data (altname -> d .dNSName );
438+
439+ if (ASN1_STRING_length (altname -> d .dNSName ) != (int )strlen (data ))
440+ {
441+ g_warning ("NUL byte in subjectAltName, probably a malicious certificate.\n" );
442+ rv = -2 ;
443+ break ;
444+ }
445+
446+ if (_SSL_match_hostname (data , host ) == 0 )
447+ {
448+ rv = 0 ;
449+ break ;
450+ }
451+ }
452+ else
453+ g_warning ("unhandled subjectAltName dNSName encoding (%d)\n" , format );
454+
455+ }
456+ else if (type == GEN_IPADD )
457+ {
458+ unsigned char * data ;
459+ const guint8 * addr_bytes ;
460+ int datalen , addr_len ;
461+
462+ datalen = ASN1_STRING_length (altname -> d .iPAddress );
463+ data = ASN1_STRING_data (altname -> d .iPAddress );
464+
465+ addr_bytes = g_inet_address_to_bytes (addr );
466+ addr_len = (int )g_inet_address_get_native_size (addr );
467+
468+ if (datalen == addr_len && memcmp (data , addr_bytes , addr_len ) == 0 )
469+ {
470+ rv = 0 ;
471+ break ;
472+ }
473+ }
474+ }
475+
476+ if (addr != NULL )
477+ g_object_unref (addr );
478+ sk_GENERAL_NAME_free (altname_stack );
479+ return rv ;
480+ }
481+
482+ static int
483+ _SSL_check_common_name (X509 * cert , const char * host )
484+ {
485+ X509_NAME * name ;
486+ char * common_name = NULL ;
487+ int common_name_len ;
488+ int rv = -1 ;
489+ GInetAddress * addr ;
490+
491+ name = X509_get_subject_name (cert );
492+ if (name == NULL )
493+ return -1 ;
494+
495+ common_name_len = X509_NAME_get_text_by_NID (name , NID_commonName , NULL , 0 );
496+ if (common_name_len < 0 )
497+ return -1 ;
498+
499+ common_name = calloc (common_name_len + 1 , 1 );
500+ if (common_name == NULL )
501+ return -1 ;
502+
503+ X509_NAME_get_text_by_NID (name , NID_commonName , common_name , common_name_len + 1 );
504+
505+ /* NUL bytes in CN? */
506+ if (common_name_len != (int )strlen (common_name ))
507+ {
508+ g_warning ("NUL byte in Common Name field, probably a malicious certificate.\n" );
509+ rv = -2 ;
510+ goto out ;
511+ }
512+
513+ if ((addr = g_inet_address_new_from_string (host )) != NULL )
514+ {
515+ /*
516+ * We don't want to attempt wildcard matching against IP
517+ * addresses, so perform a simple comparison here.
518+ */
519+ if (g_strcmp0 (common_name , host ) == 0 )
520+ rv = 0 ;
521+ else
522+ rv = -1 ;
523+
524+ g_object_unref (addr );
525+ }
526+ else if (_SSL_match_hostname (common_name , host ) == 0 )
527+ rv = 0 ;
528+
529+ out :
530+ free (common_name );
531+ return rv ;
532+ }
533+
534+ int
535+ _SSL_check_hostname (X509 * cert , const char * host )
536+ {
537+ int rv ;
538+
539+ rv = _SSL_check_subject_altname (cert , host );
540+ if (rv == 0 || rv == -2 )
541+ return rv ;
542+
543+ return _SSL_check_common_name (cert , host );
544+ }
0 commit comments