xref: /reactos/dll/win32/credui/credui_main.c (revision cc439606)
1 /*
2  * Credentials User Interface
3  *
4  * Copyright 2006 Robert Shearman (for CodeWeavers)
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 <stdarg.h>
22 
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winnt.h"
26 #include "winuser.h"
27 #include "wincred.h"
28 #include "rpc.h"
29 #include "sspi.h"
30 #include "commctrl.h"
31 
32 #include "credui_resources.h"
33 
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36 #include "wine/list.h"
37 
38 WINE_DEFAULT_DEBUG_CHANNEL(credui);
39 
40 #define TOOLID_INCORRECTPASSWORD    1
41 #define TOOLID_CAPSLOCKON           2
42 
43 #define ID_CAPSLOCKPOP              1
44 
45 struct pending_credentials
46 {
47     struct list entry;
48     PWSTR pszTargetName;
49     PWSTR pszUsername;
50     PWSTR pszPassword;
51     BOOL generic;
52 };
53 
54 static HINSTANCE hinstCredUI;
55 
56 static struct list pending_credentials_list = LIST_INIT(pending_credentials_list);
57 
58 static CRITICAL_SECTION csPendingCredentials;
59 static CRITICAL_SECTION_DEBUG critsect_debug =
60 {
61     0, 0, &csPendingCredentials,
62     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
63     0, 0, { (DWORD_PTR)(__FILE__ ": csPendingCredentials") }
64 };
65 static CRITICAL_SECTION csPendingCredentials = { &critsect_debug, -1, 0, 0, 0, 0 };
66 
67 
68 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
69 {
70     struct pending_credentials *entry, *cursor2;
71     TRACE("(0x%p, %d, %p)\n",hinstDLL,fdwReason,lpvReserved);
72 
73     switch (fdwReason)
74     {
75     case DLL_WINE_PREATTACH:
76         return FALSE;	/* prefer native version */
77 
78     case DLL_PROCESS_ATTACH:
79         DisableThreadLibraryCalls(hinstDLL);
80         hinstCredUI = hinstDLL;
81         InitCommonControls();
82         break;
83 
84     case DLL_PROCESS_DETACH:
85         if (lpvReserved) break;
86         LIST_FOR_EACH_ENTRY_SAFE(entry, cursor2, &pending_credentials_list, struct pending_credentials, entry)
87         {
88             list_remove(&entry->entry);
89 
90             HeapFree(GetProcessHeap(), 0, entry->pszTargetName);
91             HeapFree(GetProcessHeap(), 0, entry->pszUsername);
92             ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
93             HeapFree(GetProcessHeap(), 0, entry->pszPassword);
94             HeapFree(GetProcessHeap(), 0, entry);
95         }
96         DeleteCriticalSection(&csPendingCredentials);
97         break;
98     }
99 
100     return TRUE;
101 }
102 
103 static DWORD save_credentials(PCWSTR pszTargetName, PCWSTR pszUsername,
104                               PCWSTR pszPassword, BOOL generic)
105 {
106     CREDENTIALW cred;
107 
108     TRACE("saving servername %s with username %s\n", debugstr_w(pszTargetName), debugstr_w(pszUsername));
109 
110     cred.Flags = 0;
111     cred.Type = generic ? CRED_TYPE_GENERIC : CRED_TYPE_DOMAIN_PASSWORD;
112     cred.TargetName = (LPWSTR)pszTargetName;
113     cred.Comment = NULL;
114     cred.CredentialBlobSize = strlenW(pszPassword) * sizeof(WCHAR);
115     cred.CredentialBlob = (LPBYTE)pszPassword;
116     cred.Persist = CRED_PERSIST_ENTERPRISE;
117     cred.AttributeCount = 0;
118     cred.Attributes = NULL;
119     cred.TargetAlias = NULL;
120     cred.UserName = (LPWSTR)pszUsername;
121 
122     if (CredWriteW(&cred, 0))
123         return ERROR_SUCCESS;
124     else
125     {
126         DWORD ret = GetLastError();
127         ERR("CredWriteW failed with error %d\n", ret);
128         return ret;
129     }
130 }
131 
132 struct cred_dialog_params
133 {
134     PCWSTR pszTargetName;
135     PCWSTR pszMessageText;
136     PCWSTR pszCaptionText;
137     HBITMAP hbmBanner;
138     PWSTR pszUsername;
139     ULONG ulUsernameMaxChars;
140     PWSTR pszPassword;
141     ULONG ulPasswordMaxChars;
142     BOOL fSave;
143     DWORD dwFlags;
144     HWND hwndBalloonTip;
145     BOOL fBalloonTipActive;
146 };
147 
148 static void CredDialogFillUsernameCombo(HWND hwndUsername, const struct cred_dialog_params *params)
149 {
150     DWORD count;
151     DWORD i;
152     PCREDENTIALW *credentials;
153 
154     if (!CredEnumerateW(NULL, 0, &count, &credentials))
155         return;
156 
157     for (i = 0; i < count; i++)
158     {
159         COMBOBOXEXITEMW comboitem;
160         DWORD j;
161         BOOL duplicate = FALSE;
162 
163         if (params->dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS)
164         {
165             if ((credentials[i]->Type != CRED_TYPE_GENERIC) || !credentials[i]->UserName)
166                 continue;
167         }
168         else
169         {
170             if (credentials[i]->Type == CRED_TYPE_GENERIC)
171                 continue;
172         }
173 
174         /* don't add another item with the same name if we've already added it */
175         for (j = 0; j < i; j++)
176             if (!strcmpW(credentials[i]->UserName, credentials[j]->UserName))
177             {
178                 duplicate = TRUE;
179                 break;
180             }
181 
182         if (duplicate)
183             continue;
184 
185         comboitem.mask = CBEIF_TEXT;
186         comboitem.iItem = -1;
187         comboitem.pszText = credentials[i]->UserName;
188         SendMessageW(hwndUsername, CBEM_INSERTITEMW, 0, (LPARAM)&comboitem);
189     }
190 
191     CredFree(credentials);
192 }
193 
194 static void CredDialogCreateBalloonTip(HWND hwndDlg, struct cred_dialog_params *params)
195 {
196     TTTOOLINFOW toolinfo;
197     WCHAR wszText[256];
198 
199     if (params->hwndBalloonTip)
200         return;
201 
202     params->hwndBalloonTip = CreateWindowExW(WS_EX_TOOLWINDOW, TOOLTIPS_CLASSW,
203         NULL, WS_POPUP | TTS_NOPREFIX | TTS_BALLOON, CW_USEDEFAULT,
204         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, hwndDlg, NULL,
205         hinstCredUI, NULL);
206     SetWindowPos(params->hwndBalloonTip, HWND_TOPMOST, 0, 0, 0, 0,
207                  SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
208 
209     if (!LoadStringW(hinstCredUI, IDS_INCORRECTPASSWORD, wszText, ARRAY_SIZE(wszText)))
210     {
211         ERR("failed to load IDS_INCORRECTPASSWORD\n");
212         return;
213     }
214 
215     toolinfo.cbSize = sizeof(toolinfo);
216     toolinfo.uFlags = TTF_TRACK;
217     toolinfo.hwnd = hwndDlg;
218     toolinfo.uId = TOOLID_INCORRECTPASSWORD;
219     SetRectEmpty(&toolinfo.rect);
220     toolinfo.hinst = NULL;
221     toolinfo.lpszText = wszText;
222     toolinfo.lParam = 0;
223     toolinfo.lpReserved = NULL;
224     SendMessageW(params->hwndBalloonTip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo);
225 
226     if (!LoadStringW(hinstCredUI, IDS_CAPSLOCKON, wszText, ARRAY_SIZE(wszText)))
227     {
228         ERR("failed to load IDS_CAPSLOCKON\n");
229         return;
230     }
231 
232     toolinfo.uId = TOOLID_CAPSLOCKON;
233     SendMessageW(params->hwndBalloonTip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo);
234 }
235 
236 static void CredDialogShowIncorrectPasswordBalloon(HWND hwndDlg, struct cred_dialog_params *params)
237 {
238     TTTOOLINFOW toolinfo;
239     RECT rcPassword;
240     INT x;
241     INT y;
242     WCHAR wszTitle[256];
243 
244     /* user name likely wrong so balloon would be confusing. focus is also
245      * not set to the password edit box, so more notification would need to be
246      * handled */
247     if (!params->pszUsername[0])
248         return;
249 
250     /* don't show two balloon tips at once */
251     if (params->fBalloonTipActive)
252         return;
253 
254     if (!LoadStringW(hinstCredUI, IDS_INCORRECTPASSWORDTITLE, wszTitle, ARRAY_SIZE(wszTitle)))
255     {
256         ERR("failed to load IDS_INCORRECTPASSWORDTITLE\n");
257         return;
258     }
259 
260     CredDialogCreateBalloonTip(hwndDlg, params);
261 
262     memset(&toolinfo, 0, sizeof(toolinfo));
263     toolinfo.cbSize = sizeof(toolinfo);
264     toolinfo.hwnd = hwndDlg;
265     toolinfo.uId = TOOLID_INCORRECTPASSWORD;
266 
267     SendMessageW(params->hwndBalloonTip, TTM_SETTITLEW, TTI_ERROR, (LPARAM)wszTitle);
268 
269     GetWindowRect(GetDlgItem(hwndDlg, IDC_PASSWORD), &rcPassword);
270     /* centered vertically and in the right side of the password edit control */
271     x = rcPassword.right - 12;
272     y = (rcPassword.top + rcPassword.bottom) / 2;
273     SendMessageW(params->hwndBalloonTip, TTM_TRACKPOSITION, 0, MAKELONG(x, y));
274 
275     SendMessageW(params->hwndBalloonTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&toolinfo);
276 
277     params->fBalloonTipActive = TRUE;
278 }
279 
280 static void CredDialogShowCapsLockBalloon(HWND hwndDlg, struct cred_dialog_params *params)
281 {
282     TTTOOLINFOW toolinfo;
283     RECT rcPassword;
284     INT x;
285     INT y;
286     WCHAR wszTitle[256];
287 
288     /* don't show two balloon tips at once */
289     if (params->fBalloonTipActive)
290         return;
291 
292     if (!LoadStringW(hinstCredUI, IDS_CAPSLOCKONTITLE, wszTitle, ARRAY_SIZE(wszTitle)))
293     {
294         ERR("failed to load IDS_IDSCAPSLOCKONTITLE\n");
295         return;
296     }
297 
298     CredDialogCreateBalloonTip(hwndDlg, params);
299 
300     memset(&toolinfo, 0, sizeof(toolinfo));
301     toolinfo.cbSize = sizeof(toolinfo);
302     toolinfo.hwnd = hwndDlg;
303     toolinfo.uId = TOOLID_CAPSLOCKON;
304 
305     SendMessageW(params->hwndBalloonTip, TTM_SETTITLEW, TTI_WARNING, (LPARAM)wszTitle);
306 
307     GetWindowRect(GetDlgItem(hwndDlg, IDC_PASSWORD), &rcPassword);
308     /* just inside the left side of the password edit control */
309     x = rcPassword.left + 12;
310     y = rcPassword.bottom - 3;
311     SendMessageW(params->hwndBalloonTip, TTM_TRACKPOSITION, 0, MAKELONG(x, y));
312 
313     SendMessageW(params->hwndBalloonTip, TTM_TRACKACTIVATE, TRUE, (LPARAM)&toolinfo);
314 
315     SetTimer(hwndDlg, ID_CAPSLOCKPOP,
316              SendMessageW(params->hwndBalloonTip, TTM_GETDELAYTIME, TTDT_AUTOPOP, 0),
317              NULL);
318 
319     params->fBalloonTipActive = TRUE;
320 }
321 
322 static void CredDialogHideBalloonTip(HWND hwndDlg, struct cred_dialog_params *params)
323 {
324     TTTOOLINFOW toolinfo;
325 
326     if (!params->hwndBalloonTip)
327         return;
328 
329     memset(&toolinfo, 0, sizeof(toolinfo));
330 
331     toolinfo.cbSize = sizeof(toolinfo);
332     toolinfo.hwnd = hwndDlg;
333     toolinfo.uId = 0;
334     SendMessageW(params->hwndBalloonTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&toolinfo);
335     toolinfo.uId = 1;
336     SendMessageW(params->hwndBalloonTip, TTM_TRACKACTIVATE, FALSE, (LPARAM)&toolinfo);
337 
338     params->fBalloonTipActive = FALSE;
339 }
340 
341 static inline BOOL CredDialogCapsLockOn(void)
342 {
343     return (GetKeyState(VK_CAPITAL) & 0x1) != 0;
344 }
345 
346 static LRESULT CALLBACK CredDialogPasswordSubclassProc(HWND hwnd, UINT uMsg,
347     WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
348 {
349     struct cred_dialog_params *params = (struct cred_dialog_params *)dwRefData;
350     switch (uMsg)
351     {
352     case WM_KEYDOWN:
353         if (wParam == VK_CAPITAL)
354         {
355             HWND hwndDlg = GetParent(hwnd);
356             if (CredDialogCapsLockOn())
357                 CredDialogShowCapsLockBalloon(hwndDlg, params);
358             else
359                 CredDialogHideBalloonTip(hwndDlg, params);
360         }
361         break;
362     case WM_DESTROY:
363         RemoveWindowSubclass(hwnd, CredDialogPasswordSubclassProc, uIdSubclass);
364         break;
365     }
366     return DefSubclassProc(hwnd, uMsg, wParam, lParam);
367 }
368 
369 static BOOL CredDialogInit(HWND hwndDlg, struct cred_dialog_params *params)
370 {
371     HWND hwndUsername = GetDlgItem(hwndDlg, IDC_USERNAME);
372     HWND hwndPassword = GetDlgItem(hwndDlg, IDC_PASSWORD);
373 
374     SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)params);
375 
376     if (params->hbmBanner)
377         SendMessageW(GetDlgItem(hwndDlg, IDB_BANNER), STM_SETIMAGE,
378                      IMAGE_BITMAP, (LPARAM)params->hbmBanner);
379 
380     if (params->pszMessageText)
381         SetDlgItemTextW(hwndDlg, IDC_MESSAGE, params->pszMessageText);
382     else
383     {
384         WCHAR format[256];
385         WCHAR message[256];
386         LoadStringW(hinstCredUI, IDS_MESSAGEFORMAT, format, ARRAY_SIZE(format));
387         snprintfW(message, ARRAY_SIZE(message), format, params->pszTargetName);
388         SetDlgItemTextW(hwndDlg, IDC_MESSAGE, message);
389     }
390     SetWindowTextW(hwndUsername, params->pszUsername);
391     SetWindowTextW(hwndPassword, params->pszPassword);
392 
393     CredDialogFillUsernameCombo(hwndUsername, params);
394 
395     if (params->pszUsername[0])
396     {
397         /* prevent showing a balloon tip here */
398         params->fBalloonTipActive = TRUE;
399         SetFocus(hwndPassword);
400         params->fBalloonTipActive = FALSE;
401     }
402     else
403         SetFocus(hwndUsername);
404 
405     if (params->pszCaptionText)
406         SetWindowTextW(hwndDlg, params->pszCaptionText);
407     else
408     {
409         WCHAR format[256];
410         WCHAR title[256];
411         LoadStringW(hinstCredUI, IDS_TITLEFORMAT, format, ARRAY_SIZE(format));
412         snprintfW(title, ARRAY_SIZE(title), format, params->pszTargetName);
413         SetWindowTextW(hwndDlg, title);
414     }
415 
416     if (params->dwFlags & CREDUI_FLAGS_PERSIST ||
417         (params->dwFlags & CREDUI_FLAGS_DO_NOT_PERSIST &&
418          !(params->dwFlags & CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX)))
419         ShowWindow(GetDlgItem(hwndDlg, IDC_SAVE), SW_HIDE);
420     else if (params->fSave)
421         CheckDlgButton(hwndDlg, IDC_SAVE, BST_CHECKED);
422 
423     /* setup subclassing for Caps Lock detection */
424     SetWindowSubclass(hwndPassword, CredDialogPasswordSubclassProc, 1, (DWORD_PTR)params);
425 
426     if (params->dwFlags & CREDUI_FLAGS_INCORRECT_PASSWORD)
427         CredDialogShowIncorrectPasswordBalloon(hwndDlg, params);
428     else if ((GetFocus() == hwndPassword) && CredDialogCapsLockOn())
429         CredDialogShowCapsLockBalloon(hwndDlg, params);
430 
431     return FALSE;
432 }
433 
434 static void CredDialogCommandOk(HWND hwndDlg, struct cred_dialog_params *params)
435 {
436     HWND hwndUsername = GetDlgItem(hwndDlg, IDC_USERNAME);
437     LPWSTR user;
438     INT len;
439     INT len2;
440 
441     len = GetWindowTextLengthW(hwndUsername);
442     user = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
443     GetWindowTextW(hwndUsername, user, len + 1);
444 
445     if (!user[0])
446     {
447         HeapFree(GetProcessHeap(), 0, user);
448         return;
449     }
450 
451     if (!strchrW(user, '\\') && !strchrW(user, '@'))
452     {
453         ULONG len_target = strlenW(params->pszTargetName);
454         memcpy(params->pszUsername, params->pszTargetName,
455                min(len_target, params->ulUsernameMaxChars) * sizeof(WCHAR));
456         if (len_target + 1 < params->ulUsernameMaxChars)
457             params->pszUsername[len_target] = '\\';
458         if (len_target + 2 < params->ulUsernameMaxChars)
459             params->pszUsername[len_target + 1] = '\0';
460     }
461     else if (params->ulUsernameMaxChars > 0)
462         params->pszUsername[0] = '\0';
463 
464     len2 = strlenW(params->pszUsername);
465     memcpy(params->pszUsername + len2, user, min(len, params->ulUsernameMaxChars - len2) * sizeof(WCHAR));
466     if (params->ulUsernameMaxChars)
467         params->pszUsername[len2 + min(len, params->ulUsernameMaxChars - len2 - 1)] = '\0';
468 
469     HeapFree(GetProcessHeap(), 0, user);
470 
471     GetDlgItemTextW(hwndDlg, IDC_PASSWORD, params->pszPassword,
472                     params->ulPasswordMaxChars);
473 
474     params->fSave = IsDlgButtonChecked(hwndDlg, IDC_SAVE) == BST_CHECKED;
475 
476     EndDialog(hwndDlg, IDOK);
477 }
478 
479 static INT_PTR CALLBACK CredDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,
480                                        LPARAM lParam)
481 {
482     switch (uMsg)
483     {
484         case WM_INITDIALOG:
485         {
486             struct cred_dialog_params *params = (struct cred_dialog_params *)lParam;
487 
488             return CredDialogInit(hwndDlg, params);
489         }
490         case WM_COMMAND:
491             switch (wParam)
492             {
493                 case MAKELONG(IDOK, BN_CLICKED):
494                 {
495                     struct cred_dialog_params *params =
496                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
497                     CredDialogCommandOk(hwndDlg, params);
498                     return TRUE;
499                 }
500                 case MAKELONG(IDCANCEL, BN_CLICKED):
501                     EndDialog(hwndDlg, IDCANCEL);
502                     return TRUE;
503                 case MAKELONG(IDC_PASSWORD, EN_SETFOCUS):
504                     if (CredDialogCapsLockOn())
505                     {
506                         struct cred_dialog_params *params =
507                             (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
508                         CredDialogShowCapsLockBalloon(hwndDlg, params);
509                     }
510                     /* don't allow another window to steal focus while the
511                      * user is typing their password */
512                     LockSetForegroundWindow(LSFW_LOCK);
513                     return TRUE;
514                 case MAKELONG(IDC_PASSWORD, EN_KILLFOCUS):
515                 {
516                     struct cred_dialog_params *params =
517                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
518                     /* the user is no longer typing their password, so allow
519                      * other windows to become foreground ones */
520                     LockSetForegroundWindow(LSFW_UNLOCK);
521                     CredDialogHideBalloonTip(hwndDlg, params);
522                     return TRUE;
523                 }
524                 case MAKELONG(IDC_PASSWORD, EN_CHANGE):
525                 {
526                     struct cred_dialog_params *params =
527                         (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
528                     CredDialogHideBalloonTip(hwndDlg, params);
529                     return TRUE;
530                 }
531             }
532             return FALSE;
533         case WM_TIMER:
534             if (wParam == ID_CAPSLOCKPOP)
535             {
536                 struct cred_dialog_params *params =
537                     (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
538                 CredDialogHideBalloonTip(hwndDlg, params);
539                 return TRUE;
540             }
541             return FALSE;
542         case WM_DESTROY:
543         {
544             struct cred_dialog_params *params =
545                 (struct cred_dialog_params *)GetWindowLongPtrW(hwndDlg, DWLP_USER);
546             if (params->hwndBalloonTip) DestroyWindow(params->hwndBalloonTip);
547             return TRUE;
548         }
549         default:
550             return FALSE;
551     }
552 }
553 
554 static BOOL find_existing_credential(const WCHAR *target, WCHAR *username, ULONG len_username,
555                                      WCHAR *password, ULONG len_password)
556 {
557     DWORD count, i;
558     CREDENTIALW **credentials;
559 
560     if (!CredEnumerateW(target, 0, &count, &credentials)) return FALSE;
561     for (i = 0; i < count; i++)
562     {
563         if (credentials[i]->Type != CRED_TYPE_DOMAIN_PASSWORD &&
564             credentials[i]->Type != CRED_TYPE_GENERIC)
565         {
566             FIXME("no support for type %u credentials\n", credentials[i]->Type);
567             continue;
568         }
569         if ((!*username || !strcmpW(username, credentials[i]->UserName)) &&
570             strlenW(credentials[i]->UserName) < len_username &&
571             credentials[i]->CredentialBlobSize / sizeof(WCHAR) < len_password)
572         {
573             TRACE("found existing credential for %s\n", debugstr_w(credentials[i]->UserName));
574 
575             strcpyW(username, credentials[i]->UserName);
576             memcpy(password, credentials[i]->CredentialBlob, credentials[i]->CredentialBlobSize);
577             password[credentials[i]->CredentialBlobSize / sizeof(WCHAR)] = 0;
578 
579             CredFree(credentials);
580             return TRUE;
581         }
582     }
583     CredFree(credentials);
584     return FALSE;
585 }
586 
587 /******************************************************************************
588  *           CredUIPromptForCredentialsW [CREDUI.@]
589  */
590 DWORD WINAPI CredUIPromptForCredentialsW(PCREDUI_INFOW pUIInfo,
591                                          PCWSTR pszTargetName,
592                                          PCtxtHandle Reserved,
593                                          DWORD dwAuthError,
594                                          PWSTR pszUsername,
595                                          ULONG ulUsernameMaxChars,
596                                          PWSTR pszPassword,
597                                          ULONG ulPasswordMaxChars, PBOOL pfSave,
598                                          DWORD dwFlags)
599 {
600     INT_PTR ret;
601     struct cred_dialog_params params;
602     DWORD result = ERROR_SUCCESS;
603 
604     TRACE("(%p, %s, %p, %d, %s, %d, %p, %d, %p, 0x%08x)\n", pUIInfo,
605           debugstr_w(pszTargetName), Reserved, dwAuthError, debugstr_w(pszUsername),
606           ulUsernameMaxChars, pszPassword, ulPasswordMaxChars, pfSave, dwFlags);
607 
608     if ((dwFlags & (CREDUI_FLAGS_ALWAYS_SHOW_UI|CREDUI_FLAGS_GENERIC_CREDENTIALS)) == CREDUI_FLAGS_ALWAYS_SHOW_UI)
609         return ERROR_INVALID_FLAGS;
610 
611     if (!pszTargetName)
612         return ERROR_INVALID_PARAMETER;
613 
614     if ((dwFlags & CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX) && !pfSave)
615         return ERROR_INVALID_PARAMETER;
616 
617     if (!(dwFlags & CREDUI_FLAGS_ALWAYS_SHOW_UI) &&
618         !(dwFlags & CREDUI_FLAGS_INCORRECT_PASSWORD) &&
619         find_existing_credential(pszTargetName, pszUsername, ulUsernameMaxChars, pszPassword, ulPasswordMaxChars))
620         return ERROR_SUCCESS;
621 
622     params.pszTargetName = pszTargetName;
623     if (pUIInfo)
624     {
625         params.pszMessageText = pUIInfo->pszMessageText;
626         params.pszCaptionText = pUIInfo->pszCaptionText;
627         params.hbmBanner  = pUIInfo->hbmBanner;
628     }
629     else
630     {
631         params.pszMessageText = NULL;
632         params.pszCaptionText = NULL;
633         params.hbmBanner = NULL;
634     }
635     params.pszUsername = pszUsername;
636     params.ulUsernameMaxChars = ulUsernameMaxChars;
637     params.pszPassword = pszPassword;
638     params.ulPasswordMaxChars = ulPasswordMaxChars;
639     params.fSave = pfSave ? *pfSave : FALSE;
640     params.dwFlags = dwFlags;
641     params.hwndBalloonTip = NULL;
642     params.fBalloonTipActive = FALSE;
643 
644     ret = DialogBoxParamW(hinstCredUI, MAKEINTRESOURCEW(IDD_CREDDIALOG),
645                           pUIInfo ? pUIInfo->hwndParent : NULL,
646                           CredDialogProc, (LPARAM)&params);
647     if (ret <= 0)
648         return GetLastError();
649 
650     if (ret == IDCANCEL)
651     {
652         TRACE("dialog cancelled\n");
653         return ERROR_CANCELLED;
654     }
655 
656     if (pfSave)
657         *pfSave = params.fSave;
658 
659     if (params.fSave)
660     {
661         if (dwFlags & CREDUI_FLAGS_EXPECT_CONFIRMATION)
662         {
663             BOOL found = FALSE;
664             struct pending_credentials *entry;
665             int len;
666 
667             EnterCriticalSection(&csPendingCredentials);
668 
669             /* find existing pending credentials for the same target and overwrite */
670             /* FIXME: is this correct? */
671             LIST_FOR_EACH_ENTRY(entry, &pending_credentials_list, struct pending_credentials, entry)
672                 if (!strcmpW(pszTargetName, entry->pszTargetName))
673                 {
674                     found = TRUE;
675                     HeapFree(GetProcessHeap(), 0, entry->pszUsername);
676                     ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
677                     HeapFree(GetProcessHeap(), 0, entry->pszPassword);
678                 }
679 
680             if (!found)
681             {
682                 entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry));
683                 len = strlenW(pszTargetName);
684                 entry->pszTargetName = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
685                 memcpy(entry->pszTargetName, pszTargetName, (len + 1)*sizeof(WCHAR));
686                 list_add_tail(&pending_credentials_list, &entry->entry);
687             }
688 
689             len = strlenW(params.pszUsername);
690             entry->pszUsername = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
691             memcpy(entry->pszUsername, params.pszUsername, (len + 1)*sizeof(WCHAR));
692             len = strlenW(params.pszPassword);
693             entry->pszPassword = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
694             memcpy(entry->pszPassword, params.pszPassword, (len + 1)*sizeof(WCHAR));
695             entry->generic = (dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS) != 0;
696 
697             LeaveCriticalSection(&csPendingCredentials);
698         }
699         else if (!(dwFlags & CREDUI_FLAGS_DO_NOT_PERSIST))
700             result = save_credentials(pszTargetName, pszUsername, pszPassword,
701                                       (dwFlags & CREDUI_FLAGS_GENERIC_CREDENTIALS) != 0);
702     }
703 
704     return result;
705 }
706 
707 /******************************************************************************
708  *           CredUIConfirmCredentialsW [CREDUI.@]
709  */
710 DWORD WINAPI CredUIConfirmCredentialsW(PCWSTR pszTargetName, BOOL bConfirm)
711 {
712     struct pending_credentials *entry;
713     DWORD result = ERROR_NOT_FOUND;
714 
715     TRACE("(%s, %s)\n", debugstr_w(pszTargetName), bConfirm ? "TRUE" : "FALSE");
716 
717     if (!pszTargetName)
718         return ERROR_INVALID_PARAMETER;
719 
720     EnterCriticalSection(&csPendingCredentials);
721 
722     LIST_FOR_EACH_ENTRY(entry, &pending_credentials_list, struct pending_credentials, entry)
723     {
724         if (!strcmpW(pszTargetName, entry->pszTargetName))
725         {
726             if (bConfirm)
727                 result = save_credentials(entry->pszTargetName, entry->pszUsername,
728                                           entry->pszPassword, entry->generic);
729             else
730                 result = ERROR_SUCCESS;
731 
732             list_remove(&entry->entry);
733 
734             HeapFree(GetProcessHeap(), 0, entry->pszTargetName);
735             HeapFree(GetProcessHeap(), 0, entry->pszUsername);
736             ZeroMemory(entry->pszPassword, (strlenW(entry->pszPassword) + 1) * sizeof(WCHAR));
737             HeapFree(GetProcessHeap(), 0, entry->pszPassword);
738             HeapFree(GetProcessHeap(), 0, entry);
739 
740             break;
741         }
742     }
743 
744     LeaveCriticalSection(&csPendingCredentials);
745 
746     return result;
747 }
748 
749 /******************************************************************************
750  *           CredUIParseUserNameW [CREDUI.@]
751  */
752 DWORD WINAPI CredUIParseUserNameW(PCWSTR pszUserName, PWSTR pszUser,
753                                   ULONG ulMaxUserChars, PWSTR pszDomain,
754                                   ULONG ulMaxDomainChars)
755 {
756     PWSTR p;
757 
758     TRACE("(%s, %p, %d, %p, %d)\n", debugstr_w(pszUserName), pszUser,
759           ulMaxUserChars, pszDomain, ulMaxDomainChars);
760 
761     if (!pszUserName || !pszUser || !ulMaxUserChars || !pszDomain ||
762         !ulMaxDomainChars)
763         return ERROR_INVALID_PARAMETER;
764 
765     /* FIXME: handle marshaled credentials */
766 
767     p = strchrW(pszUserName, '\\');
768     if (p)
769     {
770         if (p - pszUserName > ulMaxDomainChars - 1)
771             return ERROR_INSUFFICIENT_BUFFER;
772         if (strlenW(p + 1) > ulMaxUserChars - 1)
773             return ERROR_INSUFFICIENT_BUFFER;
774         strcpyW(pszUser, p + 1);
775         memcpy(pszDomain, pszUserName, (p - pszUserName)*sizeof(WCHAR));
776         pszDomain[p - pszUserName] = '\0';
777 
778         return ERROR_SUCCESS;
779     }
780 
781     p = strrchrW(pszUserName, '@');
782     if (p)
783     {
784         if (p + 1 - pszUserName > ulMaxUserChars - 1)
785             return ERROR_INSUFFICIENT_BUFFER;
786         if (strlenW(p + 1) > ulMaxDomainChars - 1)
787             return ERROR_INSUFFICIENT_BUFFER;
788         strcpyW(pszDomain, p + 1);
789         memcpy(pszUser, pszUserName, (p - pszUserName)*sizeof(WCHAR));
790         pszUser[p - pszUserName] = '\0';
791 
792         return ERROR_SUCCESS;
793     }
794 
795     if (strlenW(pszUserName) > ulMaxUserChars - 1)
796         return ERROR_INSUFFICIENT_BUFFER;
797     strcpyW(pszUser, pszUserName);
798     pszDomain[0] = '\0';
799 
800     return ERROR_SUCCESS;
801 }
802 
803 /******************************************************************************
804  *           CredUIStoreSSOCredA [CREDUI.@]
805  */
806 DWORD WINAPI CredUIStoreSSOCredA(PCSTR pszRealm, PCSTR pszUsername,
807                                  PCSTR pszPassword, BOOL bPersist)
808 {
809     FIXME("(%s, %s, %p, %d)\n", debugstr_a(pszRealm), debugstr_a(pszUsername),
810           pszPassword, bPersist);
811     return ERROR_SUCCESS;
812 }
813 
814 /******************************************************************************
815  *           CredUIStoreSSOCredW [CREDUI.@]
816  */
817 DWORD WINAPI CredUIStoreSSOCredW(PCWSTR pszRealm, PCWSTR pszUsername,
818                                  PCWSTR pszPassword, BOOL bPersist)
819 {
820     FIXME("(%s, %s, %p, %d)\n", debugstr_w(pszRealm), debugstr_w(pszUsername),
821           pszPassword, bPersist);
822     return ERROR_SUCCESS;
823 }
824 
825 /******************************************************************************
826  *           CredUIReadSSOCredA [CREDUI.@]
827  */
828 DWORD WINAPI CredUIReadSSOCredA(PCSTR pszRealm, PSTR *ppszUsername)
829 {
830     FIXME("(%s, %p)\n", debugstr_a(pszRealm), ppszUsername);
831     if (ppszUsername)
832         *ppszUsername = NULL;
833     return ERROR_NOT_FOUND;
834 }
835 
836 /******************************************************************************
837  *           CredUIReadSSOCredW [CREDUI.@]
838  */
839 DWORD WINAPI CredUIReadSSOCredW(PCWSTR pszRealm, PWSTR *ppszUsername)
840 {
841     FIXME("(%s, %p)\n", debugstr_w(pszRealm), ppszUsername);
842     if (ppszUsername)
843         *ppszUsername = NULL;
844     return ERROR_NOT_FOUND;
845 }
846 
847 /******************************************************************************
848  * CredUIInitControls [CREDUI.@]
849  */
850 BOOL WINAPI CredUIInitControls(void)
851 {
852     FIXME("() stub\n");
853     return TRUE;
854 }
855 
856 /******************************************************************************
857  * SspiPromptForCredentialsW [CREDUI.@]
858  */
859 ULONG SEC_ENTRY SspiPromptForCredentialsW( PCWSTR target, void *info,
860                                            DWORD error, PCWSTR package,
861                                            PSEC_WINNT_AUTH_IDENTITY_OPAQUE input_id,
862                                            PSEC_WINNT_AUTH_IDENTITY_OPAQUE *output_id,
863                                            BOOL *save, DWORD sspi_flags )
864 {
865     static const WCHAR basicW[] = {'B','a','s','i','c',0};
866     static const WCHAR ntlmW[] = {'N','T','L','M',0};
867     static const WCHAR negotiateW[] = {'N','e','g','o','t','i','a','t','e',0};
868     WCHAR username[CREDUI_MAX_USERNAME_LENGTH + 1] = {0};
869     WCHAR password[CREDUI_MAX_PASSWORD_LENGTH + 1] = {0};
870     DWORD len_username = ARRAY_SIZE(username);
871     DWORD len_password = ARRAY_SIZE(password);
872     DWORD ret, flags;
873     CREDUI_INFOW *cred_info = info;
874     SEC_WINNT_AUTH_IDENTITY_W *id = input_id;
875 
876     FIXME( "(%s, %p, %u, %s, %p, %p, %p, %x) stub\n", debugstr_w(target), info,
877            error, debugstr_w(package), input_id, output_id, save, sspi_flags );
878 
879     if (!target) return ERROR_INVALID_PARAMETER;
880     if (!package || (strcmpiW( package, basicW ) && strcmpiW( package, ntlmW ) &&
881                      strcmpiW( package, negotiateW )))
882     {
883         FIXME( "package %s not supported\n", debugstr_w(package) );
884         return ERROR_NO_SUCH_PACKAGE;
885     }
886 
887     flags = CREDUI_FLAGS_ALWAYS_SHOW_UI | CREDUI_FLAGS_GENERIC_CREDENTIALS;
888 
889     if (sspi_flags & SSPIPFC_CREDPROV_DO_NOT_SAVE)
890         flags |= CREDUI_FLAGS_DO_NOT_PERSIST;
891 
892     if (!(sspi_flags & SSPIPFC_NO_CHECKBOX))
893         flags |= CREDUI_FLAGS_SHOW_SAVE_CHECK_BOX;
894 
895     if (!id) find_existing_credential( target, username, len_username, password, len_password );
896     else
897     {
898         if (id->User && id->UserLength > 0 && id->UserLength <= CREDUI_MAX_USERNAME_LENGTH)
899         {
900             memcpy( username, id->User, id->UserLength * sizeof(WCHAR) );
901             username[id->UserLength] = 0;
902         }
903         if (id->Password && id->PasswordLength > 0 && id->PasswordLength <= CREDUI_MAX_PASSWORD_LENGTH)
904         {
905             memcpy( password, id->Password, id->PasswordLength * sizeof(WCHAR) );
906             password[id->PasswordLength] = 0;
907         }
908     }
909 
910     if (!(ret = CredUIPromptForCredentialsW( cred_info, target, NULL, error, username,
911                                              len_username, password, len_password, save, flags )))
912     {
913         DWORD size = sizeof(*id), len_domain = 0;
914         WCHAR *ptr, *user = username, *domain = NULL;
915 
916         if ((ptr = strchrW( username, '\\' )))
917         {
918             user = ptr + 1;
919             len_username = strlenW( user );
920             if (!strcmpiW( package, ntlmW ) || !strcmpiW( package, negotiateW ))
921             {
922                 domain = username;
923                 len_domain = ptr - username;
924             }
925             *ptr = 0;
926         }
927         else len_username = strlenW( username );
928         len_password = strlenW( password );
929 
930         size += (len_username + 1) * sizeof(WCHAR);
931         size += (len_domain + 1) * sizeof(WCHAR);
932         size += (len_password + 1) * sizeof(WCHAR);
933         if (!(id = HeapAlloc( GetProcessHeap(), 0, size ))) return ERROR_OUTOFMEMORY;
934         ptr = (WCHAR *)(id + 1);
935 
936         memcpy( ptr, user, (len_username + 1) * sizeof(WCHAR) );
937         id->User           = ptr;
938         id->UserLength     = len_username;
939         ptr += len_username + 1;
940         if (len_domain)
941         {
942             memcpy( ptr, domain, (len_domain + 1) * sizeof(WCHAR) );
943             id->Domain         = ptr;
944             id->DomainLength   = len_domain;
945             ptr += len_domain + 1;
946         }
947         else
948         {
949             id->Domain         = NULL;
950             id->DomainLength   = 0;
951         }
952         memcpy( ptr, password, (len_password + 1) * sizeof(WCHAR) );
953         id->Password       = ptr;
954         id->PasswordLength = len_password;
955         id->Flags          = 0;
956 
957         *output_id = id;
958     }
959 
960     return ret;
961 }
962 
963 /******************************************************************************
964  * CredUIPromptForWindowsCredentialsW [CREDUI.@]
965  */
966 DWORD WINAPI CredUIPromptForWindowsCredentialsW( CREDUI_INFOW *info, DWORD error, ULONG *package,
967                                                  const void *in_buf, ULONG in_buf_size, void **out_buf,
968                                                  ULONG *out_buf_size, BOOL *save, DWORD flags )
969 {
970     FIXME( "(%p, %u, %p, %p, %u, %p, %p, %p, %08x) stub\n", info, error, package, in_buf, in_buf_size,
971            out_buf, out_buf_size, save, flags );
972     return ERROR_CALL_NOT_IMPLEMENTED;
973 }
974 
975 /******************************************************************************
976  * CredPackAuthenticationBufferW [CREDUI.@]
977  */
978 BOOL  WINAPI CredPackAuthenticationBufferW( DWORD flags, WCHAR *username, WCHAR *password, BYTE *buf,
979                                             DWORD *size )
980 {
981     FIXME( "(%08x, %s, %p, %p, %p) stub\n", flags, debugstr_w(username), password, buf, size );
982     return ERROR_CALL_NOT_IMPLEMENTED;
983 }
984 
985 /******************************************************************************
986  * CredUnPackAuthenticationBufferW [CREDUI.@]
987  */
988 BOOL  WINAPI CredUnPackAuthenticationBufferW( DWORD flags, void *buf, DWORD size, WCHAR *username,
989                                               DWORD *len_username, WCHAR *domain, DWORD *len_domain,
990                                               WCHAR *password, DWORD *len_password )
991 {
992     FIXME( "(%08x, %p, %u, %p, %p, %p, %p, %p, %p) stub\n", flags, buf, size, username, len_username,
993            domain, len_domain, password, len_password );
994     return ERROR_CALL_NOT_IMPLEMENTED;
995 }
996