- Added verification callback in certificate verification chain in order to allow external blacklisting

diff --git a/ChangeLog b/ChangeLog
index b19bd6c..2221e2c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,13 +2,15 @@
 
 = 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 715a4e8..3d3c020 100644
--- a/include/polarssl/ssl.h
+++ b/include/polarssl/ssl.h
@@ -215,17 +215,19 @@
     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
@@ -354,6 +356,23 @@
 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
  *
  * \param ssl      SSL context
diff --git a/include/polarssl/x509.h b/include/polarssl/x509.h
index 0df8433..c54fc2d 100644
--- a/include/polarssl/x509.h
+++ b/include/polarssl/x509.h
@@ -501,6 +501,8 @@
  * \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_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 11d50ac..a0be84f 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -1404,7 +1404,8 @@
         }
 
         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 @@
     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 e48ed94..ec7e218 100644
--- a/library/x509parse.c
+++ b/library/x509parse.c
@@ -2335,7 +2335,9 @@
 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 @@
 
     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 @@
 
         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 @@
     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 @@
     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 9f4255b..74713ce 100644
--- a/programs/test/ssl_cert_test.c
+++ b/programs/test/ssl_cert_test.c
@@ -146,7 +146,7 @@
         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 898c521..e130567 100644
--- a/tests/suites/test_suite_x509parse.data
+++ b/tests/suites/test_suite_x509parse.data
@@ -101,66 +101,73 @@
 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 b56b893..fe9a733 100644
--- a/tests/suites/test_suite_x509parse.function
+++ b/tests/suites/test_suite_x509parse.function
@@ -1,5 +1,17 @@
 BEGIN_HEADER
+#include <polarssl/config.h>
 #include <polarssl/x509.h>
+
+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 @@
 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 @@
     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