httpd-bugs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bugzi...@apache.org
Subject DO NOT REPLY [Bug 41123] - Support of OCSP in mod_ssl (rewritten patch from bug #31383)
Date Fri, 12 Oct 2007 13:22:22 GMT
DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG·
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://issues.apache.org/bugzilla/show_bug.cgi?id=41123>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND·
INSERTED IN THE BUG DATABASE.

http://issues.apache.org/bugzilla/show_bug.cgi?id=41123


marc.stern@approach.be changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
  Attachment #20958|Small corrections in error  |Small corrections in error
        description|handling and OCSP response  |handling, OCSP response
                   |logging                     |logging, and memory leaks
                   |                            |corrections (remarks from
                   |                            |OpenSSL developers)




------- Additional Comments From marc.stern@approach.be  2007-10-12 06:22 -------
(From update of attachment 20958)
diff -uaEbwNp orig/ssl_ocsp.c ./ssl_ocsp.c
--- orig/ssl_ocsp.c	1970-01-01 01:00:00.000000000 +0100
+++ ./ssl_ocsp.c	2007-10-11 16:37:20.866178500 +0200
@@ -0,0 +1,441 @@
+/* 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.
+ * The ASF 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.
+ */
+    
+/*			 _	       _
+ *  _ __ ___	___   __| |    ___ ___| |  mod_ssl
+ * | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
+ * | | | | | | (_) | (_| |   \__ \__ \ |
+ * |_| |_| |_|\___/ \__,_|___|___/___/_|
+ *			 |_____|
+ *  ssl_ocsp.c
+ *  The SSL OCSP checking
+ *
+ *  Developed by Marc Stern, for Approach Belgium / CSC / Belgian Government
+ *	      based on code developed by Zetes Pass
+ *
+ *  This code was added to support the Belgian Electronic Identity Card
+ *
+ *  The OCSP responder URL is read from the certificate itself
+ *
+ */ 
+    /* ``When the only tool you own is a hammer,
+	  every problem begins to resemble a nail.´´
+     */ 
+    
+#include "mod_ssl.h"
+#include "ssl_private.h"
+#include "apr_base64.h"
+
+#define X509_NAME2STR(name_)	     X509_NAME_oneline(name_, NULL, 0)
+#define X509_SUBJ_NAME(cert_)	    
X509_NAME2STR(X509_get_subject_name(cert_))
+#define X509_ISSUER_NAME(cert_)     X509_NAME2STR(X509_get_issuer_name(cert_))
+
+
+static char *ap_ocsp_ASN1_Int2Str(ASN1_INTEGER *data, apr_pool_t *pool)
+{
+    char *result = (char *)apr_palloc(pool, 100); /* 100 should be enough */
+    char *buf = NULL;
+    BIGNUM *bn = ASN1_INTEGER_to_BN(data, NULL);
+
+    *result = 0;
+    if (bn && !BN_is_zero(bn)) {
+	 buf = BN_bn2hex(bn);
+	 if (buf) {
+	     strncpy(result, buf, sizeof(result) - 1);
+	     result[sizeof(result) - 1 ] = 0;
+	 }
+    }
+
+    if (bn) BN_free(bn);
+    if (buf) OPENSSL_free(buf);
+    return result;
+}
+
+static char *ap_ocsp_get_ocsp_uri(X509 *cert, apr_pool_t *pool)
+{
+    int crit, j;
+    STACK_OF(ACCESS_DESCRIPTION) *values =
+	 (STACK_OF(ACCESS_DESCRIPTION) *)
+		X509_get_ext_d2i(cert, NID_info_access, &crit, NULL);
+    if (! values) return NULL;
+
+    for (j = 0; j < sk_ACCESS_DESCRIPTION_num(values); j++) {
+	 ACCESS_DESCRIPTION *value = sk_ACCESS_DESCRIPTION_value(values, j);
+	 if(OBJ_obj2nid(value->method) == NID_ad_OCSP) {
+	     /* Name found in extension */
+			char *result;
+			
+			/* Check that it is a URI */
+			if (value->location->type != GEN_URI)
+				continue;
+
+	     result = apr_pstrdup(pool,
+			      (char
*)value->location->d.uniformResourceIdentifier->data);
+			 AUTHORITY_INFO_ACCESS_free(values);
+	     return result;
+	 }
+    }
+
+	sk_ACCESS_DESCRIPTION_free(values);
+	//AUTHORITY_INFO_ACCESS_free(values);
+    return NULL;
+}
+
+
+static BIO *ap_ocsp_connect(const char *host, int port) 
+{
+    BIO *connection = BIO_new_connect((char *)host);
+    if (!connection) return 0;
+
+    BIO_set_conn_int_port(connection, &port);
+    if (BIO_do_connect(connection) <= 0) {
+	 /* Not needed - default: BIO_set_close(connection, BIO_CLOSE); */
+	 BIO_free_all(connection);
+	 return NULL;
+    }
+
+    return connection;
+
+}
+
+
+static OCSP_RESPONSE *ap_ocsp_sendreq(const char *ocspHost, const char
*ocspPort, const char *ocspPath, OCSP_REQUEST *request, server_rec *s) 
+{
+    BIO *bio = NULL;
+    OCSP_RESPONSE *response = NULL;
+
+    /* establish a connection to the OCSP responder */ 
+    ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+	 "Connect to OCSP responder '%s:%s'", ocspHost, ocspPort);
+    bio = ap_ocsp_connect(ocspHost, atoi(ocspPort));
+    if (!bio) {
+	 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+		 "Cannot connect to OCSP responder '%s:%s'", ocspHost,
ocspPort);
+	 return NULL;
+    }
+    
+    /* send the request and get a response */ 
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+		 "sending request to OCSP responder");
+    response = OCSP_sendreq_bio(bio, (char *)ocspPath, request);
+    if (!response) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+			 "Cannot send request to OCSP responder '%s'",
ocspHost);
+	 }
+
+    BIO_free_all(bio);
+
+    return response;
+}
+
+
+static int ap_ocsp_verify_ocsp(X509 *cert, X509_STORE_CTX *ctx, server_rec *s,
+				int *ocspStatus, apr_pool_t *pool) 
+{
+    int rc = SSL_OCSP_OK;
+    X509 *issuer = NULL;
+    char *ocspUrl = NULL, *ocspHost = NULL, *ocspPort = NULL, *ocspPath =
NULL;
+    BIO * bio = NULL;
+    OCSP_RESPONSE * response = NULL;
+    OCSP_BASICRESP * basicResponse = NULL;
+    OCSP_REQUEST * request = NULL;
+    OCSP_CERTID * certID = NULL;
+    int ssl = 0;
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+	char *subj_name = X509_SUBJ_NAME(cert);
+	char *issuer_name = X509_ISSUER_NAME(cert);
+
+    *ocspStatus = V_OCSP_CERTSTATUS_UNKNOWN;
+    X509_STORE_CTX_set_error(ctx, X509_V_ERR_APPLICATION_VERIFICATION);
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
+	 "OCSP check - cert='%s', issuer='%s'", subj_name, issuer_name);
+   
+    
+    /* First look if we force the responder url*/
+    if (sc->server->OCSPForceResponderURL) {
+	 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+		 "Force the url of responder to: %s",
sc->server->OCSPForceResponderURL);
+	 ocspUrl = sc->server->OCSPForceResponderURL;
+    }
+    /* if not, look inside the certificate if we have one */
+    else {
+	  /* Get OCSP Responder URI (only first one) */
+	 ocspUrl = ap_ocsp_get_ocsp_uri(cert, pool); 
+	 if (ocspUrl)
+	     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+		 "OCSP responder from certificate: %s", ocspUrl);
+    }
+
+    if (!ocspUrl && sc->server->OCSPDefaultResponderURL) {
+	 ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
+	     "No Responder URL in certificate - using default: %s",
+	     sc->server->OCSPDefaultResponderURL);
+	 ocspUrl = sc->server->OCSPDefaultResponderURL;
+    }
+
+    if (!ocspUrl) {
+	 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
+	  "Cannot get OCSP responder URL from '%s' and no default URL
Responder",
+	  subj_name);
+	 rc = SSL_OCSP_ERROR_PARSE_URL;
+    }
+    
+    if (rc == SSL_OCSP_OK) {
+	 if (!OCSP_parse_url(ocspUrl, &ocspHost, &ocspPort, &ocspPath, &ssl)) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+		 "Cannot parse OCSP responder URL from '%s'",
+				subj_name);
+	     rc = SSL_OCSP_ERROR_PARSE_URL;
+	 }
+    }
+    
+
+    if (rc == SSL_OCSP_OK) {
+	 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Create new OCSP
request");
+	 request = OCSP_REQUEST_new();
+	 if (!request) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+			 "Cannot create new OCSP request");
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+    
+    if (rc == SSL_OCSP_OK) {
+	 /* Get issuer */
+		int r;
+		/* Enhancement: ctx->chain is already ordered -> extract 2nd ?
*/
+	 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Get Issuer");
+	 r = X509_STORE_CTX_get1_issuer(&issuer, ctx, cert);
+	 if (r != 1) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+			 "Cannot get issuer of '%s'. rc=%d", subj_name, rc);
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+
+    if (rc == SSL_OCSP_OK) {
+	 certID = OCSP_cert_to_id(0, cert, issuer);
+	 if (!certID || !OCSP_request_add0_id(request, certID)) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s
+			, "Cannot get certificate id from '%s'", subj_name);
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+
+    if (rc == SSL_OCSP_OK) {
+	 OCSP_request_add1_nonce(request, 0, -1);
+
+	 /*  To use a proxy, do the following
+	       - ocspHost = proxyHost;
+	       - ocspPort = proxyPort;
+	       - ocspPath = ocspUrl;
+	  */
+    
+	 /* establish a connection to the OCSP responder */ 
+	 response = ap_ocsp_sendreq(ocspHost, ocspPort, ocspPath, request, s);
+	 if (!response) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+		     "Cannot send request to OCSP responder '%s'", ocspHost);
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+
+
+    if ( (rc == SSL_OCSP_OK) && (s->loglevel >= APLOG_DEBUG) ) {
+	 /* Log OCSP answer (complete OpenSSL buffer) */
+	 char *buf = apr_palloc(pool,
+		
apr_base64_encode_len(response->responseBytes->response->length) + 1);
+	 if (buf) {
+	     apr_base64_encode(buf,
+			 (const char*)response->responseBytes->response->data,
+			 response->responseBytes->response->length);
+	     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+			 "OCSP response (OpenSSL bufer): serial=%s | dn=%s |
%s",
+			  ap_ocsp_ASN1_Int2Str(X509_get_serialNumber(cert),
pool),
+			   X509_SUBJ_NAME(cert), buf);
+	 }
+	 else {
+	    ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Cannot allocate
buffer");
+	 }
+    }
+
+    if (rc == SSL_OCSP_OK) {
+		int r;
+	 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+		 "Analyse OCSP request answer");
+	 r = OCSP_response_status(response);
+	 if (r != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+			 "Bad OCSP responder answer. rc=%d", rc);
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+    
+    if ( (rc == SSL_OCSP_OK) && (s->loglevel >= APLOG_DEBUG) ) {
+	 /* Log OCSP answer (only the "bare" response) */
+	 int len = i2d_OCSP_RESPONSE(response, NULL);
+	 if (len <= 0)
+	    rc = SSL_OCSP_ERROR_INTERNAL;
+	 else {
+			unsigned char *buf1, *buf2;
+	     buf1 = buf2 = (unsigned char *)apr_palloc(pool, len);
+	     if (!buf1) {
+				ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Out
of memory");
+		 rc = SSL_OCSP_ERROR_INTERNAL;
+	     }
+	     else {
+		if (i2d_OCSP_RESPONSE(response, &buf1) != len) 
+			       rc = SSL_OCSP_ERROR_INTERNAL;
+		else {
+		     /* contents is in buf2, because buf1 is now pointing
+			to the end of the structure */
+		     char h[] = "OCSP response : ";
+		     int len64 = apr_base64_encode_len(len);
+		     char *msg = (char *)apr_palloc(pool, len64 + strlen(h) +
1);
+		     if (msg) {
+			 strcpy(msg, h);
+			 apr_base64_encode(msg + strlen(h), buf2, len);
+			 msg[strlen(h) + len64 + 1] = 0;
+			 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, msg);
+		     }
+		}
+	    }
+	}
+    }
+
+    if (rc == SSL_OCSP_OK) {
+	 basicResponse = OCSP_response_get1_basic(response);
+	 if (!basicResponse) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+			 "Bad OCSP responder answer");
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+
+    if (rc == SSL_OCSP_OK) {
+	 if (OCSP_check_nonce(request, basicResponse) <= 0) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+			 "Bad OCSP responder answer (bad nonce)");
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+	 
+    if (rc == SSL_OCSP_OK) {
+	 if (OCSP_basic_verify(basicResponse, 0, ctx->ctx,
+		 sc->server->OCSPResponderVerifyFlag) <= 0) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+			 "Error verifying OCSP responder answer");
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+    
+    if (rc == SSL_OCSP_OK) {
+	 int ocspReason = -1;
+	 ASN1_GENERALIZEDTIME * ocspProducedAt, *ocspThisUpdate,
+	     *ocspNextUpdate;
+	 rc = OCSP_resp_find_status(basicResponse, certID, ocspStatus,
+		       &ocspReason, &ocspProducedAt,
+		       &ocspThisUpdate, &ocspNextUpdate);
+	 if (rc == 0) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Bad OCSP status");
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+	 "OCSP validation completed: status=%d", *ocspStatus);
+    rc = SSL_OCSP_OK;
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Cleanup OCSP code");
+	if (issuer) X509_free(issuer);
+	if (subj_name)	 OPENSSL_free(subj_name);
+	if (issuer_name) OPENSSL_free(issuer_name);
+    if (request)  OCSP_REQUEST_free(request);
+    if (response) OCSP_RESPONSE_free(response);
+    if (basicResponse) OCSP_BASICRESP_free(basicResponse);
+	if (ocspHost) OPENSSL_free(ocspHost);
+	if (ocspPort) OPENSSL_free(ocspPort);
+	if (ocspPath) OPENSSL_free(ocspPath);
+    /* certID is just a pointer, nothing to free */ 
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Ending cleanup OCSP code");
+    return rc;
+}
+
+int ssl_cmd_VerifyOCSP(X509_STORE_CTX *ctx, server_rec *s, int *ocspStatus,
+ apr_pool_t *pool) 
+{
+    int rc = SSL_OCSP_OK, i;
+    X509 *cert = X509_STORE_CTX_get_current_cert(ctx);
+    X509_STORE_CTX *ocspCtx = NULL;
+    X509_STORE *store = NULL;
+    SSLSrvConfigRec *sc = mySrvConfig(s);
+	char *subj_name = X509_SUBJ_NAME(cert);
+	char *issuer_name = X509_ISSUER_NAME(cert);
+
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, s,
+	 "Validating certificate '%s', issuer: '%s'",
+	    subj_name, issuer_name);
+    
+    /* Store certif chain in a store */ 
+    if (!ctx->chain) {
+	 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "No certificate chain");
+	 return SSL_OCSP_ERROR_INTERNAL;
+    }
+
+    /*
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
+	 "certificates chain length: %d", ctx->chain->num);
+    */ 
+
+    store = X509_STORE_new();
+    if (!store) {
+	 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+		 "Cannot create a new X509 store");
+	 return SSL_OCSP_ERROR_INTERNAL;
+    }
+    
+    for (i = 0; i < ctx->chain->num; i++)
+    if (!X509_STORE_add_cert(store, sk_X509_value(ctx->chain, i))) {
+	 ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+		 "Cannot add certificate to X509 store");
+	 rc = SSL_OCSP_ERROR_INTERNAL;
+    }
+
+    if (rc == SSL_OCSP_OK) {
+	 ocspCtx = X509_STORE_CTX_new();
+	 if (!ocspCtx) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+		     "Cannot create a new X509 context");
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+    if (rc == SSL_OCSP_OK) {
+	 if (X509_STORE_CTX_init(ocspCtx, store, cert, 0) != 1) {
+	     ap_log_error(APLOG_MARK, APLOG_ERR, 0, s,
+			 "Cannot initialise the new X509 context");
+	     rc = SSL_OCSP_ERROR_INTERNAL;
+	 }
+    }
+
+    if (rc == SSL_OCSP_OK)
+	 rc = ap_ocsp_verify_ocsp(cert, ocspCtx, s, ocspStatus, pool);
+
+	 if (subj_name)   OPENSSL_free(subj_name);
+	 if (issuer_name) OPENSSL_free(issuer_name);
+    if (store)   X509_STORE_free(store);
+    if (ocspCtx) X509_STORE_CTX_free(ocspCtx);
+    return rc;
+}


-- 
Configure bugmail: http://issues.apache.org/bugzilla/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are the assignee for the bug, or are watching the assignee.

---------------------------------------------------------------------
To unsubscribe, e-mail: bugs-unsubscribe@httpd.apache.org
For additional commands, e-mail: bugs-help@httpd.apache.org


Mime
View raw message