diff --git a/ChangeLog b/ChangeLog index b19bd6c2c..2221e2cdd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,13 +2,15 @@ PolarSSL ChangeLog = Version Trunk Features - * Added Doxygen source code documentation parts (donated - by Fox-IT) +Note: Most of these features have been donated by Fox-IT + * Added Doxygen source code documentation parts * Added generic message digest wrapper for integration - with OpenVPN (donated by Fox-IT) + with OpenVPN * Added generic cipher wrapper for integration - with OpenVPN (donated by Fox-IT) + with OpenVPN * Added reading of DHM context from memory and file + * Added verification callback on certificate chain + verification to allow external blacklisting. = Version 0.14.0 released on 2010-08-16 Features diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 715a4e886..3d3c020b1 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -215,17 +215,19 @@ struct _ssl_context int max_minor_ver; /*!< max. minor version from client */ /* - * Callbacks (RNG, debug, I/O) + * Callbacks (RNG, debug, I/O, verification) */ int (*f_rng)(void *); void (*f_dbg)(void *, int, const char *); int (*f_recv)(void *, unsigned char *, int); int (*f_send)(void *, unsigned char *, int); + int (*f_vrfy)(void *, x509_cert *, int, int); void *p_rng; /*!< context for the RNG function */ void *p_dbg; /*!< context for the debug function */ void *p_recv; /*!< context for reading operations */ void *p_send; /*!< context for writing operations */ + void *p_vrfy; /*!< context for verification */ /* * Session layer @@ -353,6 +355,23 @@ void ssl_set_endpoint( ssl_context *ssl, int endpoint ); */ void ssl_set_authmode( ssl_context *ssl, int authmode ); +/** + * \brief Set the verification callback (Optional). + * + * If set, the verification callback is called once for every + * certificate in the chain. The verification function has the + * following parameter: (void *parameter, x509_cert certificate, + * int certifcate_depth, int preverify_ok). It should + * return 0 on SUCCESS. + * + * \param ssl SSL context + * \param f_vrfy verification function + * \param p_vrfy verification parameter + */ +void ssl_set_verify( ssl_context *ssl, + int (*f_vrfy)(void *, x509_cert *, int, int), + void *p_vrfy ); + /** * \brief Set the random number generator callback * diff --git a/include/polarssl/x509.h b/include/polarssl/x509.h index 0df84332f..c54fc2dd3 100644 --- a/include/polarssl/x509.h +++ b/include/polarssl/x509.h @@ -501,6 +501,8 @@ int x509parse_time_expired( const x509_time *time ); * \param cn expected Common Name (can be set to * NULL if the CN must not be verified) * \param flags result of the verification + * \param f_vrfy verification function + * \param p_vrfy verification parameter * * \return 0 if successful or POLARSSL_ERR_X509_SIG_VERIFY_FAILED, * in which case *flags will have one or more of @@ -515,7 +517,9 @@ int x509parse_time_expired( const x509_time *time ); int x509parse_verify( x509_cert *crt, x509_cert *trust_ca, x509_crl *ca_crl, - const char *cn, int *flags ); + const char *cn, int *flags, + int (*f_vrfy)(void *, x509_cert *, int, int), + void *p_vrfy ); /** @} name Functions to verify a certificate */ diff --git a/library/ssl_tls.c b/library/ssl_tls.c index 11d50ac99..a0be84f68 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -1404,7 +1404,8 @@ int ssl_parse_certificate( ssl_context *ssl ) } ret = x509parse_verify( ssl->peer_cert, ssl->ca_chain, ssl->ca_crl, - ssl->peer_cn, &ssl->verify_result ); + ssl->peer_cn, &ssl->verify_result, + ssl->f_vrfy, ssl->p_vrfy ); if( ret != 0 ) SSL_DEBUG_RET( 1, "x509_verify_cert", ret ); @@ -1725,6 +1726,14 @@ void ssl_set_authmode( ssl_context *ssl, int authmode ) ssl->authmode = authmode; } +void ssl_set_verify( ssl_context *ssl, + int (*f_vrfy)(void *, x509_cert *, int, int), + void *p_vrfy ) +{ + ssl->f_vrfy = f_vrfy; + ssl->p_vrfy = p_vrfy; +} + void ssl_set_rng( ssl_context *ssl, int (*f_rng)(void *), void *p_rng ) diff --git a/library/x509parse.c b/library/x509parse.c index e48ed949a..ec7e2186c 100644 --- a/library/x509parse.c +++ b/library/x509parse.c @@ -2335,7 +2335,9 @@ static void x509_hash( const unsigned char *in, int len, int alg, int x509parse_verify( x509_cert *crt, x509_cert *trust_ca, x509_crl *ca_crl, - const char *cn, int *flags ) + const char *cn, int *flags, + int (*f_vrfy)(void *, x509_cert *, int, int), + void *p_vrfy ) { int cn_len; int hash_id; @@ -2380,6 +2382,8 @@ int x509parse_verify( x509_cert *crt, while( cur != NULL && cur->version != 0 ) { + int verify_ok = 1; + if( cur->ca_istrue == 0 || crt->issuer_raw.len != cur->subject_raw.len || memcmp( crt->issuer_raw.p, cur->subject_raw.p, @@ -2395,7 +2399,15 @@ int x509parse_verify( x509_cert *crt, if( rsa_pkcs1_verify( &cur->rsa, RSA_PUBLIC, hash_id, 0, hash, crt->sig.p ) != 0 ) + verify_ok = 0; + + /* crt is verified to be a child of the parent cur, call verify callback */ + if( NULL != f_vrfy ) { + if ( f_vrfy( p_vrfy, crt, pathlen-1, verify_ok ) != 0 ) + return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + } else if ( verify_ok == 0 ) { return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + } pathlen++; @@ -2495,6 +2507,13 @@ int x509parse_verify( x509_cert *crt, if( *flags != 0 ) return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + + /* Verification succeeded, call callback on top cert */ + if( NULL != f_vrfy ) { + if ( f_vrfy(p_vrfy, crt, pathlen - 1, 1) != 0 ) + return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + } + return( 0 ); } @@ -2676,7 +2695,7 @@ int x509_self_test( int verbose ) if( verbose != 0 ) printf( "passed\n X.509 signature verify: "); - ret = x509parse_verify( &clicert, &cacert, NULL, "PolarSSL Client 2", &i ); + ret = x509parse_verify( &clicert, &cacert, NULL, "PolarSSL Client 2", &i, NULL, NULL ); if( ret != 0 ) { if( verbose != 0 ) diff --git a/programs/test/ssl_cert_test.c b/programs/test/ssl_cert_test.c index 9f4255b5c..74713ce04 100644 --- a/programs/test/ssl_cert_test.c +++ b/programs/test/ssl_cert_test.c @@ -146,7 +146,7 @@ int main( void ) printf( " . Verify the client certificate with CA certificate..." ); fflush( stdout ); - ret = x509parse_verify( &clicert, &cacert, &crl, NULL, &flags ); + ret = x509parse_verify( &clicert, &cacert, &crl, NULL, &flags, NULL, NULL ); if( ret != 0 ) { if( ret == POLARSSL_ERR_X509_CERT_VERIFY_FAILED ) diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index 898c52107..e130567e5 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -101,66 +101,73 @@ X509 Time Expired #6 x509_time_expired:"data_files/test-ca.crt":valid_to:0 X509 Certificate verification #1 (Revoked Cert, Expired CRL) -x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl_expired.pem":NULL:BADCERT_REVOKED | BADCRL_EXPIRED +x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl_expired.pem":NULL:POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_REVOKED | BADCRL_EXPIRED:NULL X509 Certificate verification #2 (Revoked Cert, Expired CRL) -x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl_expired.pem":"PolarSSL Server 1":BADCERT_REVOKED | BADCRL_EXPIRED +x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl_expired.pem":"PolarSSL Server 1":POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_REVOKED | BADCRL_EXPIRED:NULL X509 Certificate verification #3 (Revoked Cert, Expired CRL, CN Mismatch) -x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl_expired.pem":"PolarSSL Wrong CN":BADCERT_REVOKED | BADCRL_EXPIRED | BADCERT_CN_MISMATCH +x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl_expired.pem":"PolarSSL Wrong CN":POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_REVOKED | BADCRL_EXPIRED | BADCERT_CN_MISMATCH:NULL X509 Certificate verification #4 (Valid Cert, Expired CRL) -x509_verify:"data_files/server2.crt":"data_files/test-ca.crt":"data_files/crl_expired.pem":NULL:BADCRL_EXPIRED +x509_verify:"data_files/server2.crt":"data_files/test-ca.crt":"data_files/crl_expired.pem":NULL:POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCRL_EXPIRED:NULL X509 Certificate verification #5 (Revoked Cert) -x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:BADCERT_REVOKED +x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_REVOKED:NULL X509 Certificate verification #6 (Revoked Cert) -x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl.pem":"PolarSSL Server 1":BADCERT_REVOKED +x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl.pem":"PolarSSL Server 1":POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_REVOKED:NULL X509 Certificate verification #7 (Revoked Cert, CN Mismatch) -x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl.pem":"PolarSSL Wrong CN":BADCERT_REVOKED | BADCERT_CN_MISMATCH +x509_verify:"data_files/server1.crt":"data_files/test-ca.crt":"data_files/crl.pem":"PolarSSL Wrong CN":POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_REVOKED | BADCERT_CN_MISMATCH:NULL X509 Certificate verification #8 (Valid Cert) -x509_verify:"data_files/server2.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/server2.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL X509 Certificate verification #9 (Not trusted Cert) -x509_verify:"data_files/server2.crt":"data_files/server1.crt":"data_files/crl.pem":NULL:BADCERT_NOT_TRUSTED +x509_verify:"data_files/server2.crt":"data_files/server1.crt":"data_files/crl.pem":NULL:POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_NOT_TRUSTED:NULL X509 Certificate verification #10 (Not trusted Cert, Expired CRL) -x509_verify:"data_files/server2.crt":"data_files/server1.crt":"data_files/crl_expired.pem":NULL:BADCERT_NOT_TRUSTED +x509_verify:"data_files/server2.crt":"data_files/server1.crt":"data_files/crl_expired.pem":NULL:POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_NOT_TRUSTED:NULL X509 Certificate verification #11 (Valid Cert MD2 Digest) depends_on:POLARSSL_MD2_C -x509_verify:"data_files/cert_md2.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/cert_md2.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL X509 Certificate verification #12 (Valid Cert MD4 Digest) depends_on:POLARSSL_MD4_C -x509_verify:"data_files/cert_md4.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/cert_md4.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL X509 Certificate verification #13 (Valid Cert MD5 Digest) depends_on:POLARSSL_MD5_C -x509_verify:"data_files/cert_md5.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/cert_md5.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL X509 Certificate verification #14 (Valid Cert SHA1 Digest) depends_on:POLARSSL_SHA1_C -x509_verify:"data_files/cert_sha1.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/cert_sha1.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL X509 Certificate verification #15 (Valid Cert SHA224 Digest) depends_on:POLARSSL_SHA2_C -x509_verify:"data_files/cert_sha224.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/cert_sha224.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL X509 Certificate verification #16 (Valid Cert SHA256 Digest) depends_on:POLARSSL_SHA2_C -x509_verify:"data_files/cert_sha256.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/cert_sha256.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL X509 Certificate verification #17 (Valid Cert SHA384 Digest) depends_on:POLARSSL_SHA4_C -x509_verify:"data_files/cert_sha384.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/cert_sha384.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL X509 Certificate verification #18 (Valid Cert SHA512 Digest) depends_on:POLARSSL_SHA4_C -x509_verify:"data_files/cert_sha512.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0 +x509_verify:"data_files/cert_sha512.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:0:0:NULL + +X509 Certificate verification #19 (Valid Cert, denying callback) +depends_on:POLARSSL_SHA4_C +x509_verify:"data_files/cert_sha512.crt":"data_files/test-ca.crt":"data_files/crl.pem":NULL:POLARSSL_ERR_X509_CERT_VERIFY_FAILED:0:&verify_none + +X509 Certificate verification #20 (Not trusted Cert, allowing callback) +x509_verify:"data_files/server2.crt":"data_files/server1.crt":"data_files/crl_expired.pem":NULL:POLARSSL_ERR_X509_CERT_VERIFY_FAILED:BADCERT_NOT_TRUSTED:&verify_all X509 Parse Selftest depends_on:POLARSSL_MD5_C diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function index b56b89310..fe9a73375 100644 --- a/tests/suites/test_suite_x509parse.function +++ b/tests/suites/test_suite_x509parse.function @@ -1,5 +1,17 @@ BEGIN_HEADER +#include #include + +int verify_none( void *data, x509_cert *crt, int certificate_depth, int preverify_ok ) +{ + return 1; +} + +int verify_all( void *data, x509_cert *crt, int certificate_depth, int preverify_ok ) +{ + return 0; +} + END_HEADER BEGIN_CASE @@ -43,7 +55,7 @@ x509_crl_info:crl_file:result_str END_CASE BEGIN_CASE -x509_verify:crt_file:ca_file:crl_file:cn_name:result +x509_verify:crt_file:ca_file:crl_file:cn_name:result:flags:verify_callback { x509_cert crt; x509_cert ca; @@ -59,16 +71,10 @@ x509_verify:crt_file:ca_file:crl_file:cn_name:result TEST_ASSERT( x509parse_crtfile( &ca, {ca_file} ) == 0 ); TEST_ASSERT( x509parse_crlfile( &crl, {crl_file} ) == 0 ); - res = x509parse_verify( &crt, &ca, &crl, {cn_name}, &flags ); + res = x509parse_verify( &crt, &ca, &crl, {cn_name}, &flags, {verify_callback}, NULL ); - if( res == 0 ) - { - TEST_ASSERT( res == ( {result} ) ); - } - else - { - TEST_ASSERT( flags == ( {result} ) ); - } + TEST_ASSERT( res == ( {result} ) ); + TEST_ASSERT( flags == ( {flags} ) ); } END_CASE