diff --git a/ChangeLog b/ChangeLog index 516462ee9..1730fcd67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,10 @@ PolarSSL ChangeLog systems (Found by Gernot). * Undefining POLARSSL_HAVE_ASM now also handles prevents asm in padlock and timing code. + * Fixed an off-by-one buffer allocation in ssl_set_hostname(). + * Added support for Certificate Revocation List (CRL parsing. + * Added support for CRL revocation to x509parse_verify() and + SSL/TLS code. = Version 0.10.0 released on 2009-01-12 * Migrated XySSL to PolarSSL diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index f4d7d1acc..b26e4edb6 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -235,6 +235,7 @@ struct _ssl_context rsa_context *rsa_key; /*!< own RSA private key */ x509_cert *own_cert; /*!< own X.509 certificate */ x509_cert *ca_chain; /*!< own trusted CA chain */ + x509_crl *ca_crl; /*!< trusted CA CRLs */ x509_cert *peer_cert; /*!< peer X.509 cert chain */ char *peer_cn; /*!< expected peer CN */ @@ -389,12 +390,13 @@ void ssl_set_ciphers( ssl_context *ssl, int *ciphers ); * * \param ssl SSL context * \param ca_chain trusted CA chain + * \param ca_crl trusted CA CRLs * \param peer_cn expected peer CommonName (or NULL) * * \note TODO: add two more parameters: depth and crl */ void ssl_set_ca_chain( ssl_context *ssl, x509_cert *ca_chain, - char *peer_cn ); + x509_crl *ca_crl, char *peer_cn ); /** * \brief Set own certificate and private key diff --git a/include/polarssl/x509.h b/include/polarssl/x509.h index c0a50b772..b7b971c70 100644 --- a/include/polarssl/x509.h +++ b/include/polarssl/x509.h @@ -60,6 +60,8 @@ #define BADCERT_REVOKED 2 #define BADCERT_CN_MISMATCH 4 #define BADCERT_NOT_TRUSTED 8 +#define BADCRL_NOT_TRUSTED 16 +#define BADCRL_EXPIRED 32 /* * DER constants @@ -335,16 +337,17 @@ int x509parse_cert_info( char *buf, size_t size, char *prefix, x509_cert *crt ); int x509parse_crl_info( char *buf, size_t size, char *prefix, x509_crl *crl ); /** - * \brief Return 0 if the certificate is still valid, - * or BADCERT_EXPIRED + * \brief Return 0 if the x509_time is still valid, + * or 1 otherwise. */ -int x509parse_expired( x509_cert *crt ); +int x509parse_time_expired( x509_time *time ); /** * \brief Verify the certificate signature * * \param crt a certificate to be verified * \param trust_ca the trusted CA chain + * \param ca_crl the CRL chain for trusted CA's * \param cn expected Common Name (can be set to * NULL if the CN must not be verified) * \param flags result of the verification @@ -361,6 +364,7 @@ int x509parse_expired( x509_cert *crt ); */ int x509parse_verify( x509_cert *crt, x509_cert *trust_ca, + x509_crl *ca_crl, char *cn, int *flags ); /** diff --git a/library/ssl_tls.c b/library/ssl_tls.c index ecaefb7f9..b21e44bd3 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -1332,7 +1332,7 @@ int ssl_parse_certificate( ssl_context *ssl ) return( POLARSSL_ERR_SSL_CA_CHAIN_REQUIRED ); } - ret = x509parse_verify( ssl->peer_cert, ssl->ca_chain, + ret = x509parse_verify( ssl->peer_cert, ssl->ca_chain, ssl->ca_crl, ssl->peer_cn, &ssl->verify_result ); if( ret != 0 ) @@ -1702,9 +1702,10 @@ void ssl_set_ciphers( ssl_context *ssl, int *ciphers ) } void ssl_set_ca_chain( ssl_context *ssl, x509_cert *ca_chain, - char *peer_cn ) + x509_crl *ca_crl, char *peer_cn ) { ssl->ca_chain = ca_chain; + ssl->ca_crl = ca_crl; ssl->peer_cn = peer_cn; } @@ -1740,10 +1741,12 @@ int ssl_set_hostname( ssl_context *ssl, char *hostname ) return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); ssl->hostname_len = strlen( hostname ); - ssl->hostname = (unsigned char *) malloc( ssl->hostname_len ); + ssl->hostname = (unsigned char *) malloc( ssl->hostname_len + 1 ); memcpy( ssl->hostname, (unsigned char *) hostname, ssl->hostname_len ); + + ssl->hostname[ssl->hostname_len] = '\0'; return( 0 ); } diff --git a/library/x509parse.c b/library/x509parse.c index de9abe319..79bb0a6ed 100644 --- a/library/x509parse.c +++ b/library/x509parse.c @@ -2068,9 +2068,9 @@ int x509parse_crl_info( char *buf, size_t size, char *prefix, x509_crl *crl ) } /* - * Return 0 if the certificate is still valid, or BADCERT_EXPIRED + * Return 0 if the x509_time is still valid, or 1 otherwise. */ -int x509parse_expired( x509_cert *crt ) +int x509parse_time_expired( x509_time *to ) { struct tm *lt; time_t tt; @@ -2078,17 +2078,38 @@ int x509parse_expired( x509_cert *crt ) tt = time( NULL ); lt = localtime( &tt ); - if( lt->tm_year > crt->valid_to.year - 1900 ) - return( BADCERT_EXPIRED ); + if( lt->tm_year > to->year - 1900 ) + return( 1 ); - if( lt->tm_year == crt->valid_to.year - 1900 && - lt->tm_mon > crt->valid_to.mon - 1 ) - return( BADCERT_EXPIRED ); + if( lt->tm_year == to->year - 1900 && + lt->tm_mon > to->mon - 1 ) + return( 1 ); - if( lt->tm_year == crt->valid_to.year - 1900 && - lt->tm_mon == crt->valid_to.mon - 1 && - lt->tm_mday > crt->valid_to.day ) - return( BADCERT_EXPIRED ); + if( lt->tm_year == to->year - 1900 && + lt->tm_mon == to->mon - 1 && + lt->tm_mday > to->day ) + return( 1 ); + + return( 0 ); +} + +/* + * Return 1 if the certificate is revoked, or 0 otherwise. + */ +int x509parse_revoked( x509_cert *crt, x509_crl *crl ) +{ + x509_crl_entry *cur = &crl->entry; + + while( cur != NULL && cur->serial.len != 0 ) + { + if( memcmp( crt->serial.p, cur->serial.p, crt->serial.len ) == 0 ) + { + if( x509parse_time_expired( &cur->revocation_date ) ) + return( 1 ); + } + + cur = cur->next; + } return( 0 ); } @@ -2125,6 +2146,7 @@ static void x509_hash( unsigned char *in, int len, int alg, */ int x509parse_verify( x509_cert *crt, x509_cert *trust_ca, + x509_crl *ca_crl, char *cn, int *flags ) { int cn_len; @@ -2134,7 +2156,10 @@ int x509parse_verify( x509_cert *crt, x509_name *name; unsigned char hash[64]; - *flags = x509parse_expired( crt ); + *flags = 0; + + if( x509parse_time_expired( &crt->valid_to ) ) + *flags = BADCERT_EXPIRED; if( cn != NULL ) { @@ -2224,6 +2249,61 @@ int x509parse_verify( x509_cert *crt, trust_ca = trust_ca->next; } + /* + * TODO: What happens if no CRL is present? + * Suggestion: Revocation state should be unknown if no CRL is present. + * For backwards compatibility this is not yet implemented. + */ + + /* + * Check if the topmost certificate is revoked if the trusted CA is + * determined. + */ + while( trust_ca != NULL && ca_crl != NULL && ca_crl->version != 0 ) + { + if( ca_crl->issuer_raw.len != trust_ca->subject_raw.len || + memcmp( ca_crl->issuer_raw.p, trust_ca->subject_raw.p, + ca_crl->issuer_raw.len ) != 0 ) + { + ca_crl = ca_crl->next; + continue; + } + + /* + * Check if CRL is correctry signed by the trusted CA + */ + hash_id = ca_crl->sig_oid1.p[8]; + + x509_hash( ca_crl->tbs.p, ca_crl->tbs.len, hash_id, hash ); + + if( !rsa_pkcs1_verify( &trust_ca->rsa, RSA_PUBLIC, hash_id, + 0, hash, ca_crl->sig.p ) == 0 ) + { + /* + * CRL is not trusted + */ + *flags |= BADCRL_NOT_TRUSTED; + break; + } + + /* + * Check for validity of CRL (Do not drop out) + */ + if( x509parse_time_expired( &ca_crl->next_update ) ) + *flags |= BADCRL_EXPIRED; + + /* + * Check if certificate is revoked + */ + if( x509parse_revoked(crt, ca_crl) ) + { + *flags |= BADCERT_REVOKED; + break; + } + + ca_crl = ca_crl->next; + } + if( *flags != 0 ) return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); @@ -2406,7 +2486,7 @@ int x509_self_test( int verbose ) if( verbose != 0 ) printf( "passed\n X.509 signature verify: "); - ret = x509parse_verify( &clicert, &cacert, "Joe User", &i ); + ret = x509parse_verify( &clicert, &cacert, NULL, "Joe User", &i ); if( ret != 0 ) { if( verbose != 0 ) diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 66e0c69a1..86720e60c 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -158,7 +158,7 @@ int main( void ) ssl_set_ciphers( &ssl, ssl_default_ciphers ); ssl_set_session( &ssl, 1, 600, &ssn ); - ssl_set_ca_chain( &ssl, &cacert, SERVER_NAME ); + ssl_set_ca_chain( &ssl, &cacert, NULL, SERVER_NAME ); ssl_set_own_cert( &ssl, &clicert, &rsa ); ssl_set_hostname( &ssl, SERVER_NAME ); diff --git a/programs/ssl/ssl_server.c b/programs/ssl/ssl_server.c index f44c933a8..f9b9b36c0 100644 --- a/programs/ssl/ssl_server.c +++ b/programs/ssl/ssl_server.c @@ -286,7 +286,7 @@ accept: memset( &ssn, 0, sizeof( ssl_session ) ); - ssl_set_ca_chain( &ssl, srvcert.next, NULL ); + ssl_set_ca_chain( &ssl, srvcert.next, NULL, NULL ); ssl_set_own_cert( &ssl, &srvcert, &rsa ); ssl_set_dh_param( &ssl, my_dhm_P, my_dhm_G ); diff --git a/programs/test/ssl_cert_test.c b/programs/test/ssl_cert_test.c index a0c2e13d8..00488bae4 100644 --- a/programs/test/ssl_cert_test.c +++ b/programs/test/ssl_cert_test.c @@ -32,12 +32,14 @@ #define snprintf _snprintf #endif -#define MAX_CLIENT_CERTS 6 +#define MAX_CLIENT_CERTS 8 char *client_certificates[MAX_CLIENT_CERTS] = { "client1.crt", "client2.crt", + "server1.crt", + "server2.crt", "cert_sha224.crt", "cert_sha256.crt", "cert_sha384.crt", @@ -48,6 +50,8 @@ char *client_private_keys[MAX_CLIENT_CERTS] = { "client1.key", "client2.key", + "server1.key", + "server2.key", "cert_sha224.key", "cert_sha256.key", "cert_sha384.key", @@ -83,6 +87,9 @@ int main( void ) printf( " ok\n" ); + x509parse_cert_info( buf, 1024, "CRT: ", &cacert ); + printf("%s\n", buf ); + /* * 1.2. Load the CRL */ @@ -134,11 +141,17 @@ int main( void ) printf( " . Verify the client certificate with CA certificate..." ); fflush( stdout ); - ret = x509parse_verify( &clicert, &cacert, NULL, &flags ); + ret = x509parse_verify( &clicert, &cacert, &crl, NULL, &flags ); if( ret != 0 ) { - printf( " failed\n ! x509parse_verify returned %d\n\n", ret ); - goto exit; + if( ret == POLARSSL_ERR_X509_CERT_VERIFY_FAILED ) + { + if( flags == BADCERT_REVOKED ) + printf( " REVOKED " ); + } else { + printf( " failed\n ! x509parse_verify returned %d\n\n", ret ); + goto exit; + } } printf( " ok\n" ); diff --git a/programs/test/ssl_test.c b/programs/test/ssl_test.c index 32ea1c1c6..0c088d4b2 100644 --- a/programs/test/ssl_test.c +++ b/programs/test/ssl_test.c @@ -225,7 +225,7 @@ static int ssl_test( struct options *opt ) ssl_set_endpoint( &ssl, SSL_IS_SERVER ); ssl_set_dh_param( &ssl, dhm_P, dhm_G ); - ssl_set_ca_chain( &ssl, srvcert.next, NULL ); + ssl_set_ca_chain( &ssl, srvcert.next, NULL, NULL ); ssl_set_own_cert( &ssl, &srvcert, &rsa ); }