📄 ssl_engine_kernel.c
字号:
sc = mySrvConfig(r->server); ssl = ap_ctx_get(r->connection->client->ctx, "ssl"); if (ssl != NULL) ctx = SSL_get_SSL_CTX(ssl); /* * Support for SSLRequireSSL directive */ if (dc->bSSLRequired && ssl == NULL) { ap_log_reason("SSL connection required", r->filename, r); /* remember forbidden access for strict require option */ ap_table_setn(r->notes, "ssl-access-forbidden", (void *)1); return FORBIDDEN; } /* * Check to see if SSL protocol is on */ if (!sc->bEnabled) return DECLINED; if (ssl == NULL) return DECLINED; /* * Support for per-directory reconfigured SSL connection parameters. * * This is implemented by forcing an SSL renegotiation with the * reconfigured parameter suite. But Apache's internal API processing * makes our life very hard here, because when internal sub-requests occur * we nevertheless should avoid multiple unnecessary SSL handshakes (they * require extra network I/O and especially time to perform). * * But the optimization for filtering out the unnecessary handshakes isn't * obvious and trivial. Especially because while Apache is in its * sub-request processing the client could force additional handshakes, * too. And these take place perhaps without our notice. So the only * possibility is to explicitly _ask_ OpenSSL whether the renegotiation * has to be performed or not. It has to performed when some parameters * which were previously known (by us) are not those we've now * reconfigured (as known by OpenSSL) or (in optimized way) at least when * the reconfigured parameter suite is stronger (more restrictions) than * the currently active one. */ renegotiate = FALSE; renegotiate_quick = FALSE;#ifdef SSL_EXPERIMENTAL_PERDIRCA reconfigured_locations = FALSE;#endif /* * Override of SSLCipherSuite * * We provide two options here: * * o The paranoid and default approach where we force a renegotiation when * the cipher suite changed in _any_ way (which is straight-forward but * often forces renegotiations too often and is perhaps not what the * user actually wanted). * * o The optimized and still secure way where we force a renegotiation * only if the currently active cipher is no longer contained in the * reconfigured/new cipher suite. Any other changes are not important * because it's the servers choice to select a cipher from the ones the * client supports. So as long as the current cipher is still in the new * cipher suite we're happy. Because we can assume we would have * selected it again even when other (better) ciphers exists now in the * new cipher suite. This approach is fine because the user explicitly * has to enable this via ``SSLOptions +OptRenegotiate''. So we do no * implicit optimizations. */ if (dc->szCipherSuite != NULL) { /* remember old state */ pCipher = NULL; skCipherOld = NULL; if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) pCipher = SSL_get_current_cipher(ssl); else { skCipherOld = SSL_get_ciphers(ssl); if (skCipherOld != NULL) skCipherOld = sk_SSL_CIPHER_dup(skCipherOld); } /* configure new state */ if (!SSL_set_cipher_list(ssl, dc->szCipherSuite)) { ssl_log(r->server, SSL_LOG_WARN|SSL_ADD_SSLERR, "Unable to reconfigure (per-directory) permitted SSL ciphers"); if (skCipherOld != NULL) sk_SSL_CIPHER_free(skCipherOld); return FORBIDDEN; } /* determine whether a renegotiation has to be forced */ skCipher = SSL_get_ciphers(ssl); if (dc->nOptions & SSL_OPT_OPTRENEGOTIATE) { /* optimized way */ if ((pCipher == NULL && skCipher != NULL) || (pCipher != NULL && skCipher == NULL) ) renegotiate = TRUE; else if (pCipher != NULL && skCipher != NULL && sk_SSL_CIPHER_find(skCipher, pCipher) < 0) { renegotiate = TRUE; } } else { /* paranoid way */ if ((skCipherOld == NULL && skCipher != NULL) || (skCipherOld != NULL && skCipher == NULL) ) renegotiate = TRUE; else if (skCipherOld != NULL && skCipher != NULL) { for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipher); n++) { if (sk_SSL_CIPHER_find(skCipherOld, sk_SSL_CIPHER_value(skCipher, n)) < 0) renegotiate = TRUE; } for (n = 0; !renegotiate && n < sk_SSL_CIPHER_num(skCipherOld); n++) { if (sk_SSL_CIPHER_find(skCipher, sk_SSL_CIPHER_value(skCipherOld, n)) < 0) renegotiate = TRUE; } } } /* cleanup */ if (skCipherOld != NULL) sk_SSL_CIPHER_free(skCipherOld); /* tracing */ if (renegotiate) ssl_log(r->server, SSL_LOG_TRACE, "Reconfigured cipher suite will force renegotiation"); } /* * override of SSLVerifyDepth * * The depth checks are handled by us manually inside the verify callback * function and not by OpenSSL internally (and our function is aware of * both the per-server and per-directory contexts). So we cannot ask * OpenSSL about the currently verify depth. Instead we remember it in our * ap_ctx attached to the SSL* of OpenSSL. We've to force the * renegotiation if the reconfigured/new verify depth is less than the * currently active/remembered verify depth (because this means more * restriction on the certificate chain). */ if (dc->nVerifyDepth != UNSET) { apctx = SSL_get_app_data2(ssl); if ((vp = ap_ctx_get(apctx, "ssl::verify::depth")) != NULL) n = (int)AP_CTX_PTR2NUM(vp); else n = sc->nVerifyDepth; ap_ctx_set(apctx, "ssl::verify::depth", AP_CTX_NUM2PTR(dc->nVerifyDepth)); /* determine whether a renegotiation has to be forced */ if (dc->nVerifyDepth < n) { renegotiate = TRUE; ssl_log(r->server, SSL_LOG_TRACE, "Reduced client verification depth will force renegotiation"); } } /* * 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 */ nVerifyOld = SSL_get_verify_mode(ssl); /* configure new state */ nVerify = SSL_VERIFY_NONE; if (dc->nVerifyClient == SSL_CVERIFY_REQUIRE) nVerify |= SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT; if ( (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) || (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ) nVerify |= SSL_VERIFY_PEER; SSL_set_verify(ssl, nVerify, ssl_callback_SSLVerify); SSL_set_verify_result(ssl, X509_V_OK); /* determine whether we've to force a renegotiation */ if (!renegotiate && nVerify != nVerifyOld) { if ( ( (nVerifyOld == SSL_VERIFY_NONE) && (nVerify != SSL_VERIFY_NONE)) || ( !(nVerifyOld & SSL_VERIFY_PEER) && (nVerify & SSL_VERIFY_PEER)) || ( !(nVerifyOld & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) && (nVerify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) { renegotiate = TRUE; /* optimization */ if ( dc->nOptions & SSL_OPT_OPTRENEGOTIATE && nVerifyOld == SSL_VERIFY_NONE && (cert = SSL_get_peer_certificate(ssl)) != NULL) { renegotiate_quick = TRUE; X509_free(cert); } ssl_log(r->server, SSL_LOG_TRACE, "Changed client verification type will force %srenegotiation", renegotiate_quick ? "quick " : ""); } } } /* * override SSLCACertificateFile & SSLCACertificatePath * This is tagged experimental because it has to use an ugly kludge: We * have to change the locations inside the SSL_CTX* (per-server global) * instead inside SSL* (per-connection local) and reconfigure it to the * old values later. That's problematic at least for the threaded process * model of Apache under Win32 or when an error occurs. But unless * OpenSSL provides a SSL_load_verify_locations() function we've no other * chance to provide this functionality... */#ifdef SSL_EXPERIMENTAL_PERDIRCA if ( ( dc->szCACertificateFile != NULL && ( sc->szCACertificateFile == NULL || ( sc->szCACertificateFile != NULL && strNE(dc->szCACertificateFile, sc->szCACertificateFile)))) || ( dc->szCACertificatePath != NULL && ( sc->szCACertificatePath == NULL || ( sc->szCACertificatePath != NULL && strNE(dc->szCACertificatePath, sc->szCACertificatePath)))) ) { cpCAFile = dc->szCACertificateFile != NULL ? dc->szCACertificateFile : sc->szCACertificateFile; cpCAPath = dc->szCACertificatePath != NULL ? dc->szCACertificatePath : sc->szCACertificatePath; /* FIXME: This should be... if (!SSL_load_verify_locations(ssl, cpCAFile, cpCAPath)) { ...but OpenSSL still doesn't provide this! */ if (!SSL_CTX_load_verify_locations(ctx, cpCAFile, cpCAPath)) { ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Unable to reconfigure verify locations " "for client authentication"); return FORBIDDEN; } if ((skCAList = ssl_init_FindCAList(r->server, r->pool, cpCAFile, cpCAPath)) == NULL) { ssl_log(r->server, SSL_LOG_ERROR, "Unable to determine list of available " "CA certificates for client authentication"); return FORBIDDEN; } SSL_set_client_CA_list(ssl, skCAList); renegotiate = TRUE; reconfigured_locations = TRUE; ssl_log(r->server, SSL_LOG_TRACE, "Changed client verification locations will force renegotiation"); }#endif /* SSL_EXPERIMENTAL_PERDIRCA */#ifdef SSL_CONSERVATIVE /* * SSL renegotiations in conjunction with HTTP * requests using the POST method are not supported. */ if (renegotiate && r->method_number == M_POST) { ssl_log(r->server, SSL_LOG_ERROR, "SSL Re-negotiation in conjunction with POST method not supported!"); ssl_log(r->server, SSL_LOG_INFO, "You have to compile without -DSSL_CONSERVATIVE to enabled support for this."); return METHOD_NOT_ALLOWED; }#endif /* SSL_CONSERVATIVE */ /* * 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. */ ssl_log(r->server, SSL_LOG_INFO, "Requesting connection re-negotiation"); if (renegotiate_quick) { /* perform just a manual re-verification of the peer */ ssl_log(r->server, SSL_LOG_TRACE, "Performing quick renegotiation: just re-verifying the peer"); certstack = SSL_get_peer_cert_chain(ssl); cert = SSL_get_peer_certificate(ssl); if (certstack == NULL && cert != NULL) { /* client certificate is in the SSL session cache, but there is no chain, since ssl3_get_client_certificate() sk_X509_shift()'ed the peer certificate out of the chain. So we put it back here for the purpose of quick renegotiation. */ certstack = sk_new_null(); sk_X509_push(certstack, cert); } if (certstack == NULL || sk_X509_num(certstack) == 0) { ssl_log(r->server, SSL_LOG_ERROR, "Cannot find peer certificate chain"); return FORBIDDEN; } if (cert == NULL) cert = sk_X509_value(certstack, 0); if ((certstore = SSL_CTX_get_cert_store(ctx)) == NULL) { ssl_log(r->server, SSL_LOG_ERROR, "Cannot find certificate storage"); return FORBIDDEN; } X509_STORE_CTX_init(&certstorectx, certstore, cert, certstack); depth = SSL_get_verify_depth(ssl); if (depth >= 0) X509_STORE_CTX_set_depth(&certstorectx, depth); X509_STORE_CTX_set_ex_data(&certstorectx, SSL_get_ex_data_X509_STORE_CTX_idx(), (char *)ssl); if (!X509_verify_cert(&certstorectx)) ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Re-negotiation verification step failed"); SSL_set_verify_result(ssl, certstorectx.error); X509_STORE_CTX_cleanup(&certstorectx); if (SSL_get_peer_cert_chain(ssl) != certstack) { /* created by us above, so free it */ sk_X509_pop_free(certstack, X509_free); } else { /* X509_free(cert); not necessary AFAIK --rse */ } } else { /* do a full renegotiation */ ssl_log(r->server, SSL_LOG_TRACE, "Performing full renegotiation: complete handshake protocol"); if (r->main != NULL) SSL_set_session_id_context(ssl, (unsigned char *)&(r->main), sizeof(r->main)); else SSL_set_session_id_context(ssl, (unsigned char *)&r, sizeof(r));#ifndef SSL_CONSERVATIVE ssl_io_suck(r, ssl);#endif SSL_renegotiate(ssl); SSL_do_handshake(ssl); if (SSL_get_state(ssl) != SSL_ST_OK) { ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation request failed");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -