Author: rjung Date: Fri May 22 12:39:51 2015 New Revision: 1681092 URL: http://svn.apache.org/r1681092 Log: Sync trunk with 1.1.x branch for ssl.
1.1 has several additions trunk never got, e.g. OCSP support, pkcs12 support and others. Below is a list of revisions added to 1.1.x which are part of the forward port. The only remaining changes that have not been forward ported all concern sslnetwork.c: r1456353 | mturk | 2013-03-14 08:35:56 +0100 (Thu, 14 Mar 2013) | 1 line Prevent infinite loop for SSL_ERROR_SSL r1409681 | mturk | 2012-11-15 08:59:36 +0100 (Thu, 15 Nov 2012) | 1 line Clean up SSL network layer. Ensure the behavior is similar to standard APR layer They have not (yet) been forward ported, because it is not clear (to me) how well they apply and whether they conflict with the following change in trunk, which is not part of 1.1: r1299312 | costin | 2012-03-11 00:59:25 +0100 (Sun, 11 Mar 2012) | 5 lines Small fix for ssl_read/ssl_write if the socket is not blocking. Tomcat is not currently using apr in non blocking mode, so it shouldn't be affected. Without the fix read would block. Also added/fixed methods in sslext to set the mode - needed for non-blocking write. It would be nice if we could clean up this remaining difference. Apart from that the only things in trunk ssl that are now not in 1.1 are the recent additions concerning ALPN and SNI support. An addition to forward ports, this commit also removes all code with #if or #ifdef checks which only apply to OpenSSL 1.0.1 or earlier and removes all code only active when OPENSSL_USE_DEPRECATED is defined. As a consequence support for custom DHE params is gone but this will re-intgrated from mod_ssl in a more appropriate form to trunk and 1.1 next. List of forward ports contained in this commit: r1680456 | billbarker | 2015-05-20 06:22:58 +0200 (Wed, 20 May 2015) | 1 line Fix errors when compiling against openssl master r1673617 | billbarker | 2015-04-15 04:08:02 +0200 (Wed, 15 Apr 2015) | 1 line More cleanup of thread ID handling for openssl master plus remove compile warning that doesn't make any difference. Still no change if compiling against any currently released v ersion of openssl r1673333 | billbarker | 2015-04-14 03:41:49 +0200 (Tue, 14 Apr 2015) | 1 line openssl master handles thread id itself (and better than we do). No change if compiling against any currently released version of openssl r1649733 | billbarker | 2015-01-06 05:42:24 +0100 (Tue, 06 Jan 2015) | 1 line More fixes to help Gump build. No change if compiling against any currently released version of openssl Plus removing all code that is only used for older OpenSSL versions than 1.0.2 plus remove all code that only runs for OPENSSL_USE_DEPRECATED. r1649278 | billbarker | 2015-01-04 02:06:53 +0100 (Sun, 04 Jan 2015) | 1 line Fix compilation error for Gump. There is no change if compiling against any currently released version of openssl. r1586221 | mturk | 2014-04-10 07:44:09 +0200 (Thu, 10 Apr 2014) | 1 line Revert NO_SSL_INTERN define. We need direct struct access for sslinfo r1585957 | mturk | 2014-04-09 14:54:25 +0200 (Wed, 09 Apr 2014) | 1 line Ensure we have the needed ECDH with the OpenSSL used r1585923 | mturk | 2014-04-09 11:52:40 +0200 (Wed, 09 Apr 2014) | 1 line As per Tom's suggestion. Set SSL_OP_SINGLE_ECDH_USE to context r1585898 | mturk | 2014-04-09 09:32:29 +0200 (Wed, 09 Apr 2014) | 1 line Apply Ognjen's patch for bz55915 r1507125 | schultz | 2013-07-25 23:01:25 +0200 (Thu, 25 Jul 2013) | 5 lines Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=53937 Eliminate double-call to apr_pool_destroy. r1409079 | mturk | 2012-11-14 06:21:53 +0100 (Wed, 14 Nov 2012) | 1 line Trim trailing spaces - No functional change r1350270 | rjung | 2012-06-14 16:38:33 +0200 (Thu, 14 Jun 2012) | 4 lines Return error if BIO_flush() didn't work. Fixes compiler warning about unused return value. r1294171 | mturk | 2012-02-27 15:56:07 +0100 (Mon, 27 Feb 2012) | 1 line Ensure that cert_file is present. Key can be null in which case it'll get the cert_file value, but cer must be provided r1293368 | mturk | 2012-02-24 19:07:07 +0100 (Fri, 24 Feb 2012) | 1 line Fix typo. Thanks Felix :) r1293187 | mturk | 2012-02-24 12:29:35 +0100 (Fri, 24 Feb 2012) | 1 line Use correct variable types to suppress compile time warning r1293144 | mturk | 2012-02-24 10:26:28 +0100 (Fri, 24 Feb 2012) | 1 line Fix few remaining issues in OCSP patch r1293123 | mturk | 2012-02-24 09:01:52 +0100 (Fri, 24 Feb 2012) | 1 line Fix code style. No functional change r1293122 | mturk | 2012-02-24 08:58:26 +0100 (Fri, 24 Feb 2012) | 1 line Apply additional security check from mod_ssl r1293120 | mturk | 2012-02-24 08:46:39 +0100 (Fri, 24 Feb 2012) | 1 line No need for public function r1293119 | mturk | 2012-02-24 08:43:44 +0100 (Fri, 24 Feb 2012) | 1 line BZ45392 Apply modified patch. OCSP is enabled is explicitly configure with --enable-ocsp at build time r1292859 | mturk | 2012-02-23 18:21:50 +0100 (Thu, 23 Feb 2012) | 1 line Add support for loading pkcs12 certificates. Certificate must have .pkcs12 extension Modified: tomcat/native/trunk/native/configure.in tomcat/native/trunk/native/include/ssl_private.h tomcat/native/trunk/native/src/ssl.c tomcat/native/trunk/native/src/sslcontext.c tomcat/native/trunk/native/src/sslnetwork.c tomcat/native/trunk/native/src/sslutils.c Modified: tomcat/native/trunk/native/configure.in URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/configure.in?rev=1681092&r1=1681091&r2=1681092&view=diff ============================================================================== --- tomcat/native/trunk/native/configure.in (original) +++ tomcat/native/trunk/native/configure.in Fri May 22 12:39:51 2015 @@ -149,6 +149,17 @@ AC_ARG_ENABLE(openssl, esac ]) +AC_ARG_ENABLE(ocsp, +[AS_HELP_STRING([--enable-ocsp],[Turn on OpenSSL OCSP verification support])], +[ + case "${enableval}" in + yes ) + APR_ADDTO(CFLAGS, [-DHAVE_OPENSSL_OCSP]) + AC_MSG_RESULT([Enabling OCSP verification support...]) + ;; + esac +]) + if $use_openssl ; then TCN_CHECK_SSL_TOOLKIT fi Modified: tomcat/native/trunk/native/include/ssl_private.h URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/include/ssl_private.h?rev=1681092&r1=1681091&r2=1681092&view=diff ============================================================================== --- tomcat/native/trunk/native/include/ssl_private.h (original) +++ tomcat/native/trunk/native/include/ssl_private.h Fri May 22 12:39:51 2015 @@ -40,10 +40,12 @@ #endif /* OpenSSL headers */ +#include <openssl/opensslv.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/x509.h> #include <openssl/pem.h> +#include <openssl/pkcs12.h> #include <openssl/crypto.h> #include <openssl/evp.h> #include <openssl/rand.h> @@ -200,12 +202,20 @@ || (errnum == X509_V_ERR_CERT_UNTRUSTED) \ || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) - - #define SSL_DEFAULT_PASS_PROMPT "Some of your private key files are encrypted for security reasons.\n" \ "In order to read them you have to provide the pass phrases.\n" \ "Enter password :" +#define OCSP_STATUS_OK 0 +#define OCSP_STATUS_REVOKED 1 +#define OCSP_STATUS_UNKNOWN 2 + + +/* ECC: make sure we have at least 1.0.0 */ +#if !defined(OPENSSL_NO_EC) && defined(TLSEXT_ECPOINTFORMAT_uncompressed) +#define HAVE_ECC 1 +#endif + extern void *SSL_temp_keys[SSL_TMP_KEY_MAX]; typedef struct { @@ -317,4 +327,5 @@ void SSL_callback_handshake(const int SSL_CTX_use_certificate_chain(SSL_CTX *, const char *, int); int SSL_callback_SSL_verify(int, X509_STORE_CTX *); int SSL_rand_seed(const char *file); + #endif /* SSL_PRIVATE_H */ Modified: tomcat/native/trunk/native/src/ssl.c URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/src/ssl.c?rev=1681092&r1=1681091&r2=1681092&view=diff ============================================================================== --- tomcat/native/trunk/native/src/ssl.c (original) +++ tomcat/native/trunk/native/src/ssl.c Fri May 22 12:39:51 2015 @@ -53,11 +53,15 @@ struct CRYPTO_dynlock_value { * Handle the Temporary RSA Keys and DH Params */ +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) #define SSL_TMP_KEY_FREE(type, idx) \ if (SSL_temp_keys[idx]) { \ type##_free((type *)SSL_temp_keys[idx]); \ SSL_temp_keys[idx] = NULL; \ } else (void)(0) +#else +#define SSL_TMP_KEY_FREE(type, idx) SSL_temp_keys[idx] = NULL +#endif #define SSL_TMP_KEYS_FREE(type) \ SSL_TMP_KEY_FREE(type, SSL_TMP_KEY_##type##_512); \ @@ -229,20 +233,39 @@ static const jint supported_ssl_opts = 0 static int ssl_tmp_key_init_rsa(int bits, int idx) { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) if (!(SSL_temp_keys[idx] = - RSA_generate_key(bits, RSA_F4, NULL, NULL))) + RSA_generate_key(bits, RSA_F4, NULL, NULL))) { +#ifdef OPENSSL_FIPS + /** + * With FIPS mode short RSA keys cannot be + * generated. + */ + if (bits < 1024) + return 0; + else +#endif return 1; - else + } + else { return 0; + } +#else + return 0; +#endif } static int ssl_tmp_key_init_dh(int bits, int idx) { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) if (!(SSL_temp_keys[idx] = SSL_dh_get_tmp_param(bits))) return 1; else return 0; +#else + return 0; +#endif } @@ -292,7 +315,11 @@ static apr_status_t ssl_init_cleanup(voi ENGINE_cleanup(); #endif CRYPTO_cleanup_all_ex_data(); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) ERR_remove_state(0); +#else + ERR_remove_thread_state(NULL); +#endif /* Don't call ERR_free_strings here; ERR_load_*_strings only * actually load the error strings once per process due to static @@ -368,7 +395,9 @@ static apr_status_t ssl_thread_cleanup(v { UNREFERENCED(data); CRYPTO_set_locking_callback(NULL); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) CRYPTO_set_id_callback(NULL); +#endif CRYPTO_set_dynlock_create_callback(NULL); CRYPTO_set_dynlock_lock_callback(NULL); CRYPTO_set_dynlock_destroy_callback(NULL); @@ -430,6 +459,8 @@ static struct CRYPTO_dynlock_value *ssl_ static void ssl_dyn_lock_function(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line) { + + if (mode & CRYPTO_LOCK) { apr_thread_mutex_lock(l->mutex); } @@ -467,7 +498,9 @@ static void ssl_thread_setup(apr_pool_t APR_THREAD_MUTEX_DEFAULT, p); } +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) CRYPTO_set_id_callback(ssl_thread_id); +#endif CRYPTO_set_locking_callback(ssl_thread_lock); /* Set up dynamic locking scaffolding for OpenSSL to use at its @@ -606,9 +639,9 @@ static int ssl_rand_make(const char *fil BIO_write(out, buf, len); num -= len; } - BIO_flush(out); + r = BIO_flush(out); BIO_free_all(out); - return 1; + return r > 0 ? 1 : 0; } TCN_IMPLEMENT_CALL(jint, SSL, initialize)(TCN_STDARGS, jstring engine) Modified: tomcat/native/trunk/native/src/sslcontext.c URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/src/sslcontext.c?rev=1681092&r1=1681091&r2=1681092&view=diff ============================================================================== --- tomcat/native/trunk/native/src/sslcontext.c (original) +++ tomcat/native/trunk/native/src/sslcontext.c Fri May 22 12:39:51 2015 @@ -71,7 +71,7 @@ static jmethodID sni_java_callback; int ssl_callback_ServerNameIndication(SSL *ssl, int *al, tcn_ssl_ctxt_t *c) { // TODO: Is it better to cache the JNIEnv* during the call to handshake? - + // Get the JNI environment for this callback JavaVM *javavm = tcn_get_java_vm(); JNIEnv *env; @@ -79,14 +79,14 @@ int ssl_callback_ServerNameIndication(SS jstring hostname; jlong original_ssl_context, new_ssl_context; (*javavm)->AttachCurrentThread(javavm, (void **)&env, NULL); - + // Get the host name presented by the client servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); - + // Convert parameters ready for the method call hostname = (*env)->NewStringUTF(env, servername); original_ssl_context = P2J(c->ctx); - + // Make the call new_ssl_context = (*env)->CallStaticLongMethod(env, ssl_context_class, @@ -97,10 +97,10 @@ int ssl_callback_ServerNameIndication(SS if (original_ssl_context != new_ssl_context) { SSL_set_SSL_CTX(ssl, J2P(new_ssl_context, SSL_CTX *)); } - + return SSL_TLSEXT_ERR_OK; } - + /* Initialize server context */ TCN_IMPLEMENT_CALL(jlong, SSLContext, make)(TCN_STDARGS, jlong pool, jint protocol, jint mode) @@ -141,6 +141,15 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma ctx = SSL_CTX_new(SSLv3_server_method()); else ctx = SSL_CTX_new(SSLv3_method()); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) && !defined(OPENSSL_NO_SSL2) + } else if (protocol == SSL_PROTOCOL_SSLV2) { + if (mode == SSL_MODE_CLIENT) + ctx = SSL_CTX_new(SSLv2_client_method()); + else if (mode == SSL_MODE_SERVER) + ctx = SSL_CTX_new(SSLv2_server_method()); + else + ctx = SSL_CTX_new(SSLv2_method()); +#endif #ifndef OPENSSL_NO_SSL2 } else if (protocol == SSL_PROTOCOL_SSLV2) { if (mode == SSL_MODE_CLIENT) @@ -159,12 +168,21 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma /* requested but not supported */ #endif } else { +#if OPENSSL_VERSION_NUMBER < 0x10100000L if (mode == SSL_MODE_CLIENT) - ctx = SSL_CTX_new(SSLv23_client_method()); + ctx = SSL_CTX_new(SSLv23_client_method()); else if (mode == SSL_MODE_SERVER) - ctx = SSL_CTX_new(SSLv23_server_method()); + ctx = SSL_CTX_new(SSLv23_server_method()); else - ctx = SSL_CTX_new(SSLv23_method()); + ctx = SSL_CTX_new(SSLv23_method()); +#else + if (mode == SSL_MODE_CLIENT) + ctx = SSL_CTX_new(TLS_client_method()); + else if (mode == SSL_MODE_SERVER) + ctx = SSL_CTX_new(TLS_server_method()); + else + ctx = SSL_CTX_new(TLS_method()); +#endif } if (!ctx) { @@ -204,6 +222,9 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma * Configure additional context ingredients */ SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_DH_USE); +#ifdef HAVE_ECC + SSL_CTX_set_options(c->ctx, SSL_OP_SINGLE_ECDH_USE); +#endif /** To get back the tomcat wrapper from CTX */ SSL_CTX_set_app_data(c->ctx, (char *)c); @@ -221,6 +242,12 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma (unsigned long)((sizeof SSL_DEFAULT_VHOST_NAME) - 1), &(c->context_id[0]), NULL, EVP_sha1(), NULL); if (mode) { +#ifdef HAVE_ECC + /* Set default (nistp256) elliptic curve for ephemeral ECDH keys */ + EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + SSL_CTX_set_tmp_ecdh(c->ctx, ecdh); + EC_KEY_free(ecdh); +#endif SSL_CTX_set_tmp_rsa_callback(c->ctx, SSL_callback_tmp_RSA); SSL_CTX_set_tmp_dh_callback(c->ctx, SSL_callback_tmp_DH); } @@ -235,7 +262,7 @@ TCN_IMPLEMENT_CALL(jlong, SSLContext, ma SSL_CTX_set_default_passwd_cb(c->ctx, (pem_password_cb *)SSL_password_callback); SSL_CTX_set_default_passwd_cb_userdata(c->ctx, (void *)(&tcn_password_callback)); SSL_CTX_set_info_callback(c->ctx, SSL_callback_handshake); - + /* Cache Java side SNI callback if not already cached */ if (ssl_context_class == NULL) { ssl_context_class = (*e)->NewGlobalRef(e, o); @@ -591,6 +618,53 @@ static X509 *load_pem_cert(tcn_ssl_ctxt_ return cert; } +static int ssl_load_pkcs12(tcn_ssl_ctxt_t *c, const char *file, + EVP_PKEY **pkey, X509 **cert, STACK_OF(X509) **ca) +{ + const char *pass; + char buff[PEM_BUFSIZE]; + int len, rc = 0; + PKCS12 *p12; + BIO *in; + tcn_pass_cb_t *cb_data = c->cb_data; + + if ((in = BIO_new(BIO_s_file())) == 0) + return 0; + if (BIO_read_filename(in, file) <= 0) { + BIO_free(in); + return 0; + } + p12 = d2i_PKCS12_bio(in, 0); + if (p12 == 0) { + /* Error loading PKCS12 file */ + goto cleanup; + } + /* See if an empty password will do */ + if (PKCS12_verify_mac(p12, "", 0) || PKCS12_verify_mac(p12, 0, 0)) { + pass = ""; + } + else { + if (!cb_data) + cb_data = &tcn_password_callback; + len = SSL_password_callback(buff, PEM_BUFSIZE, 0, cb_data); + if (len < 0) { + /* Passpharse callback error */ + goto cleanup; + } + if (!PKCS12_verify_mac(p12, buff, len)) { + /* Mac verify error (wrong password?) in PKCS12 file */ + goto cleanup; + } + pass = buff; + } + rc = PKCS12_parse(p12, pass, pkey, cert, ca); +cleanup: + if (p12 != 0) + PKCS12_free(p12); + BIO_free(in); + return rc; +} + TCN_IMPLEMENT_CALL(void, SSLContext, setRandom)(TCN_STDARGS, jlong ctx, jstring file) { @@ -614,6 +688,7 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, TCN_ALLOC_CSTRING(key); TCN_ALLOC_CSTRING(password); const char *key_file, *cert_file; + const char *p; char err[256]; UNREFERENCED(o); @@ -634,24 +709,35 @@ TCN_IMPLEMENT_CALL(jboolean, SSLContext, cert_file = J2S(cert); if (!key_file) key_file = cert_file; - if (!key_file) { + if (!key_file || !cert_file) { tcn_Throw(e, "No Certificate file specified or invalid file format"); rv = JNI_FALSE; goto cleanup; } - if ((c->keys[idx] = load_pem_key(c, key_file)) == NULL) { - ERR_error_string(ERR_get_error(), err); - tcn_Throw(e, "Unable to load certificate key %s (%s)", - key_file, err); - rv = JNI_FALSE; - goto cleanup; + if ((p = strrchr(cert_file, '.')) != NULL && strcmp(p, ".pkcs12") == 0) { + if (!ssl_load_pkcs12(c, cert_file, &c->keys[idx], &c->certs[idx], 0)) { + ERR_error_string(ERR_get_error(), err); + tcn_Throw(e, "Unable to load certificate %s (%s)", + cert_file, err); + rv = JNI_FALSE; + goto cleanup; + } } - if ((c->certs[idx] = load_pem_cert(c, cert_file)) == NULL) { - ERR_error_string(ERR_get_error(), err); - tcn_Throw(e, "Unable to load certificate %s (%s)", - cert_file, err); - rv = JNI_FALSE; - goto cleanup; + else { + if ((c->keys[idx] = load_pem_key(c, key_file)) == NULL) { + ERR_error_string(ERR_get_error(), err); + tcn_Throw(e, "Unable to load certificate key %s (%s)", + key_file, err); + rv = JNI_FALSE; + goto cleanup; + } + if ((c->certs[idx] = load_pem_cert(c, cert_file)) == NULL) { + ERR_error_string(ERR_get_error(), err); + tcn_Throw(e, "Unable to load certificate %s (%s)", + cert_file, err); + rv = JNI_FALSE; + goto cleanup; + } } if (SSL_CTX_use_certificate(c->ctx, c->certs[idx]) <= 0) { ERR_error_string(ERR_get_error(), err); @@ -739,7 +825,7 @@ int cb_server_alpn(SSL *ssl, // Client specified an empty protocol list. Nothing to negotiate. return SSL_TLSEXT_ERR_ALERT_FATAL; } - + client_protos = apr_array_make(con->pool , 0, sizeof(char *)); for (i = 0; i < inlen; /**/) { unsigned int plen = in[i++]; @@ -756,12 +842,12 @@ int cb_server_alpn(SSL *ssl, // Server supported protocol names not set. return SSL_TLSEXT_ERR_ALERT_FATAL; } - + if (tcsslctx->alpnlen == 0) { // Server supported protocols is an empty list return SSL_TLSEXT_ERR_ALERT_FATAL; } - + proposed_protos = apr_array_make(con->pool, 0, sizeof(char *)); for (i = 0; i < tcsslctx->alpnlen; /**/) { unsigned int plen = tcsslctx->alpn[i++]; @@ -773,12 +859,12 @@ int cb_server_alpn(SSL *ssl, APR_ARRAY_PUSH(proposed_protos, char*) = apr_pstrndup(con->pool, (const char *)tcsslctx->alpn+i, plen); i += plen; } - + if (proposed_protos->nelts <= 0) { // Should never happen. The server did not specify any protocols. return SSL_TLSEXT_ERR_ALERT_FATAL; } - + /* Now select the most preferred protocol from the proposals. */ *out = APR_ARRAY_IDX(proposed_protos, 0, const unsigned char *); for (i = 1; i < proposed_protos->nelts; ++i) { @@ -794,7 +880,7 @@ int cb_server_alpn(SSL *ssl, // Agreed protocol name too long return SSL_TLSEXT_ERR_ALERT_FATAL; } - + *outlen = (unsigned char)len; return SSL_TLSEXT_ERR_OK; @@ -804,11 +890,11 @@ TCN_IMPLEMENT_CALL(jint, SSLContext, set jbyteArray buf, jint len) { tcn_ssl_ctxt_t *sslctx = J2P(ctx, tcn_ssl_ctxt_t *); - + sslctx->alpn = apr_pcalloc(sslctx->pool, len); (*e)->GetByteArrayRegion(e, buf, 0, len, &sslctx->alpn[0]); sslctx->alpnlen = len; - + if (sslctx->mode == SSL_MODE_SERVER) { SSL_CTX_set_alpn_select_cb(sslctx->ctx, cb_server_alpn, sslctx); } else { Modified: tomcat/native/trunk/native/src/sslnetwork.c URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/src/sslnetwork.c?rev=1681092&r1=1681091&r2=1681092&view=diff ============================================================================== --- tomcat/native/trunk/native/src/sslnetwork.c (original) +++ tomcat/native/trunk/native/src/sslnetwork.c Fri May 22 12:39:51 2015 @@ -693,15 +693,15 @@ TCN_IMPLEMENT_CALL(jint, SSLSocket, getA tcn_socket_t *s = J2P(sock, tcn_socket_t *); tcn_ssl_conn_t *tcssl = (tcn_ssl_conn_t *)s->opaque; int bufLen = (*e)->GetArrayLength(e, buf); - + SSL_get0_alpn_selected(tcssl->ssl, &alpn, &alpn_len); - + if (alpn_len == 0 || bufLen < alpn_len) { return 0; } int len = alpn_len; (*e)->SetByteArrayRegion(e, buf, 0, len, alpn); - + return len; } Modified: tomcat/native/trunk/native/src/sslutils.c URL: http://svn.apache.org/viewvc/tomcat/native/trunk/native/src/sslutils.c?rev=1681092&r1=1681091&r2=1681092&view=diff ============================================================================== --- tomcat/native/trunk/native/src/sslutils.c (original) +++ tomcat/native/trunk/native/src/sslutils.c Fri May 22 12:39:51 2015 @@ -21,16 +21,27 @@ */ #include "tcn.h" -#include "apr_thread_mutex.h" -#include "apr_poll.h" #ifdef HAVE_OPENSSL +#include "apr_poll.h" #include "ssl_private.h" #ifdef WIN32 extern int WIN32_SSL_password_prompt(tcn_pass_cb_t *data); #endif +#ifdef HAVE_OPENSSL_OCSP +#include <openssl/bio.h> +#include <openssl/ocsp.h> +/* defines with the values as seen by the asn1parse -dump openssl command */ +#define ASN1_SEQUENCE 0x30 +#define ASN1_OID 0x06 +#define ASN1_STRING 0x86 +#pragma message("Using OCSP") +static int ssl_verify_OCSP(int ok, X509_STORE_CTX *ctx); +static int ssl_ocsp_request(X509 *cert, X509 *issuer); +#endif + /* _________________________________________________________________ ** ** Additional High-Level Functions for OpenSSL @@ -243,6 +254,7 @@ static unsigned char dhxxx2_g[]={ static DH *get_dh(int idx) { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) DH *dh; if ((dh = DH_new()) == NULL) @@ -268,6 +280,9 @@ static DH *get_dh(int idx) } else return dh; +#else + return NULL; +#endif } DH *SSL_dh_get_tmp_param(int key_len) @@ -599,6 +614,8 @@ static int ssl_verify_CRL(int ok, X509_S * This OpenSSL callback function is called when OpenSSL * does client authentication and verifies the certificate chain. */ + + int SSL_callback_SSL_verify(int ok, X509_STORE_CTX *ctx) { /* Get Apache context back through OpenSSL context */ @@ -610,6 +627,7 @@ int SSL_callback_SSL_verify(int ok, X509 int errdepth = X509_STORE_CTX_get_error_depth(ctx); int verify = con->ctx->verify_mode; int depth = con->ctx->verify_depth; + int skip_crl = 0; if (verify == SSL_CVERIFY_UNSET || verify == SSL_CVERIFY_NONE) @@ -620,10 +638,39 @@ int SSL_callback_SSL_verify(int ok, X509 ok = 1; SSL_set_verify_result(ssl, X509_V_OK); } + +#ifdef HAVE_OPENSSL_OCSP + /* First perform OCSP validation if possible */ + if (ok) { + /* If there was an optional verification error, it's not + * possible to perform OCSP validation since the issuer may be + * missing/untrusted. Fail in that case. + */ + if (SSL_VERIFY_ERROR_IS_OPTIONAL(errnum)) { + X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION); + errnum = X509_V_ERR_APPLICATION_VERIFICATION; + ok = 0; + } + else { + int ocsp_response = ssl_verify_OCSP(ok, ctx); + if (ocsp_response == OCSP_STATUS_OK) { + skip_crl = 1; /* we know it is valid we skip crl evaluation */ + } + else if (ocsp_response == OCSP_STATUS_REVOKED) { + ok = 0 ; + errnum = X509_STORE_CTX_get_error(ctx); + } + else if (ocsp_response == OCSP_STATUS_UNKNOWN) { + /* TODO: do nothing for time being, continue with CRL */ + ; + } + } + } +#endif /* * Additionally perform CRL-based revocation checks */ - if (ok && con->ctx->crl) { + if (ok && con->ctx->crl && !skip_crl) { if (!(ok = ssl_verify_CRL(ok, ctx, con))) { errnum = X509_STORE_CTX_get_error(ctx); /* TODO: Log something */ @@ -673,7 +720,10 @@ void SSL_callback_handshake(const SSL *s int state = SSL_get_state(ssl); if (state == SSL3_ST_SR_CLNT_HELLO_A - || state == SSL23_ST_SR_CLNT_HELLO_A) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + || state == SSL23_ST_SR_CLNT_HELLO_A +#endif + ) { con->reneg_state = RENEG_ABORT; /* XXX: rejecting client initiated renegotiation */ @@ -687,4 +737,512 @@ void SSL_callback_handshake(const SSL *s } -#endif +#ifdef HAVE_OPENSSL_OCSP + +/* Function that is used to do the OCSP verification */ +static int ssl_verify_OCSP(int ok, X509_STORE_CTX *ctx) +{ + X509 *cert, *issuer; + int r = OCSP_STATUS_UNKNOWN; + + cert = X509_STORE_CTX_get_current_cert(ctx); + /* if we can't get the issuer, we cannot perform OCSP verification */ + if (X509_STORE_CTX_get1_issuer(&issuer, ctx, cert) == 1 ) { + r = ssl_ocsp_request(cert, issuer); + if (r == OCSP_STATUS_REVOKED) { + /* we set the error if we know that it is revoked */ + X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); + } + else { + /* else we return unknown, so that we can continue with the crl */ + r = OCSP_STATUS_UNKNOWN; + } + X509_free(issuer); /* It appears that we should free issuer since + * X509_STORE_CTX_get1_issuer() calls X509_OBJECT_up_ref_count() + * on the issuer object (unline X509_STORE_CTX_get_current_cert() + * that just returns the pointer + */ + } + return r; +} + + +/* Helps with error handling or realloc */ +static void *apr_xrealloc(void *buf, size_t oldlen, size_t len, apr_pool_t *p) +{ + void *newp = apr_palloc(p, len); + + if(newp) + memcpy(newp, buf, oldlen); + return newp; +} + +/* parses the ocsp url and updates the ocsp_urls and nocsp_urls variables + returns 0 on success, 1 on failure */ +static int parse_ocsp_url(unsigned char *asn1, char ***ocsp_urls, + int *nocsp_urls, apr_pool_t *p) +{ + char **new_ocsp_urls, *ocsp_url; + int len, err = 0, new_nocsp_urls; + + if (*asn1 == ASN1_STRING) { + len = *++asn1; + asn1++; + new_nocsp_urls = *nocsp_urls+1; + if ((new_ocsp_urls = apr_xrealloc(*ocsp_urls,*nocsp_urls, new_nocsp_urls, p)) == NULL) + err = 1; + if (!err) { + *ocsp_urls = new_ocsp_urls; + *nocsp_urls = new_nocsp_urls; + *(*ocsp_urls + *nocsp_urls) = NULL; + if ((ocsp_url = apr_palloc(p, len + 1)) == NULL) { + err = 1; + } + else { + memcpy(ocsp_url, asn1, len); + ocsp_url[len] = '\0'; + *(*ocsp_urls + *nocsp_urls - 1) = ocsp_url; + } + } + } + return err; + +} + +/* parses the ANS1 OID and if it is an OCSP OID then calls the parse_ocsp_url function */ +static int parse_ASN1_OID(unsigned char *asn1, char ***ocsp_urls, int *nocsp_urls, apr_pool_t *p) +{ + int len, err = 0 ; + const unsigned char OCSP_OID[] = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01}; + + len = *++asn1; + asn1++; + if (memcmp(asn1, OCSP_OID, len) == 0) { + asn1+=len; + err = parse_ocsp_url(asn1, ocsp_urls, nocsp_urls, p); + } + return err; +} + + +/* Parses an ASN1 Sequence. It is a recursive function, since if it finds a sequence + within the sequence it calls recursively itself. This function stops when it finds + the end of the ASN1 sequence (marked by '\0'), so if there are other sequences within + the same sequence the while loop parses the sequences */ + +/* This algo was developed with AIA in mind so it was tested only with this extension */ +static int parse_ASN1_Sequence(unsigned char *asn1, char ***ocsp_urls, + int *nocsp_urls, apr_pool_t *p) +{ + int len = 0 , err = 0; + + while (!err && *asn1 != '\0') { + switch(*asn1) { + case ASN1_SEQUENCE: + len = *++asn1; + asn1++; + err = parse_ASN1_Sequence(asn1, ocsp_urls, nocsp_urls, p); + break; + case ASN1_OID: + err = parse_ASN1_OID(asn1,ocsp_urls,nocsp_urls, p); + return 0; + break; + default: + err = 1; /* we shouldn't have any errors */ + break; + } + asn1+=len; + } + return err; +} + +/* the main function that gets the ASN1 encoding string and returns + a pointer to a NULL terminated "array" of char *, that contains + the ocsp_urls */ +static char **decode_OCSP_url(ASN1_OCTET_STRING *os, apr_pool_t *p) +{ + char **response = NULL; + unsigned char *ocsp_urls; + int len, numofresponses = 0 ; + + len = ASN1_STRING_length(os); + + ocsp_urls = apr_palloc(p, len + 1); + memcpy(ocsp_urls,os->data, len); + ocsp_urls[len] = '\0'; + + if ((response = apr_pcalloc(p, sizeof(char *))) == NULL) + return NULL; + if (parse_ASN1_Sequence(ocsp_urls, &response, &numofresponses, p)) + response = NULL; + return response; +} + + +/* stolen from openssl ocsp command */ +static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, X509 *issuer, + STACK_OF(OCSP_CERTID) *ids) +{ + OCSP_CERTID *id; + + if (!issuer) + return 0; + if (!*req) + *req = OCSP_REQUEST_new(); + if (!*req) + return 0; + id = OCSP_cert_to_id(NULL, cert, issuer); + if (!id || !sk_OCSP_CERTID_push(ids, id)) + return 0; + if (!OCSP_request_add0_id(*req, id)) + return 0; + else + return 1; +} + + +/* Creates the APR socket and connect to the hostname. Returns the + socket or NULL if there is an error. +*/ +static apr_socket_t *make_socket(char *hostname, int port, apr_pool_t *mp) +{ + apr_sockaddr_t *sa_in; + apr_status_t status; + apr_socket_t *sock = NULL; + + + status = apr_sockaddr_info_get(&sa_in, hostname, APR_INET, port, 0, mp); + + if (status == APR_SUCCESS) + status = apr_socket_create(&sock, sa_in->family, SOCK_STREAM, APR_PROTO_TCP, mp); + if (status == APR_SUCCESS) + status = apr_socket_connect(sock, sa_in); + + if (status == APR_SUCCESS) + return sock; + return NULL; +} + + +/* Creates the request in a memory BIO in order to send it to the OCSP server. + Most parts of this function are taken from mod_ssl support for OCSP (with some + minor modifications +*/ +static BIO *serialize_request(OCSP_REQUEST *req, char *host, int port, char *path) +{ + BIO *bio; + int len; + + len = i2d_OCSP_REQUEST(req, NULL); + + bio = BIO_new(BIO_s_mem()); + + BIO_printf(bio, "POST %s HTTP/1.0\r\n" + "Host: %s:%d\r\n" + "Content-Type: application/ocsp-request\r\n" + "Content-Length: %d\r\n" + "\r\n", + path, host, port, len); + + if (i2d_OCSP_REQUEST_bio(bio, req) != 1) { + BIO_free(bio); + return NULL; + } + + return bio; +} + + +/* Send the OCSP request to the OCSP server. Taken from mod_ssl OCSP support */ +static int ocsp_send_req(apr_socket_t *sock, BIO *req) +{ + int len; + char buf[TCN_BUFFER_SZ]; + apr_status_t rv; + int ok = 1; + + while ((len = BIO_read(req, buf, sizeof buf)) > 0) { + char *wbuf = buf; + apr_size_t remain = len; + + do { + apr_size_t wlen = remain; + rv = apr_socket_send(sock, wbuf, &wlen); + wbuf += remain; + remain -= wlen; + } while (rv == APR_SUCCESS && remain > 0); + + if (rv != APR_SUCCESS) { + apr_socket_close(sock); + ok = 0; + } + } + + return ok; +} + + + +/* Parses the buffer from the response and extracts the OCSP response. + Taken from openssl library */ +static OCSP_RESPONSE *parse_ocsp_resp(char *buf, int len) +{ + BIO *mem = NULL; + char tmpbuf[1024]; + OCSP_RESPONSE *resp = NULL; + char *p, *q, *r; + int retcode; + + mem = BIO_new(BIO_s_mem()); + if(mem == NULL) + return NULL; + + BIO_write(mem, buf, len); /* write the buffer to the bio */ + if (BIO_gets(mem, tmpbuf, 512) <= 0) { + OCSPerr(OCSP_F_OCSP_SENDREQ_BIO,OCSP_R_SERVER_RESPONSE_PARSE_ERROR); + goto err; + } + /* Parse the HTTP response. This will look like this: + * "HTTP/1.0 200 OK". We need to obtain the numeric code and + * (optional) informational message. + */ + + /* Skip to first white space (passed protocol info) */ + for (p = tmpbuf; *p && !apr_isspace(*p); p++) + continue; + if (!*p) { + goto err; + } + /* Skip past white space to start of response code */ + while (apr_isspace(*p)) + p++; + if (!*p) { + goto err; + } + /* Find end of response code: first whitespace after start of code */ + for (q = p; *q && !apr_isspace(*q); q++) + continue; + if (!*q) { + goto err; + } + /* Set end of response code and start of message */ + *q++ = 0; + /* Attempt to parse numeric code */ + retcode = strtoul(p, &r, 10); + if (*r) + goto err; + /* Skip over any leading white space in message */ + while (apr_isspace(*q)) + q++; + if (*q) { + /* Finally zap any trailing white space in message (include CRLF) */ + /* We know q has a non white space character so this is OK */ + for(r = q + strlen(q) - 1; apr_isspace(*r); r--) *r = 0; + } + if (retcode != 200) { + goto err; + } + /* Find blank line marking beginning of content */ + while (BIO_gets(mem, tmpbuf, 512) > 0) { + for (p = tmpbuf; apr_isspace(*p); p++) + continue; + if (!*p) + break; + } + if (*p) { + goto err; + } + if (!(resp = d2i_OCSP_RESPONSE_bio(mem, NULL))) { + goto err; + } +err: + BIO_free(mem); + return resp; +} + + +/* Reads the respnse from the APR socket to a buffer, and parses the buffer to + return the OCSP response */ +#define ADDLEN 512 +static OCSP_RESPONSE *ocsp_get_resp(apr_socket_t *sock) +{ + int buflen; + apr_size_t totalread = 0; + apr_size_t readlen; + char *buf, tmpbuf[ADDLEN]; + apr_status_t rv = APR_SUCCESS; + apr_pool_t *p; + OCSP_RESPONSE *resp; + + apr_pool_create(&p, NULL); + buflen = ADDLEN; + buf = apr_palloc(p, buflen); + if (buf == NULL) { + apr_pool_destroy(p); + return NULL; + } + + while (rv == APR_SUCCESS ) { + readlen = sizeof(tmpbuf); + rv = apr_socket_recv(sock, tmpbuf, &readlen); + if (rv == APR_SUCCESS) { /* if we have read something .. we can put it in the buffer*/ + if ((totalread + readlen) >= buflen) { + buf = apr_xrealloc(buf, buflen, buflen + ADDLEN, p); + if (buf == NULL) { + apr_pool_destroy(p); + return NULL; + } + buflen += ADDLEN; /* if needed we enlarge the buffer */ + } + memcpy(buf + totalread, tmpbuf, readlen); /* the copy to the buffer */ + totalread += readlen; /* update the total bytes read */ + } + else { + if (rv == APR_EOF && readlen == 0) + ; /* EOF, normal situation */ + else if (readlen == 0) { + /* Not success, and readlen == 0 .. some error */ + apr_pool_destroy(p); + return NULL; + } + } + } + + resp = parse_ocsp_resp(buf, buflen); + apr_pool_destroy(p); + return resp; +} + +/* Creates and OCSP request and returns the OCSP_RESPONSE */ +static OCSP_RESPONSE *get_ocsp_response(X509 *cert, X509 *issuer, char *url) +{ + OCSP_RESPONSE *ocsp_resp = NULL; + OCSP_REQUEST *ocsp_req = NULL; + BIO *bio_req; + char *hostname, *path, *c_port; + int port, use_ssl; + STACK_OF(OCSP_CERTID) *ids = NULL; + int ok = 0; + apr_socket_t *apr_sock = NULL; + apr_pool_t *mp; + + apr_pool_create(&mp, NULL); + ids = sk_OCSP_CERTID_new_null(); + + /* problem parsing the URL */ + if (OCSP_parse_url(url,&hostname, &c_port, &path, &use_ssl) == 0 ) { + sk_OCSP_CERTID_free(ids); + return NULL; + } + + /* Create the OCSP request */ + if (sscanf(c_port, "%d", &port) != 1) + goto end; + ocsp_req = OCSP_REQUEST_new(); + if (ocsp_req == NULL) + return NULL; + if (add_ocsp_cert(&ocsp_req,cert,issuer,ids) == 0 ) + goto free_req; + + /* create the BIO with the request to send */ + bio_req = serialize_request(ocsp_req, hostname, port, path); + if (bio_req == NULL) { + goto free_req; + } + + apr_sock = make_socket(hostname, port, mp); + if (apr_sock == NULL) { + ocsp_resp = NULL; + goto free_bio; + } + + ok = ocsp_send_req(apr_sock, bio_req); + if (ok) + ocsp_resp = ocsp_get_resp(apr_sock); + +free_bio: + BIO_free(bio_req); + +free_req: + if(apr_sock && ok) /* if ok == 0 we have already closed the socket */ + apr_socket_close(apr_sock); + + apr_pool_destroy(mp); + + sk_OCSP_CERTID_free(ids); + OCSP_REQUEST_free(ocsp_req); + +end: + return ocsp_resp; +} + +/* Process the OCSP_RESPONSE and returns the corresponding + answert according to the status. +*/ +static int process_ocsp_response(OCSP_RESPONSE *ocsp_resp) +{ + int r, o = V_OCSP_CERTSTATUS_UNKNOWN, i; + OCSP_BASICRESP *bs; + OCSP_SINGLERESP *ss; + + r = OCSP_response_status(ocsp_resp); + + if (r != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + OCSP_RESPONSE_free(ocsp_resp); + return OCSP_STATUS_UNKNOWN; + } + bs = OCSP_response_get1_basic(ocsp_resp); + + ss = OCSP_resp_get0(bs,0); /* we know we have only 1 request */ + + i = OCSP_single_get0_status(ss, NULL, NULL, NULL, NULL); + if (i == V_OCSP_CERTSTATUS_GOOD) + o = OCSP_STATUS_OK; + else if (i == V_OCSP_CERTSTATUS_REVOKED) + o = OCSP_STATUS_REVOKED; + else if (i == V_OCSP_CERTSTATUS_UNKNOWN) + o = OCSP_STATUS_UNKNOWN; + + /* we clean up */ + OCSP_RESPONSE_free(ocsp_resp); + return o; +} + +static int ssl_ocsp_request(X509 *cert, X509 *issuer) +{ + char **ocsp_urls = NULL; + int nid; + X509_EXTENSION *ext; + ASN1_OCTET_STRING *os; + apr_pool_t *p; + + apr_pool_create(&p, NULL); + + /* Get the proper extension */ + nid = X509_get_ext_by_NID(cert,NID_info_access,-1); + if (nid >= 0 ) { + ext = X509_get_ext(cert,nid); + os = X509_EXTENSION_get_data(ext); + + ocsp_urls = decode_OCSP_url(os, p); + } + + /* if we find the extensions and we can parse it check + the ocsp status. Otherwise, return OCSP_STATUS_UNKNOWN */ + if (ocsp_urls != NULL) { + OCSP_RESPONSE *resp; + /* for the time being just check for the fist response .. a better + approach is to iterate for all the possible ocsp urls */ + resp = get_ocsp_response(cert, issuer, ocsp_urls[0]); + + if (resp != NULL) { + apr_pool_destroy(p); + return process_ocsp_response(resp); + } + } + apr_pool_destroy(p); + return OCSP_STATUS_UNKNOWN; +} + +#endif /* HAS_OCSP_ENABLED */ +#endif /* HAVE_OPENSSL */ --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org