📄 ssl_engine_io.c
字号:
int shutdown_type; if (!ssl) { return APR_SUCCESS; } /* * Now close the SSL layer of the connection. We've to take * the TLSv1 standard into account here: * * | 7.2.1. Closure alerts * | * | The client and the server must share knowledge that the connection is * | ending in order to avoid a truncation attack. Either party may * | initiate the exchange of closing messages. * | * | close_notify * | This message notifies the recipient that the sender will not send * | any more messages on this connection. The session becomes * | unresumable if any connection is terminated without proper * | close_notify messages with level equal to warning. * | * | Either party may initiate a close by sending a close_notify alert. * | Any data received after a closure alert is ignored. * | * | Each party is required to send a close_notify alert before closing * | the write side of the connection. It is required that the other party * | respond with a close_notify alert of its own and close down the * | connection immediately, discarding any pending writes. It is not * | required for the initiator of the close to wait for the responding * | close_notify alert before closing the read side of the connection. * * This means we've to send a close notify message, but haven't to wait * for the close notify of the client. Actually we cannot wait for the * close notify of the client because some clients (including Netscape * 4.x) don't send one, so we would hang. */ /* * exchange close notify messages, but allow the user * to force the type of handshake via SetEnvIf directive */ if (abortive) { shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN; type = "abortive"; } else switch (sslconn->shutdown_type) { case SSL_SHUTDOWN_TYPE_UNCLEAN: /* perform no close notify handshake at all (violates the SSL/TLS standard!) */ shutdown_type = SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN; type = "unclean"; break; case SSL_SHUTDOWN_TYPE_ACCURATE: /* send close notify and wait for clients close notify (standard compliant, but usually causes connection hangs) */ shutdown_type = 0; type = "accurate"; break; default: /* * case SSL_SHUTDOWN_TYPE_UNSET: * case SSL_SHUTDOWN_TYPE_STANDARD: */ /* send close notify, but don't wait for clients close notify (standard compliant and safe, so it's the DEFAULT!) */ shutdown_type = SSL_RECEIVED_SHUTDOWN; type = "standard"; break; } SSL_set_shutdown(ssl, shutdown_type); SSL_smart_shutdown(ssl); /* and finally log the fact that we've closed the connection */ if (c->base_server->loglevel >= APLOG_INFO) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "Connection closed to child %ld with %s shutdown " "(server %s)", c->id, type, ssl_util_vhostid(c->pool, c->base_server)); } /* deallocate the SSL connection */ if (sslconn->client_cert) { X509_free(sslconn->client_cert); sslconn->client_cert = NULL; } SSL_free(ssl); sslconn->ssl = NULL; filter_ctx->pssl = NULL; /* so filters know we've been shutdown */ if (abortive) { /* prevent any further I/O */ c->aborted = 1; } return APR_SUCCESS;}static apr_status_t ssl_io_filter_cleanup(void *data){ ssl_filter_ctx_t *filter_ctx = data; if (filter_ctx->pssl) { conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl); SSLConnRec *sslconn = myConnConfig(c); SSL_free(filter_ctx->pssl); sslconn->ssl = filter_ctx->pssl = NULL; } return APR_SUCCESS;}/* * The hook is NOT registered with ap_hook_process_connection. Instead, it is * called manually from the churn () before it tries to read any data. * There is some problem if I accept conn_rec *. Still investigating.. * Adv. if conn_rec * can be accepted is we can hook this function using the * ap_hook_process_connection hook. */static int ssl_io_filter_connect(ssl_filter_ctx_t *filter_ctx){ conn_rec *c = (conn_rec *)SSL_get_app_data(filter_ctx->pssl); SSLConnRec *sslconn = myConnConfig(c); SSLSrvConfigRec *sc = mySrvConfig(c->base_server); X509 *cert; int n; int ssl_err; long verify_result; if (SSL_is_init_finished(filter_ctx->pssl)) { return APR_SUCCESS; } if (sslconn->is_proxy) { if ((n = SSL_connect(filter_ctx->pssl)) <= 0) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "SSL Proxy connect failed"); ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); /* ensure that the SSL structures etc are freed, etc: */ ssl_filter_io_shutdown(filter_ctx, c, 1); return HTTP_BAD_GATEWAY; } return APR_SUCCESS; } if ((n = SSL_accept(filter_ctx->pssl)) <= 0) { bio_filter_in_ctx_t *inctx = (bio_filter_in_ctx_t *) (filter_ctx->pbioRead->ptr); bio_filter_out_ctx_t *outctx = (bio_filter_out_ctx_t *) (filter_ctx->pbioWrite->ptr); apr_status_t rc = inctx->rc ? inctx->rc : outctx->rc ; ssl_err = SSL_get_error(filter_ctx->pssl, n); if (ssl_err == SSL_ERROR_ZERO_RETURN) { /* * The case where the connection was closed before any data * was transferred. That's not a real error and can occur * sporadically with some clients. */ ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, "SSL handshake stopped: connection was closed"); } else if (ssl_err == SSL_ERROR_WANT_READ) { /* * This is in addition to what was present earlier. It is * borrowed from openssl_state_machine.c [mod_tls]. * TBD. */ outctx->rc = APR_EAGAIN; return SSL_ERROR_WANT_READ; } else if (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL && ERR_GET_REASON(ERR_peek_error()) == SSL_R_HTTP_REQUEST) { /* * The case where OpenSSL has recognized a HTTP request: * This means the client speaks plain HTTP on our HTTPS port. * ssl_io_filter_error will disable the ssl filters when it * sees this status code. */ return HTTP_BAD_REQUEST; } else if (ssl_err == SSL_ERROR_SYSCALL) { ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, "SSL handshake interrupted by system " "[Hint: Stop button pressed in browser?!]"); } else /* if (ssl_err == SSL_ERROR_SSL) */ { /* * Log SSL errors and any unexpected conditions. */ ap_log_cerror(APLOG_MARK, APLOG_INFO, rc, c, "SSL library error %d in handshake " "(server %s)", ssl_err, ssl_util_vhostid(c->pool, c->base_server)); ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); } if (inctx->rc == APR_SUCCESS) { inctx->rc = APR_EGENERAL; } return ssl_filter_io_shutdown(filter_ctx, c, 1); } /* * Check for failed client authentication */ verify_result = SSL_get_verify_result(filter_ctx->pssl); if ((verify_result != X509_V_OK) || sslconn->verify_error) { if (ssl_verify_error_is_optional(verify_result) && (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA)) { /* leaving this log message as an error for the moment, * according to the mod_ssl docs: * "level optional_no_ca is actually against the idea * of authentication (but can be used to establish * SSL test pages, etc.)" * optional_no_ca doesn't appear to work as advertised * in 1.x */ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "SSL client authentication failed, " "accepting certificate based on " "\"SSLVerifyClient optional_no_ca\" " "configuration"); ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); } else { const char *error = sslconn->verify_error ? sslconn->verify_error : X509_verify_cert_error_string(verify_result); ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "SSL client authentication failed: %s", error ? error : "unknown"); ssl_log_ssl_error(APLOG_MARK, APLOG_INFO, c->base_server); return ssl_filter_io_shutdown(filter_ctx, c, 1); } } /* * Remember the peer certificate's DN */ if ((cert = SSL_get_peer_certificate(filter_ctx->pssl))) { if (sslconn->client_cert) { X509_free(sslconn->client_cert); } sslconn->client_cert = cert; sslconn->client_dn = NULL; } /* * Make really sure that when a peer certificate * is required we really got one... (be paranoid) */ if ((sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE) && !sslconn->client_cert) { ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, c, "No acceptable peer certificate available"); return ssl_filter_io_shutdown(filter_ctx, c, 1); } return APR_SUCCESS;}#define SWITCH_STATUS_LINE "HTTP/1.1 101 Switching Protocols"#define UPGRADE_HEADER "Upgrade: TLS/1.0, HTTP/1.1"#define CONNECTION_HEADER "Connection: Upgrade"static apr_status_t ssl_io_filter_Upgrade(ap_filter_t *f, apr_bucket_brigade *bb){ const char *upgrade; apr_bucket_brigade *upgradebb; request_rec *r = f->r; SSLConnRec *sslconn; apr_status_t rv; apr_bucket *b; SSL *ssl; /* Just remove the filter, if it doesn't work the first time, it won't * work at all for this request. */ ap_remove_output_filter(f); /* No need to ensure that this is a server with optional SSL, the filter * is only inserted if that is true. */ upgrade = apr_table_get(r->headers_in, "Upgrade"); if (upgrade == NULL || strcmp(ap_getword(r->pool, &upgrade, ','), "TLS/1.0")) { /* "Upgrade: TLS/1.0, ..." header not found, don't do Upgrade */ return ap_pass_brigade(f->next, bb); } apr_table_unset(r->headers_out, "Upgrade"); /* Send the interim 101 response. */ upgradebb = apr_brigade_create(r->pool, f->c->bucket_alloc); ap_fputstrs(f->next, upgradebb, SWITCH_STATUS_LINE, CRLF, UPGRADE_HEADER, CRLF, CONNECTION_HEADER, CRLF, CRLF, NULL); b = apr_bucket_flush_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(upgradebb, b); rv = ap_pass_brigade(f->next, upgradebb); if (rv) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "could not send interim 101 Upgrade response"); return AP_FILTER_ERROR; } ssl_init_ssl_connection(f->c); ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "Awaiting re-negotiation handshake"); sslconn = myConnConfig(f->c); ssl = sslconn->ssl; /* XXX: Should replace SSL_set_state with SSL_renegotiate(ssl); * However, this causes failures in perl-framework currently, * perhaps pre-test if we have already negotiated? */ SSL_set_accept_state(ssl); SSL_do_handshake(ssl); if (SSL_get_state(ssl) != SSL_ST_OK) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "TLS Upgrade handshake failed: " "Not accepted by client!?"); return AP_FILTER_ERROR; } /* Now that we have initialized the ssl connection which added the ssl_io_filter, pass the brigade off to the connection based output filters so that the request can complete encrypted */ return ap_pass_brigade(f->c->output_filters, bb);}static apr_status_t ssl_io_filter_input(ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes){ apr_status_t status; bio_filter_in_ctx_t *inctx = f->ctx; apr_size_t len = sizeof(inctx->buffer); int is_init = (mode == AP_MODE_INIT); if (f->c->aborted) { /* XXX: Ok, if we aborted, we ARE at the EOS. We also have * aborted. This 'double protection' is probably redundant, * but also effective against just about anything. */ apr_bucket *bucket = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, bucket); return APR_ECONNABORTED; } if (!inctx->ssl) { return ap_get_brigade(f->next, bb, mode, block, readbytes); } /* XXX: we don't currently support anything other than these modes. */ if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE && mode != AP_MODE_SPECULATIVE && mode != AP_MODE_INIT) { return APR_ENOTIMPL; } inctx->mode = mode; inctx->block = block; /* XXX: we could actually move ssl_io_filter_connect to an * ap_hook_process_connection but would still need to call it for * AP_MODE_INIT for protocols that may upgrade the connection * rather than have SSLEngine On configured. */ if ((status = ssl_io_filter_connect(inctx->filter_ctx)) != APR_SUCCESS) { return ssl_io_filter_error(f, bb, status); } if (is_init) { /* protocol module needs to handshake before sending * data to client (e.g. NNTP or FTP) */ return APR_SUCCESS; } if (inctx->mode == AP_MODE_READBYTES || inctx->mode == AP_MODE_SPECULATIVE) { /* Protected from truncation, readbytes < MAX_SIZE_T * FIXME: No, it's *not* protected. -- jre */ if (readbytes < len) { len = (apr_size_t)readbytes; } status = ssl_io_input_read(inctx, inctx->buffer, &len); } else if (inctx->mode == AP_MODE_GETLINE) { status = ssl_io_input_getline(inctx, inctx->buffer, &len); } else { /* We have no idea what you are talking about, so return an error. */ return APR_ENOTIMPL; } if (status != APR_SUCCESS) { return ssl_io_filter_error(f, bb, status); } /* Create a transient bucket out of the decrypted data. */ if (len > 0) { apr_bucket *bucket = apr_bucket_transient_create(inctx->buffer, len, f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, bucket); } return APR_SUCCESS;}static apr_status_t ssl_io_filter_output(ap_filter_t *f, apr_bucket_brigade *bb){ apr_status_t status = APR_SUCCESS; ssl_filter_ctx_t *filter_ctx = f->ctx; bio_filter_in_ctx_t *inctx; bio_filter_out_ctx_t *outctx; apr_read_type_e rblock = APR_NONBLOCK_READ; if (f->c->aborted) { apr_brigade_cleanup(bb); return APR_ECONNABORTED; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -