BUG/MEDIUM: ssl: Fix sometimes reneg fails if requested by server.

SSL_do_handshake is not appropriate for reneg, it's only appropriate at the
beginning of a connection. OpenSSL correctly handles renegs using the data
functions, so we use SSL_peek() here to make its state machine progress if
SSL_renegotiate_pending() says a reneg is pending.
This commit is contained in:
Emeric Brun 2012-11-08 19:21:55 +01:00 committed by Willy Tarreau
parent 282a76acc1
commit 674b743067

View File

@ -855,6 +855,61 @@ int ssl_sock_handshake(struct connection *conn, unsigned int flag)
if (!conn->xprt_ctx)
goto out_error;
/* If we use SSL_do_handshake to process a reneg initiated by
* the remote peer, it sometimes returns SSL_ERROR_SSL.
* Usually SSL_write and SSL_read are used and process implicitly
* the reneg handshake.
* Here we use SSL_peek as a workaround for reneg.
*/
if ((conn->flags & CO_FL_CONNECTED) && SSL_renegotiate_pending(conn->xprt_ctx)) {
char c;
ret = SSL_peek(conn->xprt_ctx, &c, 1);
if (ret <= 0) {
/* handshake may have not been completed, let's find why */
ret = SSL_get_error(conn->xprt_ctx, ret);
if (ret == SSL_ERROR_WANT_WRITE) {
/* SSL handshake needs to write, L4 connection may not be ready */
__conn_sock_stop_recv(conn);
__conn_sock_poll_send(conn);
return 0;
}
else if (ret == SSL_ERROR_WANT_READ) {
/* handshake may have been completed but we have
* no more data to read.
*/
if (!SSL_renegotiate_pending(conn->xprt_ctx)) {
ret = 1;
goto reneg_ok;
}
/* SSL handshake needs to read, L4 connection is ready */
if (conn->flags & CO_FL_WAIT_L4_CONN)
conn->flags &= ~CO_FL_WAIT_L4_CONN;
__conn_sock_stop_send(conn);
__conn_sock_poll_recv(conn);
return 0;
}
else if (ret == SSL_ERROR_SYSCALL) {
/* if errno is null, then connection was successfully established */
if (!errno && conn->flags & CO_FL_WAIT_L4_CONN)
conn->flags &= ~CO_FL_WAIT_L4_CONN;
goto out_error;
}
else {
/* Fail on all other handshake errors */
/* Note: OpenSSL may leave unread bytes in the socket's
* buffer, causing an RST to be emitted upon close() on
* TCP sockets. We first try to drain possibly pending
* data to avoid this as much as possible.
*/
ret = recv(conn->t.sock.fd, trash.str, trash.size, MSG_NOSIGNAL|MSG_DONTWAIT);
goto out_error;
}
}
/* read some data: consider handshake completed */
goto reneg_ok;
}
ret = SSL_do_handshake(conn->xprt_ctx);
if (ret != 1) {
/* handshake did not complete, let's find why */
@ -892,6 +947,8 @@ int ssl_sock_handshake(struct connection *conn, unsigned int flag)
}
}
reneg_ok:
/* Handshake succeeded */
if (objt_server(conn->target)) {
if (!SSL_session_reused(conn->xprt_ctx)) {