Index: c/sslutils.c |
diff --git a/c/sslutils.c b/c/sslutils.c |
index f49f0bdd7f4dc1d352b570d537775e3e0c6209af..a3801040c3b6f9299c2246fdaadb89ba3dd2eaf3 100644 |
--- a/c/sslutils.c |
+++ b/c/sslutils.c |
@@ -1,3 +1,18 @@ |
+/* |
+ * Copyright 2016 The Netty Project |
+ * |
+ * The Netty Project licenses this file to you under the Apache License, |
+ * version 2.0 (the "License"); you may not use this file except in compliance |
+ * with the License. You may obtain a copy of the License at: |
+ * |
+ * http://www.apache.org/licenses/LICENSE-2.0 |
+ * |
+ * Unless required by applicable law or agreed to in writing, software |
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
+ * License for the specific language governing permissions and limitations |
+ * under the License. |
+ */ |
/* Licensed to the Apache Software Foundation (ASF) under one or more |
* contributor license agreements. See the NOTICE file distributed with |
* this work for additional information regarding copyright ownership. |
@@ -14,40 +29,104 @@ |
* limitations under the License. |
*/ |
-/** SSL Utilities |
- * |
- * @author Mladen Turk |
- * @version $Id: sslutils.c 1658728 2015-02-10 14:45:19Z kkolinko $ |
- */ |
- |
#include "tcn.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 |
** _________________________________________________________________ |
*/ |
+ |
+/* |
+ * Adapted from OpenSSL: |
+ * http://osxr.org/openssl/source/ssl/ssl_locl.h#0291 |
+ */ |
+/* Bits for algorithm_mkey (key exchange algorithm) */ |
+#define SSL_kRSA 0x00000001L /* RSA key exchange */ |
+#define SSL_kDHr 0x00000002L /* DH cert, RSA CA cert */ /* no such ciphersuites supported! */ |
+#define SSL_kDHd 0x00000004L /* DH cert, DSA CA cert */ /* no such ciphersuite supported! */ |
+#define SSL_kEDH 0x00000008L /* tmp DH key no DH cert */ |
+#define SSL_kKRB5 0x00000010L /* Kerberos5 key exchange */ |
+#define SSL_kECDHr 0x00000020L /* ECDH cert, RSA CA cert */ |
+#define SSL_kECDHe 0x00000040L /* ECDH cert, ECDSA CA cert */ |
+#define SSL_kEECDH 0x00000080L /* ephemeral ECDH */ |
+#define SSL_kPSK 0x00000100L /* PSK */ |
+#define SSL_kGOST 0x00000200L /* GOST key exchange */ |
+#define SSL_kSRP 0x00000400L /* SRP */ |
+ |
+/* Bits for algorithm_auth (server authentication) */ |
+#define SSL_aRSA 0x00000001L /* RSA auth */ |
+#define SSL_aDSS 0x00000002L /* DSS auth */ |
+#define SSL_aNULL 0x00000004L /* no auth (i.e. use ADH or AECDH) */ |
+#define SSL_aDH 0x00000008L /* Fixed DH auth (kDHd or kDHr) */ /* no such ciphersuites supported! */ |
+#define SSL_aECDH 0x00000010L /* Fixed ECDH auth (kECDHe or kECDHr) */ |
+#define SSL_aKRB5 0x00000020L /* KRB5 auth */ |
+#define SSL_aECDSA 0x00000040L /* ECDSA auth*/ |
+#define SSL_aPSK 0x00000080L /* PSK auth */ |
+#define SSL_aGOST94 0x00000100L /* GOST R 34.10-94 signature auth */ |
+#define SSL_aGOST01 0x00000200L /* GOST R 34.10-2001 signature auth */ |
+ |
+const char* TCN_UNKNOWN_AUTH_METHOD = "UNKNOWN"; |
+ |
+/* OpenSSL end */ |
+ |
+/* |
+ * Adapted from Android: |
+ * https://android.googlesource.com/platform/external/openssl/+/master/patches/0003-jsse.patch |
+ */ |
+const char* SSL_cipher_authentication_method(const SSL_CIPHER* cipher){ |
+#ifndef OPENSSL_IS_BORINGSSL |
+ switch (cipher->algorithm_mkey) |
+ { |
+ case SSL_kRSA: |
+ return SSL_TXT_RSA; |
+ case SSL_kDHr: |
+ return SSL_TXT_DH "_" SSL_TXT_RSA; |
+ |
+ case SSL_kDHd: |
+ return SSL_TXT_DH "_" SSL_TXT_DSS; |
+ case SSL_kEDH: |
+ switch (cipher->algorithm_auth) |
+ { |
+ case SSL_aDSS: |
+ return "DHE_" SSL_TXT_DSS; |
+ case SSL_aRSA: |
+ return "DHE_" SSL_TXT_RSA; |
+ case SSL_aNULL: |
+ return SSL_TXT_DH "_anon"; |
+ default: |
+ return TCN_UNKNOWN_AUTH_METHOD; |
+ } |
+ case SSL_kKRB5: |
+ return SSL_TXT_KRB5; |
+ case SSL_kECDHr: |
+ return SSL_TXT_ECDH "_" SSL_TXT_RSA; |
+ case SSL_kECDHe: |
+ return SSL_TXT_ECDH "_" SSL_TXT_ECDSA; |
+ case SSL_kEECDH: |
+ switch (cipher->algorithm_auth) |
+ { |
+ case SSL_aECDSA: |
+ return "ECDHE_" SSL_TXT_ECDSA; |
+ case SSL_aRSA: |
+ return "ECDHE_" SSL_TXT_RSA; |
+ case SSL_aNULL: |
+ return SSL_TXT_ECDH "_anon"; |
+ default: |
+ return TCN_UNKNOWN_AUTH_METHOD; |
+ } |
+ default: |
+ return TCN_UNKNOWN_AUTH_METHOD; |
+ } |
+#else |
+ return SSL_CIPHER_get_kx_name(cipher); |
+#endif |
+ |
+} |
+ |
/* we initialize this index at startup time |
* and never write to it at request time, |
* so this static is thread safe. |
@@ -56,30 +135,25 @@ static int ssl_ocsp_request(X509 *cert, X509 *issuer); |
*/ |
static int SSL_app_data2_idx = -1; |
static int SSL_app_data3_idx = -1; |
-void SSL_init_app_data2_3_idx(void) |
+static int SSL_app_data4_idx = -1; |
+void SSL_init_app_data_idx() |
{ |
int i; |
- if (SSL_app_data2_idx > -1) { |
- return; |
+ if (SSL_app_data2_idx == -1) { |
+ /* we _do_ need to call this two times */ |
+ for (i = 0; i <= 1; i++) { |
+ SSL_app_data2_idx = SSL_get_ex_new_index(0, "tcn_ssl_ctxt_t*", NULL, NULL, NULL); |
+ } |
} |
- /* we _do_ need to call this two times */ |
- for (i = 0; i <= 1; i++) { |
- SSL_app_data2_idx = |
- SSL_get_ex_new_index(0, |
- "Second Application Data for SSL", |
- NULL, NULL, NULL); |
+ if (SSL_app_data3_idx == -1) { |
+ SSL_app_data3_idx = SSL_get_ex_new_index(0, "int* handshakeCount", NULL, NULL, NULL); |
} |
- if (SSL_app_data3_idx > -1) { |
- return; |
+ if (SSL_app_data4_idx == -1) { |
+ SSL_app_data4_idx = SSL_get_ex_new_index(0, "tcn_ssl_verify_config_t*", NULL, NULL, NULL); |
} |
- |
- SSL_app_data3_idx = |
- SSL_get_ex_new_index(0, |
- "Third Application Data for SSL", |
- NULL, NULL, NULL); |
} |
void *SSL_get_app_data2(SSL *ssl) |
@@ -103,80 +177,36 @@ void SSL_set_app_data3(SSL *ssl, void *arg) |
SSL_set_ex_data(ssl, SSL_app_data3_idx, arg); |
} |
-/* Simple echo password prompting */ |
-int SSL_password_prompt(tcn_pass_cb_t *data) |
+void *SSL_get_app_data4(SSL *ssl) |
{ |
- int rv = 0; |
- data->password[0] = '\0'; |
- if (data->cb.obj) { |
- JNIEnv *e; |
- jobject o; |
- jstring prompt; |
- tcn_get_java_env(&e); |
- prompt = AJP_TO_JSTRING(data->prompt); |
- if ((o = (*e)->CallObjectMethod(e, data->cb.obj, |
- data->cb.mid[0], prompt))) { |
- TCN_ALLOC_CSTRING(o); |
- if (J2S(o)) { |
- strncpy(data->password, J2S(o), SSL_MAX_PASSWORD_LEN); |
- data->password[SSL_MAX_PASSWORD_LEN-1] = '\0'; |
- rv = (int)strlen(data->password); |
- } |
- TCN_FREE_CSTRING(o); |
- } |
- } |
- else { |
-#ifdef WIN32 |
- rv = WIN32_SSL_password_prompt(data); |
-#elif !defined(OPENSSL_IS_BORINGSSL) |
- EVP_read_pw_string(data->password, SSL_MAX_PASSWORD_LEN, |
- data->prompt, 0); |
-#endif |
- rv = (int)strlen(data->password); |
- } |
- if (rv > 0) { |
- /* Remove LF char if present */ |
- char *r = strchr(data->password, '\n'); |
- if (r) { |
- *r = '\0'; |
- rv--; |
- } |
-#ifdef WIN32 |
- if ((r = strchr(data->password, '\r'))) { |
- *r = '\0'; |
- rv--; |
- } |
-#endif |
- } |
- return rv; |
+ return SSL_get_ex_data(ssl, SSL_app_data4_idx); |
+} |
+ |
+void SSL_set_app_data4(SSL *ssl, void *arg) |
+{ |
+ SSL_set_ex_data(ssl, SSL_app_data4_idx, arg); |
} |
int SSL_password_callback(char *buf, int bufsiz, int verify, |
void *cb) |
{ |
- tcn_pass_cb_t *cb_data = (tcn_pass_cb_t *)cb; |
+ char *password = (char *) cb; |
- if (buf == NULL) |
+ if (buf == NULL || password == NULL) |
return 0; |
*buf = '\0'; |
- if (cb_data == NULL) |
- cb_data = &tcn_password_callback; |
- if (!cb_data->prompt) |
- cb_data->prompt = SSL_DEFAULT_PASS_PROMPT; |
- if (cb_data->password[0]) { |
+ |
+ if (password[0]) { |
/* Return already obtained password */ |
- strncpy(buf, cb_data->password, bufsiz); |
- buf[bufsiz - 1] = '\0'; |
- return (int)strlen(buf); |
- } |
- else { |
- if (SSL_password_prompt(cb_data) > 0) |
- strncpy(buf, cb_data->password, bufsiz); |
+ strncpy(buf, password, bufsiz); |
} |
+ |
buf[bufsiz - 1] = '\0'; |
return (int)strlen(buf); |
} |
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(OPENSSL_USE_DEPRECATED) || defined(LIBRESSL_VERSION_NUMBER) |
+ |
static unsigned char dh0512_p[]={ |
0xD9,0xBA,0xBF,0xFD,0x69,0x38,0xC9,0x51,0x2D,0x19,0x37,0x39, |
0xD7,0x7D,0x7E,0x3E,0x25,0x58,0x55,0x94,0x90,0x60,0x93,0x7A, |
@@ -273,9 +303,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) |
return NULL; |
switch (idx) { |
@@ -299,10 +327,14 @@ static DH *get_dh(int idx) |
} |
else |
return dh; |
+ return NULL; |
+} |
#else |
+static DH *get_dh(int idx) |
+{ |
return NULL; |
-#endif |
} |
+#endif |
DH *SSL_dh_get_tmp_param(int key_len) |
{ |
@@ -321,88 +353,6 @@ DH *SSL_dh_get_tmp_param(int key_len) |
return dh; |
} |
-DH *SSL_dh_get_param_from_file(const char *file) |
-{ |
- DH *dh = NULL; |
- BIO *bio; |
- |
- if ((bio = BIO_new_file(file, "r")) == NULL) |
- return NULL; |
- dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); |
- BIO_free(bio); |
- return dh; |
-} |
- |
-/* |
- * Handle out temporary RSA private keys on demand |
- * |
- * The background of this as the TLSv1 standard explains it: |
- * |
- * | D.1. Temporary RSA keys |
- * | |
- * | US Export restrictions limit RSA keys used for encryption to 512 |
- * | bits, but do not place any limit on lengths of RSA keys used for |
- * | signing operations. Certificates often need to be larger than 512 |
- * | bits, since 512-bit RSA keys are not secure enough for high-value |
- * | transactions or for applications requiring long-term security. Some |
- * | certificates are also designated signing-only, in which case they |
- * | cannot be used for key exchange. |
- * | |
- * | When the public key in the certificate cannot be used for encryption, |
- * | the server signs a temporary RSA key, which is then exchanged. In |
- * | exportable applications, the temporary RSA key should be the maximum |
- * | allowable length (i.e., 512 bits). Because 512-bit RSA keys are |
- * | relatively insecure, they should be changed often. For typical |
- * | electronic commerce applications, it is suggested that keys be |
- * | changed daily or every 500 transactions, and more often if possible. |
- * | Note that while it is acceptable to use the same temporary key for |
- * | multiple transactions, it must be signed each time it is used. |
- * | |
- * | RSA key generation is a time-consuming process. In many cases, a |
- * | low-priority process can be assigned the task of key generation. |
- * | Whenever a new key is completed, the existing temporary key can be |
- * | replaced with the new one. |
- * |
- * XXX: base on comment above, if thread support is enabled, |
- * we should spawn a low-priority thread to generate new keys |
- * on the fly. |
- * |
- * So we generated 512 and 1024 bit temporary keys on startup |
- * which we now just hand out on demand.... |
- */ |
- |
-RSA *SSL_callback_tmp_RSA(SSL *ssl, int export, int keylen) |
-{ |
- int idx; |
- |
- /* doesn't matter if export flag is on, |
- * we won't be asked for keylen > 512 in that case. |
- * if we are asked for a keylen > 1024, it is too expensive |
- * to generate on the fly. |
- */ |
- |
- switch (keylen) { |
- case 512: |
- idx = SSL_TMP_KEY_RSA_512; |
- break; |
- case 2048: |
- idx = SSL_TMP_KEY_RSA_2048; |
- if (SSL_temp_keys[idx] == NULL) |
- idx = SSL_TMP_KEY_RSA_1024; |
- break; |
- case 4096: |
- idx = SSL_TMP_KEY_RSA_4096; |
- if (SSL_temp_keys[idx] == NULL) |
- idx = SSL_TMP_KEY_RSA_2048; |
- break; |
- case 1024: |
- default: |
- idx = SSL_TMP_KEY_RSA_1024; |
- break; |
- } |
- return (RSA *)SSL_temp_keys[idx]; |
-} |
- |
/* |
* Hand out the already generated DH parameters... |
*/ |
@@ -427,13 +377,32 @@ DH *SSL_callback_tmp_DH(SSL *ssl, int export, int keylen) |
return (DH *)SSL_temp_keys[idx]; |
} |
+DH *SSL_callback_tmp_DH_512(SSL *ssl, int export, int keylen) |
+{ |
+ return (DH *)SSL_temp_keys[SSL_TMP_KEY_DH_512]; |
+} |
+ |
+DH *SSL_callback_tmp_DH_1024(SSL *ssl, int export, int keylen) |
+{ |
+ return (DH *)SSL_temp_keys[SSL_TMP_KEY_DH_1024]; |
+} |
+ |
+DH *SSL_callback_tmp_DH_2048(SSL *ssl, int export, int keylen) |
+{ |
+ return (DH *)SSL_temp_keys[SSL_TMP_KEY_DH_2048]; |
+} |
+ |
+DH *SSL_callback_tmp_DH_4096(SSL *ssl, int export, int keylen) |
+{ |
+ return (DH *)SSL_temp_keys[SSL_TMP_KEY_DH_4096]; |
+} |
+ |
/* |
* Read a file that optionally contains the server certificate in PEM |
* format, possibly followed by a sequence of CA certificates that |
* should be sent to the peer in the SSL Certificate message. |
*/ |
-int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, const char *file, |
- int skipfirst) |
+int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, const char *file, bool skipfirst) |
{ |
BIO *bio; |
int n; |
@@ -449,8 +418,7 @@ int SSL_CTX_use_certificate_chain(SSL_CTX *ctx, const char *file, |
return n; |
} |
-int SSL_CTX_use_certificate_chain_bio(SSL_CTX *ctx, BIO *bio, |
- int skipfirst) |
+static int SSL_CTX_setup_certs(SSL_CTX *ctx, BIO *bio, bool skipfirst, bool ca) |
{ |
X509 *x509; |
unsigned long err; |
@@ -464,307 +432,169 @@ int SSL_CTX_use_certificate_chain_bio(SSL_CTX *ctx, BIO *bio, |
X509_free(x509); |
} |
- /* free a perhaps already configured extra chain */ |
- SSL_CTX_clear_extra_chain_certs(ctx); |
- |
- /* create new extra chain by loading the certs */ |
n = 0; |
- while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
- if (SSL_CTX_add_extra_chain_cert(ctx, x509) != 1) { |
+ if (ca) { |
+ while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
+ if (SSL_CTX_add_client_CA(ctx, x509) != 1) { |
+ X509_free(x509); |
+ return -1; |
+ } |
+ // SSL_CTX_add_client_CA does not take ownership of the x509. It just calls X509_get_subject_name |
+ // and make a duplicate of this value. So we should always free the x509 after this call. |
+ // See https://github.com/netty/netty/issues/6249. |
X509_free(x509); |
- return -1; |
+ n++; |
+ } |
+ } else { |
+ /* free a perhaps already configured extra chain */ |
+ SSL_CTX_clear_extra_chain_certs(ctx); |
+ |
+ /* create new extra chain by loading the certs */ |
+ while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
+ // SSL_CTX_add_extra_chain_cert transfers ownership of the x509 certificate if the method succeeds. |
+ if (SSL_CTX_add_extra_chain_cert(ctx, x509) != 1) { |
+ X509_free(x509); |
+ return -1; |
+ } |
+ n++; |
} |
- n++; |
} |
+ |
/* Make sure that only the error is just an EOF */ |
if ((err = ERR_peek_error()) > 0) { |
if (!( ERR_GET_LIB(err) == ERR_LIB_PEM |
&& ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { |
return -1; |
} |
- while (ERR_get_error() > 0) ; |
+ ERR_clear_error(); |
} |
return n; |
} |
-static int ssl_X509_STORE_lookup(X509_STORE *store, int yype, |
- X509_NAME *name, X509_OBJECT *obj) |
+int SSL_CTX_use_certificate_chain_bio(SSL_CTX *ctx, BIO *bio, bool skipfirst) |
{ |
- X509_STORE_CTX ctx; |
- int rc; |
- |
- X509_STORE_CTX_init(&ctx, store, NULL, NULL); |
- rc = X509_STORE_get_by_subject(&ctx, yype, name, obj); |
- X509_STORE_CTX_cleanup(&ctx); |
- return rc; |
+ return SSL_CTX_setup_certs(ctx, bio, skipfirst, false); |
} |
-static int ssl_verify_CRL(int ok, X509_STORE_CTX *ctx, tcn_ssl_ctxt_t *c) |
-{ |
- X509_OBJECT obj; |
- X509_NAME *subject, *issuer; |
- X509 *cert; |
- X509_CRL *crl; |
- EVP_PKEY *pubkey; |
- int i, n, rc; |
- |
- /* |
- * Determine certificate ingredients in advance |
- */ |
- cert = X509_STORE_CTX_get_current_cert(ctx); |
- subject = X509_get_subject_name(cert); |
- issuer = X509_get_issuer_name(cert); |
- |
- /* |
- * OpenSSL provides the general mechanism to deal with CRLs but does not |
- * use them automatically when verifying certificates, so we do it |
- * explicitly here. We will check the CRL for the currently checked |
- * certificate, if there is such a CRL in the store. |
- * |
- * We come through this procedure for each certificate in the certificate |
- * chain, starting with the root-CA's certificate. At each step we've to |
- * both verify the signature on the CRL (to make sure it's a valid CRL) |
- * and it's revocation list (to make sure the current certificate isn't |
- * revoked). But because to check the signature on the CRL we need the |
- * public key of the issuing CA certificate (which was already processed |
- * one round before), we've a little problem. But we can both solve it and |
- * at the same time optimize the processing by using the following |
- * verification scheme (idea and code snippets borrowed from the GLOBUS |
- * project): |
- * |
- * 1. We'll check the signature of a CRL in each step when we find a CRL |
- * through the _subject_ name of the current certificate. This CRL |
- * itself will be needed the first time in the next round, of course. |
- * But we do the signature processing one round before this where the |
- * public key of the CA is available. |
- * |
- * 2. We'll check the revocation list of a CRL in each step when |
- * we find a CRL through the _issuer_ name of the current certificate. |
- * This CRLs signature was then already verified one round before. |
- * |
- * This verification scheme allows a CA to revoke its own certificate as |
- * well, of course. |
- */ |
- |
- /* |
- * Try to retrieve a CRL corresponding to the _subject_ of |
- * the current certificate in order to verify it's integrity. |
- */ |
- memset((char *)&obj, 0, sizeof(obj)); |
- rc = ssl_X509_STORE_lookup(c->crl, |
- X509_LU_CRL, subject, &obj); |
- crl = obj.data.crl; |
- |
- if ((rc > 0) && crl) { |
- /* |
- * Log information about CRL |
- * (A little bit complicated because of ASN.1 and BIOs...) |
- */ |
- /* |
- * Verify the signature on this CRL |
- */ |
- pubkey = X509_get_pubkey(cert); |
- rc = X509_CRL_verify(crl, pubkey); |
- /* Only refcounted in OpenSSL */ |
- if (pubkey) |
- EVP_PKEY_free(pubkey); |
- if (rc <= 0) { |
- /* TODO: Log Invalid signature on CRL */ |
- X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); |
- X509_OBJECT_free_contents(&obj); |
- return 0; |
- } |
- /* |
- * Check date of CRL to make sure it's not expired |
- */ |
- i = X509_cmp_current_time(X509_CRL_get_nextUpdate(crl)); |
+int SSL_CTX_use_client_CA_bio(SSL_CTX *ctx, BIO *bio) |
+{ |
+ return SSL_CTX_setup_certs(ctx, bio, false, true); |
+} |
- if (i == 0) { |
- /* TODO: Log Found CRL has invalid nextUpdate field */ |
+int SSL_use_certificate_chain_bio(SSL *ssl, BIO *bio, bool skipfirst) |
+{ |
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) |
+ // Only supported on openssl 1.0.2+ |
+ return -1; |
+#else |
+ X509 *x509; |
+ unsigned long err; |
+ int n; |
- X509_STORE_CTX_set_error(ctx, |
- X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); |
- X509_OBJECT_free_contents(&obj); |
- return 0; |
+ /* optionally skip a leading server certificate */ |
+ if (skipfirst) { |
+ if ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) == NULL) { |
+ return -1; |
} |
+ X509_free(x509); |
+ } |
- if (i < 0) { |
- /* TODO: Log Found CRL is expired */ |
- X509_STORE_CTX_set_error(ctx, X509_V_ERR_CRL_HAS_EXPIRED); |
- X509_OBJECT_free_contents(&obj); |
+ /* create new extra chain by loading the certs */ |
+ n = 0; |
- return 0; |
+ while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) { |
+ if (SSL_add0_chain_cert(ssl, x509) != 1) { |
+ X509_free(x509); |
+ return -1; |
} |
- |
- X509_OBJECT_free_contents(&obj); |
+ n++; |
} |
- |
- /* |
- * Try to retrieve a CRL corresponding to the _issuer_ of |
- * the current certificate in order to check for revocation. |
- */ |
- memset((char *)&obj, 0, sizeof(obj)); |
- rc = ssl_X509_STORE_lookup(c->crl, |
- X509_LU_CRL, issuer, &obj); |
- |
- crl = obj.data.crl; |
- if ((rc > 0) && crl) { |
- /* |
- * Check if the current certificate is revoked by this CRL |
- */ |
- n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); |
- |
- for (i = 0; i < n; i++) { |
- X509_REVOKED *revoked = |
- sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); |
- |
- ASN1_INTEGER *sn = revoked->serialNumber; |
- |
- if (!ASN1_INTEGER_cmp(sn, X509_get_serialNumber(cert))) { |
- X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_REVOKED); |
- X509_OBJECT_free_contents(&obj); |
- |
- return 0; |
- } |
+ /* Make sure that only the error is just an EOF */ |
+ if ((err = ERR_peek_error()) > 0) { |
+ if (!( ERR_GET_LIB(err) == ERR_LIB_PEM |
+ && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { |
+ return -1; |
} |
- |
- X509_OBJECT_free_contents(&obj); |
+ ERR_clear_error(); |
} |
- |
- return ok; |
+ return n; |
+#endif |
} |
-/* |
- * 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) |
+X509 *load_pem_cert_bio(const char *password, const BIO *bio) |
{ |
- /* Get Apache context back through OpenSSL context */ |
- SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, |
- SSL_get_ex_data_X509_STORE_CTX_idx()); |
- tcn_ssl_ctxt_t *c = SSL_get_app_data2(ssl); |
- |
- /* Get verify ingredients */ |
- int errnum = X509_STORE_CTX_get_error(ctx); |
- int errdepth = X509_STORE_CTX_get_error_depth(ctx); |
- int verify = c->verify_mode; |
- int depth = c->verify_depth; |
- int skip_crl = 0; |
- |
- if (verify == SSL_CVERIFY_UNSET || |
- verify == SSL_CVERIFY_NONE) |
- return 1; |
- |
- if (SSL_VERIFY_ERROR_IS_OPTIONAL(errnum) && |
- (verify == SSL_CVERIFY_OPTIONAL_NO_CA)) { |
- 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 && c->crl && !skip_crl) { |
- if (!(ok = ssl_verify_CRL(ok, ctx, c))) { |
- errnum = X509_STORE_CTX_get_error(ctx); |
- /* TODO: Log something */ |
- } |
- } |
- /* |
- * If we already know it's not ok, log the real reason |
- */ |
- if (!ok) { |
- // Just in case that sslnetwork stuff was used, which is not true for netty but it can't harm to still |
- // guard against it. |
- tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)SSL_get_app_data(ssl); |
- |
- /* TODO: Some logging |
- * Certificate Verification: Error |
- */ |
- if (con != NULL && con->peer) { |
- X509_free(con->peer); |
- con->peer = NULL; |
- } |
+ X509 *cert = PEM_read_bio_X509_AUX((BIO*) bio, NULL, |
+ (pem_password_cb *)SSL_password_callback, |
+ (void *)password); |
+ if (cert == NULL && |
+ (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE)) { |
+ ERR_clear_error(); |
+ BIO_ctrl((BIO*) bio, BIO_CTRL_RESET, 0, NULL); |
+ cert = d2i_X509_bio((BIO*) bio, NULL); |
} |
- if (errdepth > depth) { |
- /* TODO: Some logging |
- * Certificate Verification: Certificate Chain too long |
- */ |
- ok = 0; |
- } |
- return ok; |
+ return cert; |
} |
-/* |
- * This callback function is executed while OpenSSL processes the SSL |
- * handshake and does SSL record layer stuff. It's used to trap |
- * client-initiated renegotiations, and for dumping everything to the |
- * log. |
- */ |
-void SSL_callback_handshake(const SSL *ssl, int where, int rc) |
+EVP_PKEY *load_pem_key_bio(const char *password, const BIO *bio) |
{ |
- tcn_ssl_conn_t *con = (tcn_ssl_conn_t *)SSL_get_app_data(ssl); |
- |
- /* Retrieve the conn_rec and the associated SSLConnRec. */ |
- if (con == NULL) { |
- return; |
- } |
+ EVP_PKEY *key = PEM_read_bio_PrivateKey((BIO*) bio, NULL, |
+ (pem_password_cb *)SSL_password_callback, |
+ (void *)password); |
+ BIO_ctrl((BIO*) bio, BIO_CTRL_RESET, 0, NULL); |
+ return key; |
+} |
- /* If the reneg state is to reject renegotiations, check the SSL |
- * state machine and move to ABORT if a Client Hello is being |
- * read. */ |
- if ((where & SSL_CB_ACCEPT_LOOP) && con->reneg_state == RENEG_REJECT) { |
- int state = SSL_get_state(ssl); |
+int tcn_EVP_PKEY_up_ref(EVP_PKEY* pkey) { |
+#if defined(OPENSSL_IS_BORINGSSL) |
+ // Workaround for https://bugs.chromium.org/p/boringssl/issues/detail?id=89# |
+ EVP_PKEY_up_ref(pkey); |
+ return 1; |
+#elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
+ return CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); |
+#else |
+ return EVP_PKEY_up_ref(pkey); |
+#endif |
+} |
- if (state == SSL3_ST_SR_CLNT_HELLO_A |
-#ifndef OPENSSL_IS_BORINGSSL |
- || state == SSL23_ST_SR_CLNT_HELLO_A |
+int tcn_X509_up_ref(X509* cert) { |
+#if defined(OPENSSL_IS_BORINGSSL) |
+ // Workaround for https://bugs.chromium.org/p/boringssl/issues/detail?id=89# |
+ X509_up_ref(cert); |
+ return 1; |
+#elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
+ return CRYPTO_add(&cert->references, 1, CRYPTO_LOCK_X509); |
+#else |
+ return X509_up_ref(cert); |
#endif |
- ) { |
- con->reneg_state = RENEG_ABORT; |
- /* XXX: rejecting client initiated renegotiation |
- */ |
- } |
- } |
- /* If the first handshake is complete, change state to reject any |
- * subsequent client-initated renegotiation. */ |
- else if ((where & SSL_CB_HANDSHAKE_DONE) && con->reneg_state == RENEG_INIT) { |
- con->reneg_state = RENEG_REJECT; |
+} |
+ |
+int tcn_set_verify_config(tcn_ssl_verify_config_t* c, jint tcn_mode, jint depth) { |
+ if (depth >= 0) { |
+ c->verify_depth = depth; |
} |
+ switch (tcn_mode) { |
+ case SSL_CVERIFY_IGNORED: |
+ switch (c->verify_mode) { |
+ case SSL_CVERIFY_NONE: |
+ return SSL_VERIFY_NONE; |
+ case SSL_CVERIFY_OPTIONAL: |
+ return SSL_VERIFY_PEER; |
+ default: |
+ return (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT); |
+ } |
+ case SSL_CVERIFY_NONE: |
+ c->verify_mode = SSL_CVERIFY_NONE; |
+ return SSL_VERIFY_NONE; |
+ case SSL_CVERIFY_OPTIONAL: |
+ c->verify_mode = SSL_CVERIFY_OPTIONAL; |
+ return SSL_VERIFY_PEER; |
+ default: |
+ c->verify_mode = SSL_CVERIFY_REQUIRED; |
+ return SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; |
+ } |
} |
int SSL_callback_next_protos(SSL *ssl, const unsigned char **data, |
@@ -848,513 +678,3 @@ int SSL_callback_alpn_select_proto(SSL* ssl, const unsigned char **out, unsigned |
tcn_ssl_ctxt_t *ssl_ctxt = arg; |
return select_next_proto(ssl, out, outlen, in, inlen, ssl_ctxt->alpn_proto_data, ssl_ctxt->alpn_proto_len, ssl_ctxt->alpn_selector_failure_behavior); |
} |
- |
-#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 */ |