From d6a80a4833123fc8e051907ed059f47bb386d4a7 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Fri, 2 Aug 2019 11:01:37 +0200 Subject: [PATCH] winhttp: Handle Passport redirects. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/winhttp/request.c | 46 ++++++++++++++++++-- dlls/winhttp/session.c | 2 +- dlls/winhttp/tests/winhttp.c | 77 ++++++++++++++++++++++++++++++++++ dlls/winhttp/winhttp_private.h | 1 + 4 files changed, 122 insertions(+), 4 deletions(-) diff --git a/dlls/winhttp/request.c b/dlls/winhttp/request.c index 0e54168b796..175837db0bc 100644 --- a/dlls/winhttp/request.c +++ b/dlls/winhttp/request.c @@ -873,7 +873,7 @@ static enum auth_scheme scheme_from_flag( DWORD flag ) return SCHEME_INVALID; } -static DWORD auth_scheme_from_header( WCHAR *header ) +static DWORD auth_scheme_from_header( const WCHAR *header ) { unsigned int i; @@ -2628,7 +2628,7 @@ static WCHAR *get_redirect_url( struct request *request, DWORD *len ) WCHAR *ret; query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL ); - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE; + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return NULL; if (!(ret = heap_alloc( size ))) return NULL; *len = size / sizeof(WCHAR) - 1; if (query_headers( request, WINHTTP_QUERY_LOCATION, NULL, ret, &size, NULL )) return ret; @@ -2747,6 +2747,42 @@ end: return ret; } +static BOOL is_passport_request( struct request *request ) +{ + static const WCHAR passportW[] = {'P','a','s','s','p','o','r','t','1','.','4'}; + WCHAR buf[1024]; + DWORD len = ARRAY_SIZE(buf); + + if (!(request->connect->session->passport_flags & WINHTTP_ENABLE_PASSPORT_AUTH) || + !query_headers( request, WINHTTP_QUERY_WWW_AUTHENTICATE, NULL, buf, &len, NULL )) return FALSE; + + if (!strncmpiW( buf, passportW, ARRAY_SIZE(passportW) ) && + (buf[ARRAY_SIZE(passportW)] == ' ' || !buf[ARRAY_SIZE(passportW)])) return TRUE; + + return FALSE; +} + +static BOOL handle_passport_redirect( struct request *request ) +{ + static const WCHAR status401W[] = {'4','0','1',0}; + DWORD flags = WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE; + int i, len = strlenW( request->raw_headers ); + WCHAR *p = request->raw_headers; + + if (!process_header( request, attr_status, status401W, flags, FALSE )) return FALSE; + + for (i = 0; i < len; i++) + { + if (i <= len - 3 && p[i] == '3' && p[i + 1] == '0' && p[i + 2] == '2') + { + p[i] = '4'; + p[i + 2] = '1'; + break; + } + } + return TRUE; +} + static BOOL receive_response( struct request *request, BOOL async ) { BOOL ret; @@ -2774,7 +2810,11 @@ static BOOL receive_response( struct request *request, BOOL async ) if (!(request->hdr.disable_flags & WINHTTP_DISABLE_COOKIES)) record_cookies( request ); - if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB) + if (status == HTTP_STATUS_REDIRECT && is_passport_request( request )) + { + ret = handle_passport_redirect( request ); + } + else if (status == HTTP_STATUS_MOVED || status == HTTP_STATUS_REDIRECT || status == HTTP_STATUS_REDIRECT_KEEP_VERB) { if (request->hdr.disable_flags & WINHTTP_DISABLE_REDIRECTS || request->hdr.redirect_policy == WINHTTP_OPTION_REDIRECT_POLICY_NEVER) break; diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 239b86b17e6..ef4bb174699 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -210,7 +210,7 @@ static BOOL session_set_option( struct object_header *hdr, DWORD option, void *b return TRUE; case WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH: - FIXME("WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH: 0x%x\n", *(DWORD *)buffer); + session->passport_flags = *(DWORD *)buffer; return TRUE; case WINHTTP_OPTION_UNLOAD_NOTIFY_EVENT: diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 5849257e01c..b07563891b1 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -2236,6 +2236,13 @@ static const char largeauth[] = "Content-Type: text/plain\r\n" "\r\n"; +static const char passportauth[] = +"HTTP/1.1 302 Found\r\n" +"Content-Length: 0\r\n" +"Location: /\r\n" +"WWW-Authenticate: Passport1.4\r\n" +"\r\n"; + static const char unauthorized[] = "Unauthorized"; static const char hello_world[] = "Hello World"; static const char auth_unseen[] = "Auth Unseen"; @@ -2415,6 +2422,10 @@ static DWORD CALLBACK server_thread(LPVOID param) } else send(c, notokmsg, sizeof(notokmsg) - 1, 0); } + else if (strstr(buffer, "GET /passport")) + { + send(c, passportauth, sizeof(passportauth) - 1, 0); + } if (strstr(buffer, "GET /quit")) { send(c, okmsg, sizeof okmsg - 1, 0); @@ -3518,6 +3529,71 @@ static void test_connection_info( int port ) WinHttpCloseHandle( ses ); } +static void test_passport_auth( int port ) +{ + static const WCHAR passportW[] = + {'/','p','a','s','s','p','o','r','t',0}; + static const WCHAR foundW[] = + {'F','o','u','n','d',0}; + static const WCHAR unauthorizedW[] = + {'U','n','a','u','t','h','o','r','i','z','e','d',0}; + static const WCHAR headersW[] = + {'H','T','T','P','/','1','.','1',' ','4','0','1',' ','F','o','u','n','d','\r','\n', + 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','0','\r','\n', + 'L','o','c','a','t','i','o','n',':',' ','/','\r','\n', + 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',':',' ', + 'P','a','s','s','p','o','r','t','1','.','4','\r','\n','\r','\n',0}; + HINTERNET ses, con, req; + DWORD status, size, option; + WCHAR buf[128]; + BOOL ret; + + ses = WinHttpOpen( test_useragent, 0, NULL, NULL, 0 ); + ok( ses != NULL, "got %u\n", GetLastError() ); + + option = WINHTTP_ENABLE_PASSPORT_AUTH; + ret = WinHttpSetOption( ses, WINHTTP_OPTION_CONFIGURE_PASSPORT_AUTH, &option, sizeof(option) ); + ok( ret, "got %u\n", GetLastError() ); + + con = WinHttpConnect( ses, localhostW, port, 0 ); + ok( con != NULL, "got %u\n", GetLastError() ); + + req = WinHttpOpenRequest( con, NULL, passportW, NULL, NULL, NULL, 0 ); + ok( req != NULL, "got %u\n", GetLastError() ); + + ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 ); + ok( ret, "got %u\n", GetLastError() ); + + ret = WinHttpReceiveResponse( req, NULL ); + ok( ret, "got %u\n", GetLastError() ); + + status = 0xdeadbeef; + size = sizeof(status); + ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_CODE|WINHTTP_QUERY_FLAG_NUMBER, NULL, &status, &size, NULL ); + ok( ret, "got %u\n", GetLastError() ); + ok( status == HTTP_STATUS_DENIED, "got %u\n", status ); + + buf[0] = 0; + size = sizeof(buf); + ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_STATUS_TEXT, NULL, buf, &size, NULL ); + ok( ret, "got %u\n", GetLastError() ); + ok( !lstrcmpW(foundW, buf) || broken(!lstrcmpW(unauthorizedW, buf)) /* < win7 */, "got %s\n", wine_dbgstr_w(buf) ); + + buf[0] = 0; + size = sizeof(buf); + ret = WinHttpQueryHeaders( req, WINHTTP_QUERY_RAW_HEADERS_CRLF, NULL, buf, &size, NULL ); + ok( ret || broken(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER) /* < win7 */, "got %u\n", GetLastError() ); + if (ret) + { + ok( size == lstrlenW(headersW) * sizeof(WCHAR), "got %u\n", size ); + ok( !lstrcmpW(headersW, buf), "got %s\n", wine_dbgstr_w(buf) ); + } + + WinHttpCloseHandle( req ); + WinHttpCloseHandle( con ); + WinHttpCloseHandle( ses ); +} + static void test_credentials(void) { static WCHAR userW[] = {'u','s','e','r',0}; @@ -4783,6 +4859,7 @@ START_TEST (winhttp) test_multiple_reads(si.port); test_cookies(si.port); test_request_path_escapes(si.port); + test_passport_auth(si.port); /* send the basic request again to shutdown the server thread */ test_basic_request(si.port, NULL, quitW); diff --git a/dlls/winhttp/winhttp_private.h b/dlls/winhttp/winhttp_private.h index 38e39cb925e..93cf0f7125e 100644 --- a/dlls/winhttp/winhttp_private.h +++ b/dlls/winhttp/winhttp_private.h @@ -93,6 +93,7 @@ struct session struct list cookie_cache; HANDLE unload_event; DWORD secure_protocols; + DWORD passport_flags; }; struct connect