void nbbio_free(NBBIO *np) { nbbio_disable_readwrite(np); (void) close(np->fd); myfree(np->label); myfree(np->read_buf); myfree(np->write_buf); myfree((char *) np); }
static void tlsp_strategy(TLSP_STATE *state) { TLS_SESS_STATE *tls_context = state->tls_context; NBBIO *plaintext_buf; int ssl_stat; int ssl_read_err; int ssl_write_err; int handshake_err; /* * Be sure to complete the TLS handshake before enabling plain-text I/O. * In case of an unrecoverable error, this automagically cleans up all * pending read/write and timeout event requests. */ if (state->flags & TLSP_FLAG_DO_HANDSHAKE) { ssl_stat = SSL_accept(tls_context->con); if (ssl_stat != 1) { handshake_err = SSL_get_error(tls_context->con, ssl_stat); tlsp_eval_tls_error(state, handshake_err); /* At this point, state could be a dangling pointer. */ return; } if ((state->tls_context = tls_server_post_accept(tls_context)) == 0) { tlsp_state_free(state); return; } if ((state->req_flags & TLS_PROXY_FLAG_SEND_CONTEXT) != 0 && (attr_print(state->plaintext_stream, ATTR_FLAG_NONE, SEND_ATTR_FUNC(tls_proxy_context_print, (void *) state->tls_context), ATTR_TYPE_END) != 0 || vstream_fflush(state->plaintext_stream) != 0)) { msg_warn("cannot send TLS context: %m"); tlsp_state_free(state); return; } state->flags &= ~TLSP_FLAG_DO_HANDSHAKE; } /* * Shutdown and self-destruct after NBBIO error. This automagically * cleans up all pending read/write and timeout event requests. Before * shutting down TLS, we stop all plain-text I/O events but keep the * NBBIO error flags. */ plaintext_buf = state->plaintext_buf; if (NBBIO_ERROR_FLAGS(plaintext_buf)) { if (NBBIO_ACTIVE_FLAGS(plaintext_buf)) nbbio_disable_readwrite(state->plaintext_buf); ssl_stat = SSL_shutdown(tls_context->con); /* XXX Wait for return value 1 if sessions are to be reused? */ if (ssl_stat < 0) { handshake_err = SSL_get_error(tls_context->con, ssl_stat); tlsp_eval_tls_error(state, handshake_err); /* At this point, state could be a dangling pointer. */ return; } tlsp_state_free(state); return; } /* * Try to move data from the plaintext input buffer to the TLS engine. * * XXX We're supposed to repeat the exact same SSL_write() call arguments * after an SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE result. Rumor has * it that this is because each SSL_write() call reads from the buffer * incrementally, and returns > 0 only after the final byte is processed. * Rumor also has it that setting SSL_MODE_ENABLE_PARTIAL_WRITE and * SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER voids this requirement, and that * repeating the request with an increased request size is OK. * Unfortunately all this is not or poorly documented, and one has to * rely on statements from OpenSSL developers in public mailing archives. */ ssl_write_err = SSL_ERROR_NONE; while (NBBIO_READ_PEND(plaintext_buf) > 0) { ssl_stat = SSL_write(tls_context->con, NBBIO_READ_BUF(plaintext_buf), NBBIO_READ_PEND(plaintext_buf)); ssl_write_err = SSL_get_error(tls_context->con, ssl_stat); if (ssl_write_err != SSL_ERROR_NONE) break; /* Allow the plaintext pseudothread to read more data. */ NBBIO_READ_PEND(plaintext_buf) -= ssl_stat; if (NBBIO_READ_PEND(plaintext_buf) > 0) memmove(NBBIO_READ_BUF(plaintext_buf), NBBIO_READ_BUF(plaintext_buf) + ssl_stat, NBBIO_READ_PEND(plaintext_buf)); } /* * Try to move data from the TLS engine to the plaintext output buffer. * Note: data may arrive as a side effect of calling SSL_write(), * therefore we call SSL_read() after calling SSL_write(). * * XXX We're supposed to repeat the exact same SSL_read() call arguments * after an SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE result. This * supposedly means that our plaintext writer must not memmove() the * plaintext output buffer until after the SSL_read() call succeeds. For * now I'll ignore this, because 1) SSL_read() is documented to return * the bytes available, instead of returning > 0 only after the entire * buffer is processed like SSL_write() does; and 2) there is no "read" * equivalent of the SSL_R_BAD_WRITE_RETRY, SSL_MODE_ENABLE_PARTIAL_WRITE * or SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER features. */ ssl_read_err = SSL_ERROR_NONE; while (NBBIO_WRITE_PEND(state->plaintext_buf) < NBBIO_BUFSIZE(plaintext_buf)) { ssl_stat = SSL_read(tls_context->con, NBBIO_WRITE_BUF(plaintext_buf) + NBBIO_WRITE_PEND(state->plaintext_buf), NBBIO_BUFSIZE(plaintext_buf) - NBBIO_WRITE_PEND(state->plaintext_buf)); ssl_read_err = SSL_get_error(tls_context->con, ssl_stat); if (ssl_read_err != SSL_ERROR_NONE) break; NBBIO_WRITE_PEND(plaintext_buf) += ssl_stat; } /* * Try to enable/disable ciphertext read/write events. If SSL_write() was * satisfied, see if SSL_read() wants to do some work. In case of an * unrecoverable error, this automagically destroys the session state * after cleaning up all pending read/write and timeout event requests. */ if (tlsp_eval_tls_error(state, ssl_write_err != SSL_ERROR_NONE ? ssl_write_err : ssl_read_err) < 0) return; /* * Try to enable/disable plaintext read/write events. Basically, if we * have nothing to write to the postscreen(8) server, see if there is * something to read. If the write buffer is empty and the read buffer is * full, suspend plaintext I/O until conditions change (but keep the * timer active, as a safety mechanism in case ciphertext I/O gets * stuck). * * XXX In theory, if the client keeps writing fast enough then we would * never read from postscreen(8), and cause postscreen(8) to block. In * practice, postscreen(8) limits the number of client commands, and thus * postscreen(8)'s output will fit in a kernel buffer. This may not be * true in other scenarios where the tlsproxy(8) server could be used. */ if (NBBIO_WRITE_PEND(plaintext_buf) > 0) { if (NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_READ) nbbio_disable_readwrite(plaintext_buf); if ((NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_WRITE) == 0) nbbio_enable_write(plaintext_buf, state->timeout); } else if (NBBIO_READ_PEND(plaintext_buf) < NBBIO_BUFSIZE(plaintext_buf)) { if (NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_WRITE) nbbio_disable_readwrite(plaintext_buf); if ((NBBIO_ACTIVE_FLAGS(plaintext_buf) & NBBIO_FLAG_READ) == 0) nbbio_enable_read(plaintext_buf, state->timeout); } else { if (NBBIO_ACTIVE_FLAGS(plaintext_buf)) nbbio_slumber(plaintext_buf, state->timeout); } }