ASF Bugzilla – Attachment 22611 Details for
Bug 45392
No OCSP support for client SSL verification
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
OCSP support for apache tomcat
withapr.diff (text/plain), 17.98 KB, created by
Aristotelis
on 2008-09-19 07:13:04 UTC
(
hide
)
Description:
OCSP support for apache tomcat
Filename:
MIME Type:
Creator:
Aristotelis
Created:
2008-09-19 07:13:04 UTC
Size:
17.98 KB
patch
obsolete
>diff -r dfcd763ab174 jni/native/include/ocsp_sock.h >--- /dev/null Thu Jan 01 00:00:00 1970 +0000 >+++ b/jni/native/include/ocsp_sock.h Fri Sep 19 17:04:40 2008 +0300 >@@ -0,0 +1,22 @@ >+/* 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. >+ */ >+ >+#define OCSP_STATUS_OK 0 >+#define OCSP_STATUS_REVOKED 1 >+#define OCSP_STATUS_UNKNOWN 2 >+ >+ >+int ocspRequest(X509 *cert, X509 *issuer); >diff -r dfcd763ab174 jni/native/src/sslutils.c >--- a/jni/native/src/sslutils.c Thu Sep 11 16:34:31 2008 +0300 >+++ b/jni/native/src/sslutils.c Fri Sep 19 17:04:40 2008 +0300 >@@ -31,6 +31,27 @@ > extern int WIN32_SSL_password_prompt(tcn_pass_cb_t *data); > #endif > >+#ifdef HAVE_SSL_OCSP >+#include <stdio.h> >+#include <stdlib.h> >+#include <string.h> >+#include <ctype.h> >+ >+#include <openssl/bio.h> >+#include <openssl/ocsp.h> >+ >+#include "apr_network_io.h" >+ >+#include "ocsp_sock.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 >+ >+int ssl_verify_OCSP(int ok, X509_STORE_CTX *ctx); >+ >+#endif /* HAVE_SSL_OCSP */ > /* _________________________________________________________________ > ** > ** Additional High-Level Functions for OpenSSL >@@ -621,6 +642,8 @@ > * 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 */ >@@ -632,6 +655,8 @@ > int errdepth = X509_STORE_CTX_get_error_depth(ctx); > int verify = con->ctx->verify_mode; > int depth = con->ctx->verify_depth; >+ int ocsp_response; >+ int skip_crl = 0; > > if (verify == SSL_CVERIFY_UNSET || > verify == SSL_CVERIFY_NONE) >@@ -642,10 +667,26 @@ > ok = 1; > SSL_set_verify_result(ssl, X509_V_OK); > } >+ >+#ifdef HAVE_SSL_OCSP >+ /* First perform OCSP validation if possible */ >+ if(ok) { >+ 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) >+ ; /* do nothing for time being, continue with CRL */ >+ } >+#endif /* HAVE_SSL_OCSP */ > /* > * 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 */ >@@ -672,4 +713,522 @@ > return ok; > } > >+#ifdef HAVE_SSL_OCSP >+ >+/* Function that is used to do the OCSP verification */ >+int ssl_verify_OCSP(int ok, X509_STORE_CTX *ctx) >+{ >+ X509 *cert, *issuer; >+ int r = OCSP_STATUS_UNKNOWN, o; >+ >+ cert = X509_STORE_CTX_get_current_cert(ctx); >+ >+ o = X509_STORE_CTX_get1_issuer(&issuer, ctx, cert); >+ /* if we can't get the issuer, we cannot perform OCSP verification */ >+ if( o == 1 ) { >+ r = ocspRequest(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 >+ r = OCSP_STATUS_UNKNOWN; /* else we return unknown, so that we can continue with the crl */ >+ 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; >+ 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)*sizeof(char))) == 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 developped 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 i, len, err = 0, numofresponses = 0 ; >+ >+ len = ASN1_STRING_length(os); >+ >+ ocsp_urls = apr_palloc(p, ((size_t)(len+1)*sizeof(char))); >+ memcpy(ocsp_urls,os->data, (size_t) len); >+ ocsp_urls[len] = '\0'; >+ >+ if ((response = apr_palloc(p, sizeof(char *))) == NULL) >+ return NULL; >+ *response = NULL; >+ >+ err = parse_ASN1_Sequence(ocsp_urls, &response, &numofresponses, p); >+ if (err) { >+ 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) goto err; >+ id = OCSP_cert_to_id(NULL, cert, issuer); >+ if(!id || !sk_OCSP_CERTID_push(ids, id)) goto err; >+ if(!OCSP_request_add0_id(*req, id)) goto err; >+ return 1; >+ >+err: >+ return 0; >+} >+ >+ >+/* 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) >+{ >+ int r = 0; >+ 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 */ >+#define HUGE_STRING_LENGTH 8192 >+static int ocsp_send_req(apr_socket_t *sock, BIO *req) >+{ >+ int len; >+ char buf[HUGE_STRING_LENGTH]; >+ 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 && !isspace((unsigned char)*p); p++) continue; >+ if(!*p) { >+ goto err; >+ } >+ /* Skip past white space to start of response code */ >+ while(isspace((unsigned char)*p)) p++; >+ if(!*p) { >+ goto err; >+ } >+ /* Find end of response code: first whitespace after start of code */ >+ for(q = p; *q && !isspace((unsigned char)*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(isspace((unsigned char)*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; isspace((unsigned char)*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; isspace((unsigned char)*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 = 0, 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 processOCSPResponse(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; >+} >+ >+ >+int ocspRequest(X509 *cert, X509 *issuer) >+{ >+ char **ocsp_urls = NULL; >+ int nid, i; >+ 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]); >+ >+ apr_pool_destroy(p); >+ if(resp != NULL) >+ return processOCSPResponse(resp); >+ } >+ apr_pool_destroy(p); >+ return OCSP_STATUS_UNKNOWN; >+} >+ >+#endif /* HAVE_SSL_OCSP */ >+ > #endif
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 45392
:
22251
|
22252
|
22253
|
22272
|
22594
| 22611