📄 ssl_engine_kernel.c
字号:
* 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 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 = 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 (nVerify != nVerifyOld) { if ( ( (nVerifyOld == SSL_VERIFY_NONE) && (nVerify != SSL_VERIFY_NONE)) || ( !(nVerifyOld & SSL_VERIFY_PEER) && (nVerify & SSL_VERIFY_PEER)) || ( !(nVerifyOld & (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) && (nVerify & (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))) { renegotiate = TRUE; /* optimization */ if ( dc->nOptions & SSL_OPT_OPTRENEGOTIATE && nVerifyOld == SSL_VERIFY_NONE && SSL_get_peer_certificate(ssl) != NULL) renegotiate_quick = TRUE; 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 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 */#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"); certstore = SSL_CTX_get_cert_store(ctx); if (certstore == NULL) { ssl_log(r->server, SSL_LOG_ERROR, "Cannot find certificate storage"); return FORBIDDEN; } certstack = SSL_get_peer_cert_chain(ssl); if (certstack == NULL || sk_X509_num(certstack) == 0) { ssl_log(r->server, SSL_LOG_ERROR, "Cannot find peer certificate chain"); return FORBIDDEN; } cert = sk_X509_value(certstack, 0); 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); } 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"); return FORBIDDEN; } ssl_log(r->server, SSL_LOG_INFO, "Awaiting re-negotiation handshake"); SSL_set_state(ssl, SSL_ST_ACCEPT); SSL_do_handshake(ssl); if (SSL_get_state(ssl) != SSL_ST_OK) { ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation handshake failed: Not accepted by client!?"); return FORBIDDEN; } } /* * Remember the peer certificate's DN */ if ((cert = SSL_get_peer_certificate(ssl)) != NULL) { cp = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); ap_ctx_set(r->connection->client->ctx, "ssl::client::dn", ap_pstrdup(r->connection->pool, cp)); free(cp); } /* * Finally check for acceptable renegotiation results */ if (dc->nVerifyClient != SSL_CVERIFY_NONE) { if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE && SSL_get_verify_result(ssl) != X509_V_OK ) { ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation handshake failed: Client verification failed"); return FORBIDDEN; } if ( dc->nVerifyClient == SSL_CVERIFY_REQUIRE && SSL_get_peer_certificate(ssl) == NULL ) { ssl_log(r->server, SSL_LOG_ERROR, "Re-negotiation handshake failed: Client certificate missing"); return FORBIDDEN; } } } /* * Under old OpenSSL we had to change the X509_STORE inside the * SSL_CTX instead inside the SSL structure, so we have to reconfigure it * to the old values. This should be changed with forthcoming OpenSSL * versions when better functionality is avaiable. */#ifdef SSL_EXPERIMENTAL if (renegotiate && reconfigured_locations) { if (!SSL_CTX_load_verify_locations(ctx, sc->szCACertificateFile, sc->szCACertificatePath)) { ssl_log(r->server, SSL_LOG_ERROR|SSL_ADD_SSLERR, "Unable to reconfigure verify locations " "to per-server configuration parameters"); return FORBIDDEN; } }#endif /* SSL_EXPERIMENTAL */ /* * Check SSLRequire boolean expressions */ apRequirement = dc->aRequirement; pRequirements = (ssl_require_t *)apRequirement->elts; for (i = 0; i < apRequirement->nelts; i++) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -