mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-10 09:07: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)
|
||||
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)) {
|
||||
|
Loading…
Reference in New Issue
Block a user