📄 ssl_engine_kernel.c
字号:
* override of SSLVerifyClient * * We force a renegotiation if the reconfigured/new verify type is * stronger than the currently active verify type. * * The order is: none << optional_no_ca << optional << require * * Additionally the following optimization is possible here: When the * currently active verify type is "none" but a client certificate is * already known/present, it's enough to manually force a client * verification but at least skip the I/O-intensive renegotation * handshake. */ if (dc->nVerifyClient != SSL_CVERIFY_UNSET) { /* remember old state */ verify_old = SSL_get_verify_mode(ssl); /* configure new state */ verify = SSL_VERIFY_NONE; if (dc->nVerifyClient == SSL_CVERIFY_REQUIRE) { verify |= SSL_VERIFY_PEER_STRICT; } if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) || (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA)) { verify |= SSL_VERIFY_PEER; } modssl_set_verify(ssl, verify, ssl_callback_SSLVerify); SSL_set_verify_result(ssl, X509_V_OK); /* determine whether we've to force a renegotiation */ if (!renegotiate && verify != verify_old) { if (((verify_old == SSL_VERIFY_NONE) && (verify != SSL_VERIFY_NONE)) || (!(verify_old & SSL_VERIFY_PEER) && (verify & SSL_VERIFY_PEER)) || (!(verify_old & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) && (verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) { renegotiate = TRUE; /* optimization */ if ((dc->nOptions & SSL_OPT_OPTRENEGOTIATE) && (verify_old == SSL_VERIFY_NONE) && ((peercert = SSL_get_peer_certificate(ssl)) != NULL)) { renegotiate_quick = TRUE; X509_free(peercert); } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Changed client verification type will force " "%srenegotiation", renegotiate_quick ? "quick " : ""); } } } /* * override SSLCACertificateFile & SSLCACertificatePath * This is only enabled if the SSL_set_cert_store() function * is available in the ssl library. the 1.x based mod_ssl * used SSL_CTX_set_cert_store which is not thread safe. */#ifdef HAVE_SSL_SET_CERT_STORE /* * check if per-dir and per-server config field are not the same. * if f is defined in per-dir and not defined in per-server * or f is defined in both but not the equal ... */#define MODSSL_CFG_NE(f) \ (dc->f && (!sc->f || (sc->f && strNE(dc->f, sc->f))))#define MODSSL_CFG_CA(f) \ (dc->f ? dc->f : sc->f) if (MODSSL_CFG_NE(szCACertificateFile) || MODSSL_CFG_NE(szCACertificatePath)) { STACK_OF(X509_NAME) *ca_list; const char *ca_file = MODSSL_CFG_CA(szCACertificateFile); const char *ca_path = MODSSL_CFG_CA(szCACertificatePath); cert_store = X509_STORE_new(); if (!X509_STORE_load_locations(cert_store, ca_file, ca_path)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Unable to reconfigure verify locations " "for client authentication"); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, r->server); X509_STORE_free(cert_store); return HTTP_FORBIDDEN; } /* SSL_free will free cert_store */ SSL_set_cert_store(ssl, cert_store); if (!(ca_list = ssl_init_FindCAList(r->server, r->pool, ca_file, ca_path))) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Unable to determine list of available " "CA certificates for client authentication"); return HTTP_FORBIDDEN; } SSL_set_client_CA_list(ssl, ca_list); renegotiate = TRUE; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Changed client verification locations will force " "renegotiation"); }#endif /* HAVE_SSL_SET_CERT_STORE */ /* * SSL renegotiations in conjunction with HTTP * requests using the POST method are not supported. * * Background: * * 1. When the client sends a HTTP/HTTPS request, Apache's core code * reads only the request line ("METHOD /path HTTP/x.y") and the * attached MIME headers ("Foo: bar") up to the terminating line ("CR * LF"). An attached request body (for instance the data of a POST * method) is _NOT_ read. Instead it is read by mod_cgi's content * handler and directly passed to the CGI script. * * 2. mod_ssl supports per-directory re-configuration of SSL parameters. * This is implemented by performing an SSL renegotiation of the * re-configured parameters after the request is read, but before the * response is sent. In more detail: the renegotiation happens after the * request line and MIME headers were read, but _before_ the attached * request body is read. The reason simply is that in the HTTP protocol * usually there is no acknowledgment step between the headers and the * body (there is the 100-continue feature and the chunking facility * only), so Apache has no API hook for this step. * * 3. the problem now occurs when the client sends a POST request for * URL /foo via HTTPS the server and the server has SSL parameters * re-configured on a per-URL basis for /foo. Then mod_ssl has to * perform an SSL renegotiation after the request was read and before * the response is sent. But the problem is the pending POST body data * in the receive buffer of SSL (which Apache still has not read - it's * pending until mod_cgi sucks it in). When mod_ssl now tries to perform * the renegotiation the pending data leads to an I/O error. * * Solution Idea: * * There are only two solutions: Either to simply state that POST * requests to URLs with SSL re-configurations are not allowed, or to * renegotiate really after the _complete_ request (i.e. including * the POST body) was read. Obviously the latter would be preferred, * but it cannot be done easily inside Apache, because as already * mentioned, there is no API step between the body reading and the body * processing. And even when we mod_ssl would hook directly into the * loop of mod_cgi, we wouldn't solve the problem for other handlers, of * course. So the only general solution is to suck in the pending data * of the request body from the OpenSSL BIO into the Apache BUFF. Then * the renegotiation can be done and after this step Apache can proceed * processing the request as before. * * Solution Implementation: * * We cannot simply suck in the data via an SSL_read-based loop because of * HTTP chunking. Instead we _have_ to use the Apache API for this step which * is aware of HTTP chunking. So the trick is to suck in the pending request * data via the Apache API (which uses Apache's BUFF code and in the * background mod_ssl's I/O glue code) and re-inject it later into the Apache * BUFF code again. This way the data flows twice through the Apache BUFF, of * course. But this way the solution doesn't depend on any Apache specifics * and is fully transparent to Apache modules. * * !! BUT ALL THIS IS STILL NOT RE-IMPLEMENTED FOR APACHE 2.0 !! */ if (renegotiate && !renegotiate_quick && (r->method_number == M_POST)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "SSL Re-negotiation in conjunction " "with POST method not supported! " "hint: try SSLOptions +OptRenegotiate"); return HTTP_METHOD_NOT_ALLOWED; } /* * now do the renegotiation if anything was actually reconfigured */ if (renegotiate) { /* * Now we force the SSL renegotation by sending the Hello Request * message to the client. Here we have to do a workaround: Actually * OpenSSL returns immediately after sending the Hello Request (the * intent AFAIK is because the SSL/TLS protocol says it's not a must * that the client replies to a Hello Request). But because we insist * on a reply (anything else is an error for us) we have to go to the * ACCEPT state manually. Using SSL_set_accept_state() doesn't work * here because it resets too much of the connection. So we set the * state explicitly and continue the handshake manually. */ ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "Requesting connection re-negotiation"); if (renegotiate_quick) { STACK_OF(X509) *cert_stack; /* perform just a manual re-verification of the peer */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Performing quick renegotiation: " "just re-verifying the peer"); cert_stack = (STACK_OF(X509) *)SSL_get_peer_cert_chain(ssl); cert = SSL_get_peer_certificate(ssl); if (!cert_stack && cert) { /* client cert is in the session cache, but there is * no chain, since ssl3_get_client_certificate() * sk_X509_shift-ed the peer cert out of the chain. * we put it back here for the purpose of quick_renegotiation. */ cert_stack = sk_new_null(); sk_X509_push(cert_stack, MODSSL_PCHAR_CAST cert); } if (!cert_stack || (sk_X509_num(cert_stack) == 0)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Cannot find peer certificate chain"); return HTTP_FORBIDDEN; } if (!(cert_store || (cert_store = SSL_CTX_get_cert_store(ctx)))) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Cannot find certificate storage"); return HTTP_FORBIDDEN; } if (!cert) { cert = sk_X509_value(cert_stack, 0); } X509_STORE_CTX_init(&cert_store_ctx, cert_store, cert, cert_stack); depth = SSL_get_verify_depth(ssl); if (depth >= 0) { X509_STORE_CTX_set_depth(&cert_store_ctx, depth); } X509_STORE_CTX_set_ex_data(&cert_store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx(), (char *)ssl); if (!modssl_X509_verify_cert(&cert_store_ctx)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Re-negotiation verification step failed"); ssl_log_ssl_error(APLOG_MARK, APLOG_ERR, r->server); } SSL_set_verify_result(ssl, cert_store_ctx.error); X509_STORE_CTX_cleanup(&cert_store_ctx); if (cert_stack != SSL_get_peer_cert_chain(ssl)) { /* we created this ourselves, so free it */ sk_X509_pop_free(cert_stack, X509_free); } } else { request_rec *id = r->main ? r->main : r; /* do a full renegotiation */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Performing full renegotiation: " "complete handshake protocol"); SSL_set_session_id_context(ssl, (unsigned char *)&id, sizeof(id)); SSL_renegotiate(ssl); SSL_do_handshake(ssl); if (SSL_get_state(ssl) != SSL_ST_OK) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Re-negotiation request failed"); r->connection->aborted = 1; return HTTP_FORBIDDEN; } ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server, "Awaiting re-negotiation handshake"); SSL_set_state(ssl, SSL_ST_ACCEPT); SSL_do_handshake(ssl); if (SSL_get_state(ssl) != SSL_ST_OK) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Re-negotiation handshake failed: " "Not accepted by client!?"); r->connection->aborted = 1; return HTTP_FORBIDDEN; } } /* * Remember the peer certificate's DN */ if ((cert = SSL_get_peer_certificate(ssl))) { if (sslconn->client_cert) { X509_free(sslconn->client_cert); } sslconn->client_cert = cert; sslconn->client_dn = NULL; } /* * Finally check for acceptable renegotiation results */ if (dc->nVerifyClient != SSL_CVERIFY_NONE) { BOOL do_verify = (dc->nVerifyClient == SSL_CVERIFY_REQUIRE); if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Re-negotiation handshake failed: " "Client verification failed"); return HTTP_FORBIDDEN; } if (do_verify) { if ((peercert = SSL_get_peer_certificate(ssl)) == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Re-negotiation handshake failed: " "Client certificate missing"); return HTTP_FORBIDDEN; } X509_free(peercert); } } /* * Also check that SSLCipherSuite has been enforced as expected. */ if (cipher_list) { cipher = SSL_get_current_cipher(ssl); if (sk_SSL_CIPHER_find(cipher_list, cipher) < 0) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "SSL cipher suite not renegotiated: " "access to %s denied using cipher %s", r->filename, SSL_CIPHER_get_name(cipher)); return HTTP_FORBIDDEN; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -