wininet: Don't leak the proxy username and password in WININET_SetProxyAuthorization.
[wine] / dlls / wininet / dialogs.c
1 /*
2  * Wininet
3  *
4  * Copyright 2003 Mike McCormack for CodeWeavers Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #if defined(__MINGW32__) || defined (_MSC_VER)
25 #include <ws2tcpip.h>
26 #endif
27
28 #include <stdarg.h>
29
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winuser.h"
33 #include "winreg.h"
34 #include "wininet.h"
35 #include "winnetwk.h"
36 #include "wine/debug.h"
37 #include "winerror.h"
38 #define NO_SHLWAPI_STREAM
39 #include "shlwapi.h"
40
41 #include "internet.h"
42
43 #include "wine/unicode.h"
44
45 #include "resource.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
48
49 struct WININET_ErrorDlgParams
50 {
51     HWND       hWnd;
52     HINTERNET  hRequest;
53     DWORD      dwError;
54     DWORD      dwFlags;
55     LPVOID*    lppvData;
56 };
57
58 /***********************************************************************
59  *         WININET_GetProxyServer
60  *
61  *  Determine the name of the proxy server the request is using
62  */
63 static BOOL WININET_GetProxyServer( HINTERNET hRequest, LPWSTR szBuf, DWORD sz )
64 {
65     http_request_t *lpwhr;
66     http_session_t *lpwhs = NULL;
67     appinfo_t *hIC = NULL;
68     BOOL ret = FALSE;
69     LPWSTR p;
70
71     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
72     if (NULL == lpwhr)
73         goto done;
74
75     lpwhs = lpwhr->lpHttpSession;
76     if (NULL == lpwhs)
77         goto done;
78
79     hIC = lpwhs->lpAppInfo;
80     if (NULL == hIC)
81         goto done;
82
83     lstrcpynW(szBuf, hIC->lpszProxy, sz);
84
85     /* FIXME: perhaps it would be better to use InternetCrackUrl here */
86     p = strchrW(szBuf, ':');
87     if (p)
88         *p = 0;
89
90     ret = TRUE;
91
92 done:
93     WININET_Release( &lpwhr->hdr );
94     return ret;
95 }
96
97 /***********************************************************************
98  *         WININET_GetAuthRealm
99  *
100  *  Determine the name of the (basic) Authentication realm
101  */
102 static BOOL WININET_GetAuthRealm( HINTERNET hRequest, LPWSTR szBuf, DWORD sz )
103 {
104     LPWSTR p, q;
105     DWORD index;
106     static const WCHAR szRealm[] = { 'r','e','a','l','m','=',0 };
107
108     /* extract the Realm from the proxy response and show it */
109     index = 0;
110     if( !HttpQueryInfoW( hRequest, HTTP_QUERY_PROXY_AUTHENTICATE,
111                          szBuf, &sz, &index) )
112         return FALSE;
113
114     /*
115      * FIXME: maybe we should check that we're
116      * dealing with 'Basic' Authentication
117      */
118     p = strchrW( szBuf, ' ' );
119     if( !p || strncmpW( p+1, szRealm, strlenW(szRealm) ) )
120     {
121         ERR("proxy response wrong? (%s)\n", debugstr_w(szBuf));
122         return FALSE;
123     }
124
125
126     /* remove quotes */
127     p += 7;
128     if( *p == '"' )
129     {
130         p++;
131         q = strrchrW( p, '"' );
132         if( q )
133             *q = 0;
134     }
135     strcpyW( szBuf, p );
136
137     return TRUE;
138 }
139
140 /***********************************************************************
141  *         WININET_GetSetPassword
142  */
143 static BOOL WININET_GetSetPassword( HWND hdlg, LPCWSTR szServer, 
144                                     LPCWSTR szRealm, BOOL bSet )
145 {
146     WCHAR szResource[0x80], szUserPass[0x40];
147     LPWSTR p;
148     HWND hUserItem, hPassItem;
149     DWORD r, dwMagic = 19;
150     UINT r_len, u_len;
151     WORD sz;
152     static const WCHAR szColon[] = { ':',0 };
153     static const WCHAR szbs[] = { '/', 0 };
154
155     hUserItem = GetDlgItem( hdlg, IDC_USERNAME );
156     hPassItem = GetDlgItem( hdlg, IDC_PASSWORD );
157
158     /* now try fetch the username and password */
159     lstrcpyW( szResource, szServer);
160     lstrcatW( szResource, szbs);
161     lstrcatW( szResource, szRealm);
162
163     /*
164      * WNetCachePassword is only concerned with the length
165      * of the data stored (which we tell it) and it does
166      * not use strlen() internally so we can add WCHAR data
167      * instead of ASCII data and get it back the same way.
168      */
169     if( bSet )
170     {
171         szUserPass[0] = 0;
172         GetWindowTextW( hUserItem, szUserPass, 
173                         (sizeof szUserPass-1)/sizeof(WCHAR) );
174         lstrcatW(szUserPass, szColon);
175         u_len = strlenW( szUserPass );
176         GetWindowTextW( hPassItem, szUserPass+u_len, 
177                         (sizeof szUserPass)/sizeof(WCHAR)-u_len );
178
179         r_len = (strlenW( szResource ) + 1)*sizeof(WCHAR);
180         u_len = (strlenW( szUserPass ) + 1)*sizeof(WCHAR);
181         r = WNetCachePassword( (CHAR*)szResource, r_len,
182                                (CHAR*)szUserPass, u_len, dwMagic, 0 );
183
184         return ( r == WN_SUCCESS );
185     }
186
187     sz = sizeof szUserPass;
188     r_len = (strlenW( szResource ) + 1)*sizeof(WCHAR);
189     r = WNetGetCachedPassword( (CHAR*)szResource, r_len,
190                                (CHAR*)szUserPass, &sz, dwMagic );
191     if( r != WN_SUCCESS )
192         return FALSE;
193
194     p = strchrW( szUserPass, ':' );
195     if( p )
196     {
197         *p = 0;
198         SetWindowTextW( hUserItem, szUserPass );
199         SetWindowTextW( hPassItem, p+1 );
200     }
201
202     return TRUE;
203 }
204
205 /***********************************************************************
206  *         WININET_SetProxyAuthorization
207  */
208 static BOOL WININET_SetProxyAuthorization( HINTERNET hRequest,
209                                          LPWSTR username, LPWSTR password )
210 {
211     http_request_t *lpwhr;
212     http_session_t *lpwhs;
213     appinfo_t *hIC;
214     BOOL ret = FALSE;
215     LPWSTR p;
216
217     lpwhr = (http_request_t*) WININET_GetObject( hRequest );
218     if( !lpwhr )
219         return FALSE;
220
221     lpwhs = lpwhr->lpHttpSession;
222     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
223     {
224         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
225         goto done;
226     }
227
228     hIC = lpwhs->lpAppInfo;
229
230     p = heap_strdupW(username);
231     if( !p )
232         goto done;
233
234     HeapFree(GetProcessHeap(), 0, hIC->lpszProxyUsername);
235     hIC->lpszProxyUsername = p;
236
237     p = heap_strdupW(password);
238     if( !p )
239         goto done;
240
241     HeapFree(GetProcessHeap(), 0, hIC->lpszProxyPassword);
242     hIC->lpszProxyPassword = p;
243
244     ret = TRUE;
245
246 done:
247     WININET_Release( &lpwhr->hdr );
248     return ret;
249 }
250
251 /***********************************************************************
252  *         WININET_ProxyPasswordDialog
253  */
254 static INT_PTR WINAPI WININET_ProxyPasswordDialog(
255     HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
256 {
257     HWND hitem;
258     struct WININET_ErrorDlgParams *params;
259     WCHAR szRealm[0x80], szServer[0x80];
260
261     if( uMsg == WM_INITDIALOG )
262     {
263         TRACE("WM_INITDIALOG (%08lx)\n", lParam);
264
265         /* save the parameter list */
266         params = (struct WININET_ErrorDlgParams*) lParam;
267         SetWindowLongPtrW( hdlg, GWLP_USERDATA, lParam );
268
269         /* extract the Realm from the proxy response and show it */
270         if( WININET_GetAuthRealm( params->hRequest,
271                                   szRealm, sizeof szRealm/sizeof(WCHAR)) )
272         {
273             hitem = GetDlgItem( hdlg, IDC_REALM );
274             SetWindowTextW( hitem, szRealm );
275         }
276
277         /* extract the name of the proxy server */
278         if( WININET_GetProxyServer( params->hRequest, 
279                                     szServer, sizeof szServer/sizeof(WCHAR)) )
280         {
281             hitem = GetDlgItem( hdlg, IDC_PROXY );
282             SetWindowTextW( hitem, szServer );
283         }
284
285         WININET_GetSetPassword( hdlg, szServer, szRealm, FALSE );
286
287         return TRUE;
288     }
289
290     params = (struct WININET_ErrorDlgParams*)
291                  GetWindowLongPtrW( hdlg, GWLP_USERDATA );
292
293     switch( uMsg )
294     {
295     case WM_COMMAND:
296         if( wParam == IDOK )
297         {
298             WCHAR username[0x20], password[0x20];
299
300             username[0] = 0;
301             hitem = GetDlgItem( hdlg, IDC_USERNAME );
302             if( hitem )
303                 GetWindowTextW( hitem, username, sizeof username/sizeof(WCHAR) );
304             
305             password[0] = 0;
306             hitem = GetDlgItem( hdlg, IDC_PASSWORD );
307             if( hitem )
308                 GetWindowTextW( hitem, password, sizeof password/sizeof(WCHAR) );
309
310             hitem = GetDlgItem( hdlg, IDC_SAVEPASSWORD );
311             if( hitem &&
312                 SendMessageW( hitem, BM_GETSTATE, 0, 0 ) &&
313                 WININET_GetAuthRealm( params->hRequest,
314                                   szRealm, sizeof szRealm/sizeof(WCHAR)) &&
315                 WININET_GetProxyServer( params->hRequest, 
316                                     szServer, sizeof szServer/sizeof(WCHAR)) )
317             {
318                 WININET_GetSetPassword( hdlg, szServer, szRealm, TRUE );
319             }
320             WININET_SetProxyAuthorization( params->hRequest, username, password );
321
322             EndDialog( hdlg, ERROR_INTERNET_FORCE_RETRY );
323             return TRUE;
324         }
325         if( wParam == IDCANCEL )
326         {
327             EndDialog( hdlg, 0 );
328             return TRUE;
329         }
330         break;
331     }
332     return FALSE;
333 }
334
335 /***********************************************************************
336  *         WININET_GetConnectionStatus
337  */
338 static INT WININET_GetConnectionStatus( HINTERNET hRequest )
339 {
340     WCHAR szStatus[0x20];
341     DWORD sz, index, dwStatus;
342
343     TRACE("%p\n", hRequest );
344
345     sz = sizeof szStatus;
346     index = 0;
347     if( !HttpQueryInfoW( hRequest, HTTP_QUERY_STATUS_CODE,
348                     szStatus, &sz, &index))
349         return -1;
350     dwStatus = atoiW( szStatus );
351
352     TRACE("request %p status = %d\n", hRequest, dwStatus );
353
354     return dwStatus;
355 }
356
357
358 /***********************************************************************
359  *         InternetErrorDlg
360  */
361 DWORD WINAPI InternetErrorDlg(HWND hWnd, HINTERNET hRequest,
362                  DWORD dwError, DWORD dwFlags, LPVOID* lppvData)
363 {
364     struct WININET_ErrorDlgParams params;
365     HMODULE hwininet = GetModuleHandleA( "wininet.dll" );
366     INT dwStatus;
367
368     TRACE("%p %p %d %08x %p\n", hWnd, hRequest, dwError, dwFlags, lppvData);
369
370     params.hWnd = hWnd;
371     params.hRequest = hRequest;
372     params.dwError = dwError;
373     params.dwFlags = dwFlags;
374     params.lppvData = lppvData;
375
376     switch( dwError )
377     {
378     case ERROR_SUCCESS:
379         if( !(dwFlags & FLAGS_ERROR_UI_FILTER_FOR_ERRORS ) )
380             return 0;
381         dwStatus = WININET_GetConnectionStatus( hRequest );
382         if( HTTP_STATUS_PROXY_AUTH_REQ != dwStatus )
383             return ERROR_SUCCESS;
384         return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ),
385                     hWnd, WININET_ProxyPasswordDialog, (LPARAM) &params );
386
387     case ERROR_INTERNET_INCORRECT_PASSWORD:
388         return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ),
389                     hWnd, WININET_ProxyPasswordDialog, (LPARAM) &params );
390
391     case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
392     case ERROR_INTERNET_INVALID_CA:
393     case ERROR_INTERNET_POST_IS_NON_SECURE:
394     case ERROR_INTERNET_SEC_CERT_CN_INVALID:
395     case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
396         FIXME("Need to display dialog for error %d\n", dwError);
397         return ERROR_SUCCESS;
398     }
399     return ERROR_INVALID_PARAMETER;
400 }