winhttp: Escape untrusted URL paths.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2018-08-24 13:32:42 +02:00 committed by Alexandre Julliard
parent bc6c2eb907
commit 498042e0ee
6 changed files with 86 additions and 37 deletions

View file

@ -2517,7 +2517,7 @@ static WCHAR *get_redirect_url( request_t *request, DWORD *len )
query_headers( request, WINHTTP_QUERY_LOCATION, NULL, NULL, &size, NULL );
if (get_last_error() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
if (!(ret = heap_alloc( size ))) return NULL;
*len = size / sizeof(WCHAR);
*len = size / sizeof(WCHAR) - 1;
if (query_headers( request, WINHTTP_QUERY_LOCATION, NULL, ret, &size, NULL )) return ret;
heap_free( ret );
return NULL;
@ -2526,42 +2526,42 @@ static WCHAR *get_redirect_url( request_t *request, DWORD *len )
static BOOL handle_redirect( request_t *request, DWORD status )
{
BOOL ret = FALSE;
DWORD len, len_url;
DWORD len, len_loc;
URL_COMPONENTS uc;
connect_t *connect = request->connect;
INTERNET_PORT port;
WCHAR *hostname = NULL, *location;
int index;
if (!(location = get_redirect_url( request, &len_url ))) return FALSE;
if (!(location = get_redirect_url( request, &len_loc ))) return FALSE;
memset( &uc, 0, sizeof(uc) );
uc.dwStructSize = sizeof(uc);
uc.dwSchemeLength = uc.dwHostNameLength = uc.dwUrlPathLength = uc.dwExtraInfoLength = ~0u;
if (!WinHttpCrackUrl( location, len_url, 0, &uc )) /* assume relative redirect */
if (!WinHttpCrackUrl( location, len_loc, 0, &uc )) /* assume relative redirect */
{
WCHAR *path, *p;
if (location[0] == '/')
{
len = strlenW( location );
len = escape_string( NULL, location, len_loc );
if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
strcpyW( path, location );
escape_string( path, location, len_loc );
}
else
{
if ((p = strrchrW( request->path, '/' ))) *p = 0;
len = strlenW( request->path ) + 1 + strlenW( location );
len = strlenW( request->path ) + 1 + escape_string( NULL, location, len_loc );
if (!(path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
strcpyW( path, request->path );
strcatW( path, slashW );
strcatW( path, location );
escape_string( path + strlenW(path), location, len_loc );
}
heap_free( request->path );
request->path = path;
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_url + 1 );
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
}
else
{
@ -2577,7 +2577,7 @@ static BOOL handle_redirect( request_t *request, DWORD status )
request->hdr.flags |= WINHTTP_FLAG_SECURE;
}
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_url + 1 );
send_callback( &request->hdr, WINHTTP_CALLBACK_STATUS_REDIRECT, location, len_loc + 1 );
len = uc.dwHostNameLength;
if (!(hostname = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
@ -2607,9 +2607,9 @@ static BOOL handle_redirect( request_t *request, DWORD status )
request->path = NULL;
if (uc.dwUrlPathLength)
{
len = uc.dwUrlPathLength + uc.dwExtraInfoLength;
len = escape_string( NULL, uc.lpszUrlPath, uc.dwUrlPathLength + uc.dwExtraInfoLength );
if (!(request->path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
strcpyW( request->path, uc.lpszUrlPath );
escape_string( request->path, uc.lpszUrlPath, uc.dwUrlPathLength + uc.dwExtraInfoLength );
}
else request->path = strdupW( slashW );
}

View file

@ -1142,14 +1142,14 @@ HINTERNET WINAPI WinHttpOpenRequest( HINTERNET hconnect, LPCWSTR verb, LPCWSTR o
if (object)
{
WCHAR *path, *p;
unsigned int len;
unsigned int len, len_object = strlenW(object);
len = strlenW( object ) + 1;
len = escape_string( NULL, object, len_object );
if (object[0] != '/') len++;
if (!(p = path = heap_alloc( len * sizeof(WCHAR) ))) goto end;
if (!(p = path = heap_alloc( (len + 1) * sizeof(WCHAR) ))) goto end;
if (object[0] != '/') *p++ = '/';
strcpyW( p, object );
escape_string( p, object, len_object );
request->path = path;
}
else if (!(request->path = strdupW( slashW ))) goto end;

View file

@ -667,6 +667,7 @@ static void WinHttpCrackUrl_test( void )
ok( ret, "WinHttpCrackUrl failed le=%u\n", GetLastError() );
ok( !lstrcmpW( uc.lpszHostName, hostnameW ), "unexpected host name\n" );
ok( !lstrcmpW( uc.lpszUrlPath, pathW ), "unexpected path\n" );
ok( uc.dwUrlPathLength == lstrlenW(pathW), "got %u\n", uc.dwUrlPathLength );
uc.dwStructSize = sizeof(uc);
uc.lpszScheme = NULL;

View file

@ -2274,6 +2274,11 @@ static DWORD CALLBACK server_thread(LPVOID param)
if (!strstr(buffer, "Cookie: name=value\r\n")) send(c, cookiemsg, sizeof(cookiemsg) - 1, 0);
else send(c, notokmsg, sizeof(notokmsg) - 1, 0);
}
else if (strstr(buffer, "GET /escape"))
{
if (strstr(buffer, "GET /escape?one%20two%0D%0A HTTP/1.1")) send(c, okmsg, sizeof(okmsg) - 1, 0);
else send(c, notokmsg, sizeof(notokmsg) - 1, 0);
}
if (strstr(buffer, "GET /quit"))
{
send(c, okmsg, sizeof okmsg - 1, 0);
@ -3229,6 +3234,40 @@ static void test_cookies( int port )
WinHttpCloseHandle( ses );
}
static void test_request_path_escapes( int port )
{
static const WCHAR objW[] =
{'/','e','s','c','a','p','e','?','o','n','e',' ','t','w','o','\r','\n',0};
HINTERNET ses, con, req;
DWORD status, size;
BOOL ret;
ses = WinHttpOpen( test_useragent, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0 );
ok( ses != NULL, "failed to open session %u\n", GetLastError() );
con = WinHttpConnect( ses, localhostW, port, 0 );
ok( con != NULL, "failed to open a connection %u\n", GetLastError() );
req = WinHttpOpenRequest( con, NULL, objW, NULL, NULL, NULL, 0 );
ok( req != NULL, "failed to open a request %u\n", GetLastError() );
ret = WinHttpSendRequest( req, NULL, 0, NULL, 0, 0, 0 );
ok( ret, "failed to send request %u\n", GetLastError() );
ret = WinHttpReceiveResponse( req, NULL );
ok( ret, "failed to receive response %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, "failed to query status code %u\n", GetLastError() );
ok( status == HTTP_STATUS_OK, "request failed unexpectedly %u\n", status );
WinHttpCloseHandle( req );
WinHttpCloseHandle( con );
WinHttpCloseHandle( ses );
}
static void test_connection_info( int port )
{
static const WCHAR basicW[] = {'/','b','a','s','i','c',0};
@ -4555,6 +4594,7 @@ START_TEST (winhttp)
test_bad_header(si.port);
test_multiple_reads(si.port);
test_cookies(si.port);
test_request_path_escapes(si.port);
/* send the basic request again to shutdown the server thread */
test_basic_request(si.port, NULL, quitW);

View file

@ -118,7 +118,7 @@ static BOOL need_escape( WCHAR c )
}
}
static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len )
DWORD escape_string( WCHAR *dst, const WCHAR *src, DWORD len )
{
static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
DWORD ret = len;
@ -129,38 +129,45 @@ static DWORD copy_escape( WCHAR *dst, const WCHAR *src, DWORD len )
{
if (need_escape( src[i] ))
{
p[0] = '%';
p[1] = hex[(src[i] >> 4) & 0xf];
p[2] = hex[src[i] & 0xf];
if (dst)
{
p[0] = '%';
p[1] = hex[(src[i] >> 4) & 0xf];
p[2] = hex[src[i] & 0xf];
p += 2;
}
ret += 2;
p += 2;
}
else *p = src[i];
else if (dst) *p = src[i];
}
dst[ret] = 0;
if (dst) dst[ret] = 0;
return ret;
}
static WCHAR *escape_url( LPCWSTR url, DWORD *len )
static WCHAR *escape_url( const WCHAR *url, DWORD *len )
{
WCHAR *ret;
const WCHAR *p, *q;
const WCHAR *p;
DWORD len_base, len_path;
if ((p = q = strrchrW( url, '/' )))
if ((p = strrchrW( url, '/' )))
{
while (*q)
{
if (need_escape( *q )) *len += 2;
q++;
}
len_base = p - url;
len_path = escape_string( NULL, p, *len - len_base );
}
if (!(ret = heap_alloc( (*len + 1) * sizeof(WCHAR) ))) return NULL;
if (!p) strcpyW( ret, url );
else
{
memcpy( ret, url, (p - url) * sizeof(WCHAR) );
copy_escape( ret + (p - url), p, q - p );
len_base = *len;
len_path = 0;
}
if (!(ret = heap_alloc( (len_base + len_path + 1) * sizeof(WCHAR) ))) return NULL;
memcpy( ret, url, len_base * sizeof(WCHAR) );
if (p) escape_string( ret + len_base, p, *len - (p - url) );
ret[len_base + len_path] = 0;
*len = len_base + len_path;
return ret;
}
@ -516,7 +523,7 @@ BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS uc, DWORD flags, LPWSTR url, LPDW
if (uc->lpszUrlPath)
{
len = comp_length( uc->dwUrlPathLength, 0, uc->lpszUrlPath );
if (flags & ICU_ESCAPE) url += copy_escape( url, uc->lpszUrlPath, len );
if (flags & ICU_ESCAPE) url += escape_string( url, uc->lpszUrlPath, len );
else
{
memcpy( url, uc->lpszUrlPath, len * sizeof(WCHAR) );
@ -526,7 +533,7 @@ BOOL WINAPI WinHttpCreateUrl( LPURL_COMPONENTS uc, DWORD flags, LPWSTR url, LPDW
if (uc->lpszExtraInfo)
{
len = comp_length( uc->dwExtraInfoLength, 0, uc->lpszExtraInfo );
if (flags & ICU_ESCAPE) url += copy_escape( url, uc->lpszExtraInfo, len );
if (flags & ICU_ESCAPE) url += escape_string( url, uc->lpszExtraInfo, len );
else
{
memcpy( url, uc->lpszExtraInfo, len * sizeof(WCHAR) );

View file

@ -322,6 +322,7 @@ BOOL set_server_for_hostname( connect_t *, LPCWSTR, INTERNET_PORT ) DECLSPEC_HID
void destroy_authinfo( struct authinfo * ) DECLSPEC_HIDDEN;
void release_host( hostdata_t *host ) DECLSPEC_HIDDEN;
DWORD escape_string( WCHAR *, const WCHAR *, DWORD ) DECLSPEC_HIDDEN;
extern HRESULT WinHttpRequest_create( void ** ) DECLSPEC_HIDDEN;
void release_typelib( void ) DECLSPEC_HIDDEN;