From e7f67d5a4c6e9f06aa7a9db1b4245f5e16f00bb2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 May 2018 11:16:34 +0100 Subject: [http] Work around stateful authentication schemes As pointedly documented in RFC7230 section 2.3, HTTP is a stateless protocol: each request message can be understood in isolation from any other requests or responses. Various authentication schemes such as NTLM break this fundamental property of HTTP and rely on the same TCP connection being reused. Work around these broken authentication schemes by ensuring that the most recently pooled connection is reused for the subsequent authentication retry. Reported-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/net/tcp/httpconn.c | 9 +++++++-- src/net/tcp/httpcore.c | 12 ++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/net/tcp/httpconn.c b/src/net/tcp/httpconn.c index 2cfca9c9..5121ff6c 100644 --- a/src/net/tcp/httpconn.c +++ b/src/net/tcp/httpconn.c @@ -252,8 +252,13 @@ int http_connect ( struct interface *xfer, struct uri *uri ) { /* Identify port */ port = uri_port ( uri, scheme->port ); - /* Look for a reusable connection in the pool */ - list_for_each_entry ( conn, &http_connection_pool, pool.list ) { + /* Look for a reusable connection in the pool. Reuse the most + * recent connection in order to accommodate authentication + * schemes that break the stateless nature of HTTP and rely on + * the same connection being reused for authentication + * responses. + */ + list_for_each_entry_reverse ( conn, &http_connection_pool, pool.list ) { /* Sanity checks */ assert ( conn->uri != NULL ); diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index b3c9a00e..f755fb72 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -778,6 +778,18 @@ static int http_transfer_complete ( struct http_transaction *http ) { http->len = 0; assert ( http->remaining == 0 ); + /* Retry immediately if applicable. We cannot rely on an + * immediate timer expiry, since certain Microsoft-designed + * HTTP extensions such as NTLM break the fundamentally + * stateless nature of HTTP and rely on the same connection + * being reused for authentication. See RFC7230 section 2.3 + * for further details. + */ + if ( ! http->response.retry_after ) { + http_reopen ( http ); + return 0; + } + /* Start timer to initiate retry */ DBGC2 ( http, "HTTP %p retrying after %d seconds\n", http, http->response.retry_after ); -- cgit v1.2.3-55-g7522