From 12bbe42bd4ea0a1fe5d5408b78e84f4838a8b57a Mon Sep 17 00:00:00 2001 From: Marcel Poul Date: Sun, 15 Apr 2012 23:36:07 +0000 Subject: [PATCH] prepare implementation of OCSP support --- emi.canl.canl-c/src/canl_ssl.c | 379 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 339 insertions(+), 40 deletions(-) diff --git a/emi.canl.canl-c/src/canl_ssl.c b/emi.canl.canl-c/src/canl_ssl.c index 9f6116f..a4f3d42 100644 --- a/emi.canl.canl-c/src/canl_ssl.c +++ b/emi.canl.canl-c/src/canl_ssl.c @@ -1,19 +1,252 @@ #include "canl_locl.h" #include "canl_ssl.h" #include "canl_mech_ssl.h" +#include #define SSL_SERVER_METH SSLv23_server_method() #define SSL_CLIENT_METH SSLv3_client_method() #define DESTROY_TIMEOUT 10 +#define USENONCE 0 + +typedef struct { + char *ca_dir; + char *crl_dir; +} canl_x509store_t; + +typedef struct { + char *url; + X509 *cert; + X509 *issuer; + canl_x509store_t *store; + X509 *sign_cert; + EVP_PKEY *sign_key; + long skew; + long maxage; +} canl_ocsprequest_t; + + + +typedef enum { + CANL_OCSPRESULT_ERROR_NOTCONFIGURED = -14, + CANL_OCSPRESULT_ERROR_NOAIAOCSPURI = -13, + CANL_OCSPRESULT_ERROR_INVALIDRESPONSE = -12, + CANL_OCSPRESULT_ERROR_CONNECTFAILURE = -11, + CANL_OCSPRESULT_ERROR_SIGNFAILURE = -10, + CANL_OCSPRESULT_ERROR_BADOCSPADDRESS = -9, + CANL_OCSPRESULT_ERROR_OUTOFMEMORY = -8, + CANL_OCSPRESULT_ERROR_UNKNOWN = -7, + CANL_OCSPRESULT_ERROR_UNAUTHORIZED = -6, + CANL_OCSPRESULT_ERROR_SIGREQUIRED = -5, + CANL_OCSPRESULT_ERROR_TRYLATER = -3, + CANL_OCSPRESULT_ERROR_INTERNALERROR = -2, + CANL_OCSPRESULT_ERROR_MALFORMEDREQUEST = -1, + CANL_OCSPRESULT_CERTIFICATE_VALID = 0, + CANL_OCSPRESULT_CERTIFICATE_REVOKED = 1 +} canl_ocspresult_t; + +static int do_ssl_connect( glb_ctx *cc, io_handler *io, + SSL *ssl, struct timeval *timeout); +static int do_ssl_accept( glb_ctx *cc, io_handler *io, + SSL *ssl, struct timeval *timeout); +static int check_hostname_cert(glb_ctx *cc, io_handler *io, + SSL *ssl, const char *host); +static BIO *my_connect_ssl(char *host, int port, SSL_CTX **ctx); +static BIO *my_connect(char *host, int port, int ssl, SSL_CTX **ctx); +static int set_ocsp_sign_cert(X509 *sign_cert); +static int set_ocsp_sign_key(EVP_PKEY *sign_key); +static int set_ocsp_cert(X509 *cert); +static int set_ocsp_skew(int skew); +static int set_ocsp_maxage(int maxage); +static int set_ocsp_url(char *url); +static int set_ocsp_issuer(X509 *issuer); +static canl_x509store_t * store_dup(canl_x509store_t *store_from); +static X509_STORE * canl_create_x509store(canl_x509store_t *store); -static int do_ssl_connect( glb_ctx *cc, io_handler *io, SSL *ssl, struct timeval *timeout); -static int do_ssl_accept( glb_ctx *cc, io_handler *io, SSL *ssl, struct timeval *timeout); -static int check_hostname_cert(glb_ctx *cc, io_handler *io, SSL *ssl, const char *host); #ifdef DEBUG static void dbg_print_ssl_error(int errorcode); #endif -static canl_err_code +static canl_ocsprequest_t *ocspreq = NULL; +/*static int set_ocsp_url(char *url, X509 *cert, X509 *issuer, + canl_x509store_t *store, X509 *sign_cert, EVP_PKEY *sign_key, + long skew, long maxage) { + */ + + static int +set_ocsp_cert(X509 *cert) +{ + + if (!ocspreq) + ocspreq = calloc(1, sizeof(*ocspreq)); + if (!ocspreq) + return 1; + + if (cert) { + if (!ocspreq->cert) { + X509_free(ocspreq->cert); + ocspreq->cert = NULL; + } + ocspreq->cert = X509_dup(cert); + if (!ocspreq->cert) + return 1; + } + return 0; +} + + static int +set_ocsp_url(char *url) +{ + + int len = 0; + if (!ocspreq) + ocspreq = calloc(1, sizeof(*ocspreq)); + if (!ocspreq) + return 1; + + if (url) { + if (!ocspreq->url) { + free (ocspreq->url); + ocspreq->url = NULL; + } + len = strlen(url); + ocspreq->url = (char *) malloc((len +1) * sizeof (char)); + if (!ocspreq->url) + return 1; + strncpy(ocspreq->url, url, len + 1); + } + return 0; +} + + static int +set_ocsp_issuer(X509 *issuer) +{ + + if (!ocspreq) + ocspreq = calloc(1, sizeof(*ocspreq)); + if (!ocspreq) + return 1; + if (issuer) { + if (!ocspreq->issuer) { + X509_free (ocspreq->issuer); + ocspreq->issuer = NULL; + } + ocspreq->issuer = X509_dup(issuer); + if (!ocspreq->issuer) + return 1; + } + return 0; +} + + static int +set_ocsp_sign_cert(X509 *sign_cert) +{ + + if (!ocspreq) + ocspreq = calloc(1, sizeof(*ocspreq)); + if (!ocspreq) + return 1; + if (sign_cert) { + if (!ocspreq->sign_cert) { + X509_free (ocspreq->sign_cert); + ocspreq->sign_cert = NULL; + } + ocspreq->sign_cert = X509_dup(sign_cert); + if (!ocspreq->sign_cert) + return 1; + } + return 0; +} + + static int +set_ocsp_sign_key(EVP_PKEY *sign_key) +{ + + if (!ocspreq) + ocspreq = calloc(1, sizeof(*ocspreq)); + if (!ocspreq) + return 1; + if (sign_key) { + if (!ocspreq->sign_key) { + EVP_PKEY_free (ocspreq->sign_key); + ocspreq->sign_key = NULL; + } + pkey_dup(&ocspreq->sign_key, sign_key); + if (!ocspreq->sign_key) + return 1; + } + return 0; +} + static int +set_ocsp_skew(int skew) +{ + + if (!ocspreq) + ocspreq = calloc(1, sizeof(*ocspreq)); + if (!ocspreq) + return 1; + if (skew) + ocspreq->skew = skew; + return 0; +} + static int +set_ocsp_maxage(int maxage) +{ + + if (!ocspreq) + ocspreq = calloc(1, sizeof(*ocspreq)); + if (!ocspreq) + return 1; + if (maxage) + ocspreq->maxage = maxage; + return 0; +} + +static canl_x509store_t * +store_dup(canl_x509store_t *store_from) +{ + canl_x509store_t *store_to = NULL; + if (!store_from) + return NULL; + + store_to = calloc(1, sizeof(*store_to)); + if (!store_to) + return NULL; + + if (store_from->ca_dir) { + int len = strlen(store_from->ca_dir); + store_to->ca_dir = (char *) malloc((len + 1) * sizeof (char)); + if (store_to->ca_dir) + return NULL; + strncpy (store_to->ca_dir, store_from->ca_dir, len + 1); + } + if (store_from->crl_dir) { + int len = strlen(store_from->crl_dir); + store_to->crl_dir = (char *) malloc((len + 1) * sizeof (char)); + if (store_to->crl_dir) + return NULL; + strncpy (store_to->crl_dir, store_from->crl_dir, len + 1); + } + return store_to; +} + + static int +set_ocsp_store(canl_x509store_t *store) +{ + + if (!ocspreq) + ocspreq = calloc(1, sizeof(*ocspreq)); + if (!ocspreq) + return 1; + if (store){ + ocspreq->store = store_dup(store); + if (!ocspreq->store) + return 1; + } + return 0; +} + + + static canl_err_code ssl_initialize(glb_ctx *cc, void **v_glb_ctx) { mech_glb_ctx **m_glb_ctx = (mech_glb_ctx **)v_glb_ctx; @@ -24,7 +257,7 @@ ssl_initialize(glb_ctx *cc, void **v_glb_ctx) SSL_CTX *ssl_ctx = NULL; if (!cc) - return EINVAL; + return EINVAL; SSL_library_init(); SSL_load_error_strings(); @@ -1014,46 +1247,65 @@ static void dbg_print_ssl_error(int errorcode) } #endif -#if 0 +static X509_STORE * +canl_create_x509store(canl_x509store_t *store) +{ + return NULL; +} -int do_ocsp_verify ((X509 *cert, X509 *issuer)) +int do_ocsp_verify (canl_ocsprequest_t *data) { OCSP_REQUEST *req = NULL; - int result = 0, ssl = 0; + OCSP_RESPONSE *resp = NULL; + OCSP_BASICRESP *basic = NULL; + X509_STORE *store = 0; + int rc = 0, reason = 0, ssl = 0, status = 0; char *host = NULL, *path = NULL, *port = NULL; OCSP_CERTID *id = NULL; + char *chosenurl = NULL; + BIO *bio = NULL; + SSL_CTX *ctx = NULL; + canl_ocspresult_t result = 0; + ASN1_GENERALIZEDTIME *producedAt, *thisUpdate, *nextUpdate; /*get url from cert or use some implicit value*/ /*get connection parameters out of url*/ if (!OCSP_parse_url(chosenurl, &host, &port, &path, &ssl)) { - result = MYPROXY_OCSPRESULT_ERROR_BADOCSPADDRESS; + result = CANL_OCSPRESULT_ERROR_BADOCSPADDRESS; goto end; } if (!(req = OCSP_REQUEST_new())) { - result = MYPROXY_OCSPRESULT_ERROR_OUTOFMEMORY; + result = CANL_OCSPRESULT_ERROR_OUTOFMEMORY; goto end; } - id = OCSP_cert_to_id(0, cert, issuer); + id = OCSP_cert_to_id(0, data->cert, data->issuer); /* Add id and nonce*/ if (!id || !OCSP_request_add0_id(req, id)) goto end; - //OCSP_request_add1_nonce(req, 0, -1); Is it necessery? - - - /*make new canl cc and io and init client*/ + if (USENONCE) + OCSP_request_add1_nonce(req, 0, -1); /* sign the request */ - if (sign_cert && sign_key && - !OCSP_request_sign(req, sign_cert, sign_key, EVP_sha1(), 0, 0)) { - result = MYPROXY_OCSPRESULT_ERROR_SIGNFAILURE; + if (data->sign_cert && data->sign_key && + !OCSP_request_sign(req, data->sign_cert, data->sign_key, + EVP_sha1(), 0, 0)) { + result = CANL_OCSPRESULT_ERROR_SIGNFAILURE; + goto end; + } + + ctx = SSL_CTX_new(SSLv3_client_method()); + if (ctx == NULL) { + result = CANL_OCSPRESULT_ERROR_OUTOFMEMORY; goto end; } + //SSL_CTX_set_cert_store(ctx, store); + SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL); /* establish a connection to the OCSP responder */ - if (!(result = ssl_connect(cc, io, ))) { - result = MYPROXY_OCSPRESULT_ERROR_CONNECTFAILURE; + if (!(bio = my_connect(host, atoi(port), ssl, &ctx))) { + result = CANL_OCSPRESULT_ERROR_CONNECTFAILURE; goto end; } @@ -1062,44 +1314,54 @@ int do_ocsp_verify ((X509 *cert, X509 *issuer)) if ((rc = OCSP_response_status(resp)) != OCSP_RESPONSE_STATUS_SUCCESSFUL) { switch (rc) { case OCSP_RESPONSE_STATUS_MALFORMEDREQUEST: - result = MYPROXY_OCSPRESULT_ERROR_MALFORMEDREQUEST; break; + result = CANL_OCSPRESULT_ERROR_MALFORMEDREQUEST; break; case OCSP_RESPONSE_STATUS_INTERNALERROR: - result = MYPROXY_OCSPRESULT_ERROR_INTERNALERROR; break; + result = CANL_OCSPRESULT_ERROR_INTERNALERROR; break; case OCSP_RESPONSE_STATUS_TRYLATER: - result = MYPROXY_OCSPRESULT_ERROR_TRYLATER; break; + result = CANL_OCSPRESULT_ERROR_TRYLATER; break; case OCSP_RESPONSE_STATUS_SIGREQUIRED: - result = MYPROXY_OCSPRESULT_ERROR_SIGREQUIRED; break; + result = CANL_OCSPRESULT_ERROR_SIGREQUIRED; break; case OCSP_RESPONSE_STATUS_UNAUTHORIZED: - result = MYPROXY_OCSPRESULT_ERROR_UNAUTHORIZED; break; + result = CANL_OCSPRESULT_ERROR_UNAUTHORIZED; break; } goto end; } /* verify the response */ - result = MYPROXY_OCSPRESULT_ERROR_INVALIDRESPONSE; - if (!(basic = OCSP_response_get1_basic(resp))) goto end; - if (usenonce && OCSP_check_nonce(req, basic) <= 0) goto end; - - if (!responder_cert || - (rc = OCSP_basic_verify(basic, responder_cert, store, - OCSP_TRUSTOTHER)) <= 0) + result = CANL_OCSPRESULT_ERROR_INVALIDRESPONSE; + if (!(basic = OCSP_response_get1_basic(resp))) + goto end; + if (USENONCE && OCSP_check_nonce(req, basic) <= 0) + goto end; + /*TODO make the store*/ + if (data->store && !(store = canl_create_x509store(data->store))) + goto end; + /*TODO check the second parametr (responder_cert) and the last one*/ + if ((rc = OCSP_basic_verify(basic, 0, store, 0)) <= 0) if ((rc = OCSP_basic_verify(basic, NULL, store, 0)) <= 0) goto end; if (!OCSP_resp_find_status(basic, id, &status, &reason, &producedAt, &thisUpdate, &nextUpdate)) goto end; - if (!OCSP_check_validity(thisUpdate, nextUpdate, skew, maxage)) + if (!OCSP_check_validity(thisUpdate, nextUpdate, data->skew, data->maxage)) goto end; /* All done. Set the return code based on the status from the response. */ if (status == V_OCSP_CERTSTATUS_REVOKED) { - result = MYPROXY_OCSPRESULT_CERTIFICATE_REVOKED; - myproxy_log("OCSP status revoked!"); + result = CANL_OCSPRESULT_CERTIFICATE_REVOKED; + /*TODO myproxy_log("OCSP status revoked!"); */ } else { - result = MYPROXY_OCSPRESULT_CERTIFICATE_VALID; - myproxy_log("OCSP status valid"); + result = CANL_OCSPRESULT_CERTIFICATE_VALID; + /*TODO myproxy_log("OCSP status valid"); */ } +end: + /*TODO check what's this + if (result < 0 && result != CANL_OCSPRESULT_ERROR_NOTCONFIGURED) { + ssl_error_to_verror(); + TODO myproxy_log("OCSP check failed"); + myproxy_log_verror(); + } */ if (host) OPENSSL_free(host); if (port) OPENSSL_free(port); @@ -1108,13 +1370,50 @@ int do_ocsp_verify ((X509 *cert, X509 *issuer)) if (resp) OCSP_RESPONSE_free(resp); if (basic) OCSP_BASICRESP_free(basic); if (ctx) SSL_CTX_free(ctx); /* this does X509_STORE_free(store) */ - if (certdir) free(certdir); - if (aiaocspurl) free(aiaocspurl); return 0; } -#endif +static BIO * +my_connect_ssl(char *host, int port, SSL_CTX **ctx) { + BIO *conn = 0; + + if (!(conn = BIO_new_ssl_connect(*ctx))) goto error_exit; + BIO_set_conn_hostname(conn, host); + BIO_set_conn_int_port(conn, &port); + + if (BIO_do_connect(conn) <= 0) goto error_exit; + return conn; + +error_exit: + if (conn) BIO_free_all(conn); + return 0; +} + +static BIO * +my_connect(char *host, int port, int ssl, SSL_CTX **ctx) { + BIO *conn; + SSL *ssl_ptr; + + if (ssl) { + if (!(conn = my_connect_ssl(host, port, ctx))) goto error_exit; + BIO_get_ssl(conn, &ssl_ptr); + /*TODO figure out, how to check cert without canl_ctx + if (!check_hostname_cert(SSL_get_peer_certificate(ssl_ptr), host)) + goto error_exit;*/ + if (SSL_get_verify_result(ssl_ptr) != X509_V_OK) goto error_exit; + return conn; + } + + if (!(conn = BIO_new_connect(host))) goto error_exit; + BIO_set_conn_int_port(conn, &port); + if (BIO_do_connect(conn) <= 0) goto error_exit; + return conn; + +error_exit: + if (conn) BIO_free_all(conn); + return 0; +} mech_glb_ctx ssl_glb_ctx; -- 1.8.2.3