Exemple #1
0
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);
    }
}
 /*
  * This is the actual startup routine for a new connection. We expect that
  * the SMTP buffers are flushed and the "220 Ready to start TLS" was sent to
  * the client, so that we can immediately start the TLS handshake process.
  */
TLS_SESS_STATE *tls_server_start(const TLS_SERVER_START_PROPS *props)
{
    int     sts;
    TLS_SESS_STATE *TLScontext;
    const char *cipher_list;
    TLS_APPL_STATE *app_ctx = props->ctx;

    if (props->log_level >= 1)
	msg_info("setting up TLS connection from %s", props->namaddr);

    cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade,
				  props->cipher_exclusions);
    if (cipher_list == 0) {
	msg_warn("%s: %s: aborting TLS session", props->namaddr,
		 vstring_str(app_ctx->why));
	return (0);
    }
    if (props->log_level >= 2)
	msg_info("%s: TLS cipher list \"%s\"", props->namaddr, cipher_list);

    /*
     * Allocate a new TLScontext for the new connection and get an SSL
     * structure. Add the location of TLScontext to the SSL to later retrieve
     * the information inside the tls_verify_certificate_callback().
     */
    TLScontext = tls_alloc_sess_context(props->log_level, props->namaddr);
    TLScontext->cache_type = app_ctx->cache_type;

    TLScontext->serverid = mystrdup(props->serverid);
    TLScontext->am_server = 1;

    TLScontext->fpt_dgst = mystrdup(props->fpt_dgst);
    TLScontext->stream = props->stream;

    ERR_clear_error();
    if ((TLScontext->con = (SSL *) SSL_new(app_ctx->ssl_ctx)) == 0) {
	msg_warn("Could not allocate 'TLScontext->con' with SSL_new()");
	tls_print_errors();
	tls_free_context(TLScontext);
	return (0);
    }
    if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
	msg_warn("Could not set application data for 'TLScontext->con'");
	tls_print_errors();
	tls_free_context(TLScontext);
	return (0);
    }

    /*
     * Before really starting anything, try to seed the PRNG a little bit
     * more.
     */
    tls_int_seed();
    (void) tls_ext_seed(var_tls_daemon_rand_bytes);

    /*
     * Initialize the SSL connection to accept state. This should not be
     * necessary anymore since 0.9.3, but the call is still in the library
     * and maintaining compatibility never hurts.
     */
    SSL_set_accept_state(TLScontext->con);

    /*
     * Connect the SSL connection with the network socket.
     */
    if (SSL_set_fd(TLScontext->con, props->stream == 0 ? props->fd :
		   vstream_fileno(props->stream)) != 1) {
	msg_info("SSL_set_fd error to %s", props->namaddr);
	tls_print_errors();
	uncache_session(app_ctx->ssl_ctx, TLScontext);
	tls_free_context(TLScontext);
	return (0);
    }

    /*
     * If the debug level selected is high enough, all of the data is dumped:
     * 3 will dump the SSL negotiation, 4 will dump everything.
     * 
     * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called?
     * Well there is a BIO below the SSL routines that is automatically
     * created for us, so we can use it for debugging purposes.
     */
    if (props->log_level >= 3)
	BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb);

    /*
     * If we don't trigger the handshake in the library, leave control over
     * SSL_accept/read/write/etc with the application.
     */
    if (props->stream == 0)
	return (TLScontext);

    /*
     * Turn on non-blocking I/O so that we can enforce timeouts on network
     * I/O.
     */
    non_blocking(vstream_fileno(props->stream), NON_BLOCKING);

    /*
     * Start TLS negotiations. This process is a black box that invokes our
     * call-backs for session caching and certificate verification.
     * 
     * Error handling: If the SSL handhake fails, we print out an error message
     * and remove all TLS state concerning this session.
     */
    sts = tls_bio_accept(vstream_fileno(props->stream), props->timeout,
			 TLScontext);
    if (sts <= 0) {
	msg_info("SSL_accept error from %s: %d", props->namaddr, sts);
	tls_print_errors();
	tls_free_context(TLScontext);
	return (0);
    }
    return (tls_server_post_accept(TLScontext));
}