mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-13 10:37:02 +02:00
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:
parent
282a76acc1
commit
674b743067
@ -855,6 +855,61 @@ int ssl_sock_handshake(struct connection *conn, unsigned int flag)
|
|||||||
if (!conn->xprt_ctx)
|
if (!conn->xprt_ctx)
|
||||||
goto out_error;
|
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);
|
ret = SSL_do_handshake(conn->xprt_ctx);
|
||||||
if (ret != 1) {
|
if (ret != 1) {
|
||||||
/* handshake did not complete, let's find why */
|
/* 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 */
|
/* Handshake succeeded */
|
||||||
if (objt_server(conn->target)) {
|
if (objt_server(conn->target)) {
|
||||||
if (!SSL_session_reused(conn->xprt_ctx)) {
|
if (!SSL_session_reused(conn->xprt_ctx)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user