@ -28,6 +28,7 @@
# include <openssl / ssl.h>
# include <openssl / err.h>
# include <openssl / hmac.h>
# include <openssl / x509v3.h>
# endif
# include "isync.h"
@ -118,6 +119,62 @@ compare_certificates( X509 *cert, X509 *peercert,
return 0 ;
}
static int
host_matches ( const char * host , const char * pattern )
{
if ( pattern [ 0 ] = = ' * ' & & pattern [ 1 ] = = ' . ' ) {
pattern + = 2 ;
if ( ! ( host = strchr ( host , ' . ' ) ) )
return 0 ;
host + + ;
}
return * host & & * pattern & & ! strcasecmp ( host , pattern ) ;
}
static int
verify_hostname ( X509 * cert , const char * hostname )
{
int i , len , found ;
X509_NAME * subj ;
STACK_OF ( GENERAL_NAME ) * subj_alt_names ;
char cname [ 1000 ] ;
/* try the DNS subjectAltNames */
found = 0 ;
if ( ( subj_alt_names = X509_get_ext_d2i ( cert , NID_subject_alt_name , NULL , NULL ) ) ) {
int num_subj_alt_names = sk_GENERAL_NAME_num ( subj_alt_names ) ;
for ( i = 0 ; i < num_subj_alt_names ; i + + ) {
GENERAL_NAME * subj_alt_name = sk_GENERAL_NAME_value ( subj_alt_names , i ) ;
if ( subj_alt_name - > type = = GEN_DNS & &
strlen ( ( const char * ) subj_alt_name - > d . ia5 - > data ) = = ( size_t ) subj_alt_name - > d . ia5 - > length & &
host_matches ( hostname , ( const char * ) ( subj_alt_name - > d . ia5 - > data ) ) )
{
found = 1 ;
break ;
}
}
sk_GENERAL_NAME_pop_free ( subj_alt_names , GENERAL_NAME_free ) ;
}
if ( found )
return 0 ;
/* try the common name */
if ( ! ( subj = X509_get_subject_name ( cert ) ) ) {
error ( " Error, cannot get certificate subject \n " ) ;
return - 1 ;
}
if ( ( len = X509_NAME_get_text_by_NID ( subj , NID_commonName , cname , sizeof ( cname ) ) ) < 0 ) {
error ( " Error, cannot get certificate common name \n " ) ;
return - 1 ;
}
if ( strlen ( cname ) = = ( size_t ) len & & host_matches ( hostname , cname ) )
return 0 ;
error ( " Error, certificate owner does not match hostname %s \n " , hostname ) ;
return - 1 ;
}
# if OPENSSL_VERSION_NUMBER >= 0x00904000L
# define READ_X509_KEY(fp, key) PEM_read_X509( fp, key, 0, 0 )
# else
@ -146,6 +203,8 @@ verify_cert( const server_conf_t *conf, conn_t *sock )
}
while ( conf - > cert_file ) { /* while() instead of if() so break works */
/* Note: this code intentionally does no hostname verification. */
if ( X509_cmp_current_time ( X509_get_notBefore ( cert ) ) > = 0 ) {
error ( " Server certificate is not yet valid \n " ) ;
break ;
@ -164,7 +223,7 @@ verify_cert( const server_conf_t *conf, conn_t *sock )
}
err = - 1 ;
for ( lcert = 0 ; READ_X509_KEY ( fp , & lcert ) ; )
if ( ! ( err = compare_certificates ( lcert , cert , md , n ) ) )
if ( ! ( err = compare_certificates ( lcert , cert , md , n ) ) ) /* TODO: check X509v3 [Extended] Key Usage? */
break ;
X509_free ( lcert ) ;
fclose ( fp ) ;
@ -193,11 +252,16 @@ verify_cert( const server_conf_t *conf, conn_t *sock )
X509_STORE_CTX_init ( & xsc , mconf - > cert_store , cert , 0 ) ;
err = X509_verify_cert ( & xsc ) > 0 ? 0 : X509_STORE_CTX_get_error ( & xsc ) ;
X509_STORE_CTX_cleanup ( & xsc ) ;
if ( ! err )
return 0 ;
error ( " Error, cannot verify certificate: %s (%d) \n " ,
X509_verify_cert_error_string ( err ) , err ) ;
if ( err ) {
error ( " Error, cannot verify certificate: %s (%d) \n " ,
X509_verify_cert_error_string ( err ) , err ) ;
goto intcheck ;
}
if ( conf - > host & & verify_hostname ( cert , conf - > host ) < 0 )
goto intcheck ;
return 0 ;
intcheck :
X509_NAME_oneline ( X509_get_subject_name ( cert ) , buf , sizeof ( buf ) ) ;
info ( " \n Subject: %s \n " , buf ) ;
X509_NAME_oneline ( X509_get_issuer_name ( cert ) , buf , sizeof ( buf ) ) ;