void ssl_init_Engine(server_rec *s, apr_pool_t *p) { SSLModConfigRec *mc = myModConfig(s); ENGINE *e; if (mc->szCryptoDevice) { if (!(e = ENGINE_by_id(mc->szCryptoDevice))) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Init: Failed to load Crypto Device API `%s'", mc->szCryptoDevice); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); ssl_die(); } if (strEQ(mc->szCryptoDevice, "chil")) { ENGINE_ctrl(e, ENGINE_CTRL_CHIL_SET_FORKCHECK, 1, 0, 0); } if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Init: Failed to enable Crypto Device API `%s'", mc->szCryptoDevice); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); ssl_die(); } ap_log_error(APLOG_MARK, APLOG_INFO, 0, s, "Init: loaded Crypto Device API `%s'", mc->szCryptoDevice); ENGINE_free(e); } }
OCSP_RESPONSE *modssl_dispatch_ocsp_request(const apr_uri_t *uri, apr_interval_time_t timeout, OCSP_REQUEST *request, conn_rec *c, apr_pool_t *p) { OCSP_RESPONSE *response = NULL; apr_socket_t *sd; BIO *bio; bio = serialize_request(request, uri); if (bio == NULL) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01989) "could not serialize OCSP request"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, mySrvFromConn(c)); return NULL; } sd = send_request(bio, uri, timeout, c, p); if (sd == NULL) { /* Errors already logged. */ BIO_free(bio); return NULL; } /* Clear the BIO contents, ready for the response. */ (void)BIO_reset(bio); response = read_response(sd, bio, c, p); apr_socket_close(sd); BIO_free(bio); return response; }
STACK_OF(X509) *ssl_read_pkcs7(server_rec *s, const char *pkcs7) { PKCS7 *p7; STACK_OF(X509) *certs = NULL; FILE *f; f = fopen(pkcs7, "r"); if (!f) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02212) "Can't open %s", pkcs7); ssl_die(s); } p7 = PEM_read_PKCS7(f, NULL, NULL, NULL); if (!p7) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02274) "Can't read PKCS7 object %s", pkcs7); ssl_log_ssl_error(SSLLOG_MARK, APLOG_CRIT, s); exit(1); } switch (OBJ_obj2nid(p7->type)) { case NID_pkcs7_signed: certs = p7->d.sign->cert; p7->d.sign->cert = NULL; PKCS7_free(p7); break; case NID_pkcs7_signedAndEnveloped: certs = p7->d.signed_and_enveloped->cert; p7->d.signed_and_enveloped->cert = NULL; PKCS7_free(p7); break; default: ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02213) "Don't understand PKCS7 file %s", pkcs7); ssl_die(s); } if (!certs) { ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02214) "No certificates in %s", pkcs7); ssl_die(s); } fclose(f); return certs; }
/* Create an OCSP request for the given certificate; returning the * certificate ID in *certid and *issuer on success. Returns the * request object on success, or NULL on error. */ static OCSP_REQUEST *create_request(X509_STORE_CTX *ctx, X509 *cert, OCSP_CERTID **certid, server_rec *s, apr_pool_t *p, SSLSrvConfigRec *sc) { OCSP_REQUEST *req = OCSP_REQUEST_new(); *certid = OCSP_cert_to_id(NULL, cert, ctx->current_issuer); if (!*certid || !OCSP_request_add0_id(req, *certid)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01921) "could not retrieve certificate id"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s); return NULL; } if (sc->server->ocsp_use_request_nonce != FALSE) { OCSP_request_add1_nonce(req, 0, -1); } return req; }
int ssl_init_ssl_connection(conn_rec *c) { SSLSrvConfigRec *sc; SSL *ssl; SSLConnRec *sslconn = myConnConfig(c); char *vhost_md5; modssl_ctx_t *mctx; server_rec *server; if (!sslconn) { sslconn = ssl_init_connection_ctx(c); } server = sslconn->server; sc = mySrvConfig(server); /* * Seed the Pseudo Random Number Generator (PRNG) */ ssl_rand_seed(server, c->pool, SSL_RSCTX_CONNECT, ""); mctx = sslconn->is_proxy ? sc->proxy : sc->server; /* * Create a new SSL connection with the configured server SSL context and * attach this to the socket. Additionally we register this attachment * so we can detach later. */ if (!(ssl = SSL_new(mctx->ssl_ctx))) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, "Unable to create a new SSL connection from the SSL " "context"); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, server); c->aborted = 1; return DECLINED; /* XXX */ } vhost_md5 = ap_md5_binary(c->pool, (unsigned char *)sc->vhost_id, sc->vhost_id_len); if (!SSL_set_session_id_context(ssl, (unsigned char *)vhost_md5, APR_MD5_DIGESTSIZE*2)) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, "Unable to set session id context to `%s'", vhost_md5); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, server); c->aborted = 1; return DECLINED; /* XXX */ } SSL_set_app_data(ssl, c); SSL_set_app_data2(ssl, NULL); /* will be request_rec */ sslconn->ssl = ssl; /* * Configure callbacks for SSL connection */ SSL_set_tmp_rsa_callback(ssl, ssl_callback_TmpRSA); SSL_set_tmp_dh_callback(ssl, ssl_callback_TmpDH); SSL_set_verify_result(ssl, X509_V_OK); ssl_io_filter_init(c, ssl); return APR_SUCCESS; }
static void ssl_init_ctx_verify(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp, modssl_ctx_t *mctx) { SSL_CTX *ctx = mctx->ssl_ctx; int verify = SSL_VERIFY_NONE; STACK_OF(X509_NAME) *ca_list; if (mctx->auth.verify_mode == SSL_CVERIFY_UNSET) { mctx->auth.verify_mode = SSL_CVERIFY_NONE; } if (mctx->auth.verify_depth == UNSET) { mctx->auth.verify_depth = 1; } /* * Configure callbacks for SSL context */ if (mctx->auth.verify_mode == SSL_CVERIFY_REQUIRE) { verify |= SSL_VERIFY_PEER_STRICT; } if ((mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL) || (mctx->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA)) { verify |= SSL_VERIFY_PEER; } SSL_CTX_set_verify(ctx, verify, ssl_callback_SSLVerify); /* * Configure Client Authentication details */ if (mctx->auth.ca_cert_file || mctx->auth.ca_cert_path) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Configuring client authentication"); if (!SSL_CTX_load_verify_locations(ctx, MODSSL_PCHAR_CAST mctx->auth.ca_cert_file, MODSSL_PCHAR_CAST mctx->auth.ca_cert_path)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Unable to configure verify locations " "for client authentication"); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, s); ssl_die(); } if (mctx->pks && (mctx->pks->ca_name_file || mctx->pks->ca_name_path)) { ca_list = ssl_init_FindCAList(s, ptemp, mctx->pks->ca_name_file, mctx->pks->ca_name_path); } else ca_list = ssl_init_FindCAList(s, ptemp, mctx->auth.ca_cert_file, mctx->auth.ca_cert_path); if (!ca_list) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Unable to determine list of acceptable " "CA certificates for client authentication"); ssl_die(); } SSL_CTX_set_client_CA_list(ctx, (STACK *)ca_list); } /* * Give a warning when no CAs were configured but client authentication * should take place. This cannot work. */ if (mctx->auth.verify_mode == SSL_CVERIFY_REQUIRE) { ca_list = (STACK_OF(X509_NAME) *)SSL_CTX_get_client_CA_list(ctx); if (sk_X509_NAME_num(ca_list) == 0) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "Init: Oops, you want to request client " "authentication, but no CAs are known for " "verification!? [Hint: SSLCACertificate*]"); } }
/* Read the OCSP response from the socket 'sd', using temporary memory * BIO 'bio', and return the decoded OCSP response object, or NULL on * error. */ static OCSP_RESPONSE *read_response(apr_socket_t *sd, BIO *bio, conn_rec *c, apr_pool_t *p) { apr_bucket_brigade *bb, *tmpbb; OCSP_RESPONSE *response; char *line; apr_size_t count; apr_int64_t code; /* Using brigades for response parsing is much simpler than using * apr_socket_* directly. */ bb = apr_brigade_create(p, c->bucket_alloc); tmpbb = apr_brigade_create(p, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_socket_create(sd, c->bucket_alloc)); line = get_line(tmpbb, bb, c, p); if (!line || strncmp(line, "HTTP/", 5) || (line = ap_strchr(line, ' ')) == NULL || (code = apr_atoi64(++line)) < 200 || code > 299) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01980) "bad response from OCSP server: %s", line ? line : "(none)"); return NULL; } /* Read till end of headers; don't have to even bother parsing the * Content-Length since the server is obliged to close the * connection after the response anyway for HTTP/1.0. */ count = 0; while ((line = get_line(tmpbb, bb, c, p)) != NULL && line[0] && ++count < MAX_HEADERS) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01981) "OCSP response header: %s", line); } if (count == MAX_HEADERS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01982) "could not read response headers from OCSP server, " "exceeded maximum count (%u)", MAX_HEADERS); return NULL; } else if (!line) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01983) "could not read response header from OCSP server"); return NULL; } /* Read the response body into the memory BIO. */ count = 0; while (!APR_BRIGADE_EMPTY(bb)) { const char *data; apr_size_t len; apr_status_t rv; apr_bucket *e = APR_BRIGADE_FIRST(bb); rv = apr_bucket_read(e, &data, &len, APR_BLOCK_READ); if (rv == APR_EOF) { ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01984) "OCSP response: got EOF"); break; } if (rv != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01985) "error reading response from OCSP server"); return NULL; } if (len == 0) { /* Ignore zero-length buckets (possible side-effect of * line splitting). */ apr_bucket_delete(e); continue; } count += len; if (count > MAX_CONTENT) { ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(01986) "OCSP response size exceeds %u byte limit", MAX_CONTENT); return NULL; } ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01987) "OCSP response: got %" APR_SIZE_T_FMT " bytes, %" APR_SIZE_T_FMT " total", len, count); BIO_write(bio, data, (int)len); apr_bucket_delete(e); } apr_brigade_destroy(bb); apr_brigade_destroy(tmpbb); /* Finally decode the OCSP response from what's stored in the * bio. */ response = d2i_OCSP_RESPONSE_bio(bio, NULL); if (response == NULL) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01988) "failed to decode OCSP response data"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, mySrvFromConn(c)); } return response; }
/* Verify the OCSP status of given certificate. Returns * V_OCSP_CERTSTATUS_* result code. */ static int verify_ocsp_status(X509 *cert, X509_STORE_CTX *ctx, conn_rec *c, SSLSrvConfigRec *sc, server_rec *s, apr_pool_t *pool) { int rc = V_OCSP_CERTSTATUS_GOOD; OCSP_RESPONSE *response = NULL; OCSP_BASICRESP *basicResponse = NULL; OCSP_REQUEST *request = NULL; OCSP_CERTID *certID = NULL; apr_uri_t *ruri; ruri = determine_responder_uri(sc, cert, c, pool); if (!ruri) { return V_OCSP_CERTSTATUS_UNKNOWN; } request = create_request(ctx, cert, &certID, s, pool, sc); if (request) { apr_interval_time_t to = sc->server->ocsp_responder_timeout == UNSET ? apr_time_from_sec(DEFAULT_OCSP_TIMEOUT) : sc->server->ocsp_responder_timeout; response = modssl_dispatch_ocsp_request(ruri, to, request, c, pool); } if (!request || !response) { rc = V_OCSP_CERTSTATUS_UNKNOWN; } if (rc == V_OCSP_CERTSTATUS_GOOD) { int r = OCSP_response_status(response); if (r != OCSP_RESPONSE_STATUS_SUCCESSFUL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01922) "OCSP response not successful: %d", rc); rc = V_OCSP_CERTSTATUS_UNKNOWN; } } if (rc == V_OCSP_CERTSTATUS_GOOD) { basicResponse = OCSP_response_get1_basic(response); if (!basicResponse) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(01923) "could not retrieve OCSP basic response"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s); rc = V_OCSP_CERTSTATUS_UNKNOWN; } } if (rc == V_OCSP_CERTSTATUS_GOOD && sc->server->ocsp_use_request_nonce != FALSE && OCSP_check_nonce(request, basicResponse) != 1) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01924) "Bad OCSP responder answer (bad nonce)"); rc = V_OCSP_CERTSTATUS_UNKNOWN; } if (rc == V_OCSP_CERTSTATUS_GOOD) { /* TODO: allow flags configuration. */ if (OCSP_basic_verify(basicResponse, NULL, ctx->ctx, 0) != 1) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(01925) "failed to verify the OCSP response"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s); rc = V_OCSP_CERTSTATUS_UNKNOWN; } } if (rc == V_OCSP_CERTSTATUS_GOOD) { int reason = -1, status; ASN1_GENERALIZEDTIME *thisup = NULL, *nextup = NULL; rc = OCSP_resp_find_status(basicResponse, certID, &status, &reason, NULL, &thisup, &nextup); if (rc != 1) { ssl_log_cxerror(SSLLOG_MARK, APLOG_ERR, 0, c, cert, APLOGNO(02272) "failed to retrieve OCSP response status"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s); rc = V_OCSP_CERTSTATUS_UNKNOWN; } else { rc = status; } /* Check whether the response is inside the defined validity * period; otherwise fail. */ if (rc != V_OCSP_CERTSTATUS_UNKNOWN) { long resptime_skew = sc->server->ocsp_resptime_skew == UNSET ? DEFAULT_OCSP_MAX_SKEW : sc->server->ocsp_resptime_skew; /* oscp_resp_maxage can be passed verbatim - UNSET (-1) means * that responses can be of any age as long as nextup is in the * future. */ int vrc = OCSP_check_validity(thisup, nextup, resptime_skew, sc->server->ocsp_resp_maxage); if (vrc != 1) { ssl_log_cxerror(SSLLOG_MARK, APLOG_ERR, 0, c, cert, APLOGNO(02273) "OCSP response outside validity period"); ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, s); rc = V_OCSP_CERTSTATUS_UNKNOWN; } } { int level = (status == V_OCSP_CERTSTATUS_GOOD) ? APLOG_INFO : APLOG_ERR; const char *result = status == V_OCSP_CERTSTATUS_GOOD ? "good" : (status == V_OCSP_CERTSTATUS_REVOKED ? "revoked" : "unknown"); ssl_log_cxerror(SSLLOG_MARK, level, 0, c, cert, "OCSP validation completed, " "certificate status: %s (%d, %d)", result, status, reason); } } if (request) OCSP_REQUEST_free(request); if (response) OCSP_RESPONSE_free(response); if (basicResponse) OCSP_BASICRESP_free(basicResponse); /* certID is freed when the request is freed */ return rc; }