--- a/jni/native/build-outputs.mk Wed Jul 09 16:06:06 2008 +0300 +++ a/jni/native/build-outputs.mk Fri Jul 11 09:47:33 2008 +0300 @@ -1,5 +1,6 @@ # DO NOT EDIT. AUTOMATICALLY GENERATED. +src/ocsp_sock.lo: src/ocsp_sock.c .make.dirs include/ocsp_sock.h src/proc.lo: src/proc.c .make.dirs include/tcn.h include/tcn_api.h src/poll.lo: src/poll.c .make.dirs include/tcn.h include/tcn_api.h src/ssl.lo: src/ssl.c .make.dirs include/tcn.h include/tcn_api.h include/ssl_private.h @@ -26,7 +27,7 @@ src/os.lo: src/os.c .make.dirs include/t src/os.lo: src/os.c .make.dirs include/tcn.h include/tcn_api.h src/pool.lo: src/pool.c .make.dirs include/tcn.h include/tcn_api.h -OBJECTS_all = src/proc.lo src/poll.lo src/ssl.lo src/sslutils.lo src/thread.lo src/dir.lo src/network.lo src/sslinfo.lo src/stdlib.lo src/multicast.lo src/error.lo src/lock.lo src/file.lo src/sslcontext.lo src/jnilib.lo src/info.lo src/mmap.lo src/shm.lo src/user.lo src/address.lo src/misc.lo src/bb.lo src/sslnetwork.lo src/os.lo src/pool.lo +OBJECTS_all = src/ocsp_sock src/proc.lo src/poll.lo src/ssl.lo src/sslutils.lo src/thread.lo src/dir.lo src/network.lo src/sslinfo.lo src/stdlib.lo src/multicast.lo src/error.lo src/lock.lo src/file.lo src/sslcontext.lo src/jnilib.lo src/info.lo src/mmap.lo src/shm.lo src/user.lo src/address.lo src/misc.lo src/bb.lo src/sslnetwork.lo src/os.lo src/pool.lo os/unix/system.lo: os/unix/system.c .make.dirs include/tcn.h include/tcn_api.h os/unix/uxpipe.lo: os/unix/uxpipe.c .make.dirs include/tcn.h include/tcn_api.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 3b86749fdda8 Fri Jul 11 09:47:33 2008 +0300 @@ -0,0 +1,6 @@ +#define OCSP_STATUS_OK 0 +#define OCSP_STATUS_REVOKED 1 +#define OCSP_STATUS_UNKNOWN 2 + + +int ocspRequest(X509 *cert, X509 *issuer); --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ 3b86749fdda8 Fri Jul 11 09:47:33 2008 +0300 @@ -0,0 +1,314 @@ +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +#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 + + + +/* 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) +{ + 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 = realloc(*ocsp_urls,new_nocsp_urls)) == NULL) + err = 1; + if(!err) { + *ocsp_urls = new_ocsp_urls; + *nocsp_urls = new_nocsp_urls; + *(*ocsp_urls + *nocsp_urls) = NULL; + if((ocsp_url = malloc((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) +{ + 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); + } + 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) +{ + int len, err = 0; + while( !err && *asn1 != '\0') { + switch(*asn1) { + case ASN1_SEQUENCE: + len = *++asn1; + asn1++; + err = parse_ASN1_Sequence(asn1, ocsp_urls, nocsp_urls); + break; + case ASN1_OID: + err = parse_ASN1_OID(asn1,ocsp_urls,nocsp_urls); + 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) +{ + char **response = NULL; + unsigned char *ocsp_urls; + int i, len, err = 0, numofresponses = 0 ; + + len = ASN1_STRING_length(os); + + ocsp_urls = malloc((size_t)(len+1)*sizeof(char)); + memcpy(ocsp_urls,os->data, (size_t) len); + ocsp_urls[len] = '\0'; + + if ((response = malloc(sizeof(char *))) == NULL) + return NULL; + *response = NULL; + + err = parse_ASN1_Sequence(ocsp_urls, &response, &numofresponses); + if (err) { + for(i = 0 ; i < numofresponses ; i++) + free(response[i]); + free(response); + response = NULL; + } + + free(ocsp_urls); + 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 a socket, connects to the OCSP host and then +returns a BIO object than can be used from the openssl +library + +TODO : Suport https connections +*/ +static BIO *make_socket(char *hostname, int port) +{ + BIO *biosock; + struct hostent *host; + int sock, r = 0; + struct sockaddr_in sa_in; + + bzero(&sa_in, sizeof(struct sockaddr_in)); + + + sa_in.sin_family = AF_INET; + sa_in.sin_port = htons(port); + + sa_in.sin_addr.s_addr = inet_addr(hostname); /* if the adrress is + already in IP notation + then we can use it */ + if(sa_in.sin_addr.s_addr == -1) { + h_errno = NETDB_SUCCESS; /* Clear h_errno */ + do { + host = gethostbyname(hostname); /* If we have a try again error */ + r++; + }while(r < 10 && h_errno == TRY_AGAIN); /* we try for 10 times */ + /* for all other errors , we just fail */ + if(host == NULL) + return NULL; + + memcpy(&(sa_in.sin_addr), host->h_addr, sizeof(sa_in.sin_addr)); + if(sa_in.sin_addr.s_addr == -1) + return NULL; + } + + sock = socket(AF_INET,SOCK_STREAM, 0); + if(sock == -1) + return NULL; + + if ( (connect(sock,(struct sockaddr *) &sa_in, sizeof(struct sockaddr_in))) == -1) + return NULL; + + biosock = BIO_new_socket(sock, BIO_CLOSE); /* when we free we want the socket to close + BIO_CLOSE does this (????) */ + return biosock; + +} + +/* 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; + OCSP_REQUEST *ocsp_req; + BIO *sock; + char *hostname, *path, *c_port; + int port, use_ssl; + STACK_OF(OCSP_CERTID) *ids = NULL; + + ids = sk_OCSP_CERTID_new_null(); + + /* problem parsing the URL */ + if(OCSP_parse_url(url,&hostname, &c_port, &path, &use_ssl) == 0 ) { + sk_OCP_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; + + sock = make_socket(hostname, port); + if (sock == NULL) { + ocsp_resp = NULL; + goto free_req; + } + + ocsp_resp = OCSP_sendreq_bio(sock,path,ocsp_req); + + + BIO_free(sock); + +free_req: + 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; + + /* 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); + } + + /* 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]); + + /* memory clean up */ + for(i = 0 ; ocsp_urls[i] != NULL ; i++ ) + free(ocsp_urls[i]); + free(ocsp_urls); + + return processOCSPResponse(resp); + } + return OCSP_STATUS_UNKNOWN; +} --- a/jni/native/src/sslutils.c Wed Jul 09 16:06:06 2008 +0300 +++ a/jni/native/src/sslutils.c Fri Jul 11 09:47:33 2008 +0300 @@ -31,6 +31,7 @@ extern int WIN32_SSL_password_prompt(tcn extern int WIN32_SSL_password_prompt(tcn_pass_cb_t *data); #endif +#include "ocsp_sock.h" /* _________________________________________________________________ ** ** Additional High-Level Functions for OpenSSL @@ -621,6 +622,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 */ @@ -632,6 +635,8 @@ 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 ocsp_response; + int skip_crl = 0; if (verify == SSL_CVERIFY_UNSET || verify == SSL_CVERIFY_NONE) @@ -642,10 +647,24 @@ int SSL_callback_SSL_verify(int ok, X509 ok = 1; SSL_set_verify_result(ssl, X509_V_OK); } + + /* 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 */ + } /* * 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 +691,32 @@ int SSL_callback_SSL_verify(int ok, X509 return ok; } + +/* Function that is used to do the OCSP verification */ +int ssl_verify_OCSP(int ok, X509_STORE_CTX *ctx) +{ + X509 *cert, *issuer; + int r, 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; +} + #endif