VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxCredProv/VBoxCredential.cpp@ 40257

Last change on this file since 40257 was 35581, checked in by vboxsync, 14 years ago

VBoxCredProv: Added support for unlocking workstations.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.3 KB
Line 
1//
2// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
3// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
4// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
5// PARTICULAR PURPOSE.
6//
7// Copyright (c) 2006 Microsoft Corporation. All rights reserved.
8//
9// Modifications (c) 2009-2010 Oracle Corporation
10//
11
12#ifndef WIN32_NO_STATUS
13#include <ntstatus.h>
14#define WIN32_NO_STATUS
15#endif
16
17#include "VBoxCredProv.h"
18#include "VBoxCredential.h"
19#include "guid.h"
20
21#include <lm.h>
22
23#include <iprt/mem.h>
24#include <iprt/string.h>
25
26
27struct REPORT_RESULT_STATUS_INFO
28{
29 NTSTATUS ntsStatus;
30 NTSTATUS ntsSubstatus;
31 PWSTR pwzMessage;
32 CREDENTIAL_PROVIDER_STATUS_ICON cpsi;
33};
34
35static const REPORT_RESULT_STATUS_INFO s_rgLogonStatusInfo[] =
36{
37 { STATUS_LOGON_FAILURE, STATUS_SUCCESS, L"Incorrect password or username.", CPSI_ERROR, },
38 { STATUS_ACCOUNT_RESTRICTION, STATUS_ACCOUNT_DISABLED, L"The account is disabled.", CPSI_WARNING },
39};
40
41
42VBoxCredential::VBoxCredential(VBoxCredProv *pProvider) : m_cRef(1),
43 m_pCredProvCredentialEvents(NULL)
44{
45 Log(("VBoxCredential::VBoxCredential\n"));
46
47 DllAddRef();
48
49 AssertPtr(pProvider);
50 m_pProvider = pProvider;
51
52 ZeroMemory(m_rgCredProvFieldDescriptors, sizeof(m_rgCredProvFieldDescriptors));
53 ZeroMemory(m_rgFieldStatePairs, sizeof(m_rgFieldStatePairs));
54 ZeroMemory(m_rgFieldStrings, sizeof(m_rgFieldStrings));
55}
56
57
58VBoxCredential::~VBoxCredential()
59{
60 Log(("VBoxCredential::~VBoxCredential\n"));
61
62 Reset();
63
64 for (int i = 0; i < ARRAYSIZE(m_rgFieldStrings); i++)
65 {
66 CoTaskMemFree(m_rgFieldStrings[i]);
67 CoTaskMemFree(m_rgCredProvFieldDescriptors[i].pszLabel);
68 }
69
70 DllRelease();
71}
72
73
74void VBoxCredential::WipeString(const PWSTR pwszString)
75{
76 if (pwszString)
77 SecureZeroMemory(pwszString, wcslen(pwszString) * sizeof(WCHAR));
78}
79
80
81void VBoxCredential::Reset(void)
82{
83 WipeString(m_rgFieldStrings[SFI_USERNAME]);
84 WipeString(m_rgFieldStrings[SFI_PASSWORD]);
85 WipeString(m_rgFieldStrings[SFI_DOMAINNAME]);
86
87 if (m_pCredProvCredentialEvents)
88 {
89 m_pCredProvCredentialEvents->SetFieldString(this, SFI_USERNAME, NULL);
90 m_pCredProvCredentialEvents->SetFieldString(this, SFI_PASSWORD, NULL);
91 m_pCredProvCredentialEvents->SetFieldString(this, SFI_DOMAINNAME, NULL);
92 }
93}
94
95
96int VBoxCredential::Update(const char *pszUser,
97 const char *pszPw,
98 const char *pszDomain)
99{
100 Log(("VBoxCredential::Update: User=%s, Password=%s, Domain=%s\n",
101 pszUser ? pszUser : "NULL",
102 pszPw ? pszPw : "NULL",
103 pszDomain ? pszDomain : "NULL"));
104
105 PWSTR *ppwszStored;
106
107 /*
108 * Update domain name (can be NULL) and will
109 * be later replaced by the local computer name in the
110 * Kerberos authentication package or by the first part
111 * of the principal name.
112 */
113 if (pszDomain && strlen(pszDomain))
114 {
115 ppwszStored = &m_rgFieldStrings[SFI_DOMAINNAME];
116 CoTaskMemFree(*ppwszStored);
117 SHStrDupA(pszDomain, ppwszStored);
118 }
119
120 /* Update user name. */
121 if (pszUser && strlen(pszUser))
122 {
123 ppwszStored = &m_rgFieldStrings[SFI_USERNAME];
124 CoTaskMemFree(*ppwszStored);
125 SHStrDupA(pszUser, ppwszStored);
126
127 /*
128 * In case we got a "display name" (e.g. "John Doe")
129 * instead of the real user name (e.g. "jdoe") we have
130 * to translate the data first ...
131 */
132 PWSTR pwszAcount;
133 if (TranslateAccountName(*ppwszStored, &pwszAcount))
134 {
135 CoTaskMemFree(*ppwszStored);
136 m_rgFieldStrings[SFI_USERNAME] = pwszAcount;
137 }
138 else
139 {
140 /*
141 * Oky, no display name, but maybe it's a
142 * principal name from which we have to extract the
143 * domain from? (jdoe@my-domain.sub.net.com -> jdoe in
144 * domain my-domain.sub.net.com.)
145 */
146 PWSTR pwszDomain;
147 if (ExtractAccoutData(*ppwszStored, &pwszAcount, &pwszDomain))
148 {
149 /* Update user name. */
150 CoTaskMemFree(*ppwszStored);
151 m_rgFieldStrings[SFI_USERNAME] = pwszAcount;
152
153 /* Update domain. */
154 ppwszStored = &m_rgFieldStrings[SFI_DOMAINNAME];
155 CoTaskMemFree(*ppwszStored);
156 m_rgFieldStrings[SFI_DOMAINNAME] = pwszDomain;
157 }
158 }
159 }
160
161 /* Update password. */
162 if (pszPw && strlen(pszPw))
163 {
164 ppwszStored = &m_rgFieldStrings[SFI_PASSWORD];
165 CoTaskMemFree(*ppwszStored);
166 SHStrDupA(pszPw, ppwszStored);
167 }
168
169 Log(("VBoxCredential::Update: Finished - User=%ls, Password=%ls, Domain=%ls\n",
170 m_rgFieldStrings[SFI_USERNAME] ? m_rgFieldStrings[SFI_USERNAME] : L"NULL",
171 m_rgFieldStrings[SFI_PASSWORD] ? m_rgFieldStrings[SFI_PASSWORD] : L"NULL",
172 m_rgFieldStrings[SFI_DOMAINNAME] ? m_rgFieldStrings[SFI_DOMAINNAME] : L"NULL"));
173 return S_OK;
174}
175
176
177/*
178 * Initializes one credential with the field information passed in.
179 * Set the value of the SFI_USERNAME field to pwzUsername.
180 * Optionally takes a password for the SetSerialization case.
181 */
182HRESULT VBoxCredential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
183 const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* rgcpfd,
184 const FIELD_STATE_PAIR* rgfsp)
185{
186 Log(("VBoxCredential::Initialize: cpus=%ld, rgcpfd=%p, rgfsp=%p\n", cpus, rgcpfd, rgfsp));
187
188 HRESULT hr = S_OK;
189
190 m_cpUS = cpus;
191
192 /*
193 * Copy the field descriptors for each field. This is useful if you want to vary the
194 * field descriptors based on what Usage scenario the credential was created for.
195 */
196 for (DWORD i = 0; SUCCEEDED(hr) && i < ARRAYSIZE(m_rgCredProvFieldDescriptors); i++)
197 {
198 m_rgFieldStatePairs[i] = rgfsp[i];
199 hr = FieldDescriptorCopy(rgcpfd[i], &m_rgCredProvFieldDescriptors[i]);
200 }
201
202 /* Fill in the default value to make winlogon happy. */
203 hr = SHStrDupW(L"Submit", &m_rgFieldStrings[SFI_SUBMIT_BUTTON]);
204
205 return S_OK;
206}
207
208
209/*
210 * LogonUI calls this in order to give us a callback in case we need to notify it of anything.
211 * Store this callback pointer for later use.
212 */
213HRESULT VBoxCredential::Advise(ICredentialProviderCredentialEvents* pcpce)
214{
215 Log(("VBoxCredential::Advise\n"));
216
217 if (m_pCredProvCredentialEvents != NULL)
218 m_pCredProvCredentialEvents->Release();
219 m_pCredProvCredentialEvents = pcpce;
220 m_pCredProvCredentialEvents->AddRef();
221 return S_OK;
222}
223
224
225/* LogonUI calls this to tell us to release the callback. */
226HRESULT VBoxCredential::UnAdvise()
227{
228 Log(("VBoxCredential::UnAdvise\n"));
229
230 /*
231 * We're done with the current iteration, trigger a refresh of ourselves
232 * to reset credentials and to keep the logon UI clean (no stale entries anymore).
233 */
234 Reset();
235
236 /*
237 * Force a re-iteration of the provider (which will give zero credentials
238 * to try out because we just reset our one and only a line above.
239 */
240 if (m_pProvider)
241 m_pProvider->OnCredentialsProvided();
242
243 if (m_pCredProvCredentialEvents)
244 m_pCredProvCredentialEvents->Release();
245 m_pCredProvCredentialEvents = NULL;
246 return S_OK;
247}
248
249
250/*
251 * LogonUI calls this function when our tile is selected (zoomed).
252 * If you simply want fields to show/hide based on the selected state,
253 * there's no need to do anything here - you can set that up in the
254 * field definitions. But if you want to do something
255 * more complicated, like change the contents of a field when the tile is
256 * selected, you would do it here.
257 */
258HRESULT VBoxCredential::SetSelected(BOOL* pbAutoLogon)
259{
260 Log(("VBoxCredential::SetSelected\n"));
261
262 /*
263 * Don't do auto logon here because it would retry too often with
264 * every credential field (user name, password, domain, ...) which makes
265 * winlogon wait before new login attempts can be made.
266 */
267 *pbAutoLogon = FALSE;
268 return S_OK;
269}
270
271
272/*
273 * Similarly to SetSelected, LogonUI calls this when your tile was selected
274 * and now no longer is. The most common thing to do here (which we do below)
275 * is to clear out the password field.
276 */
277HRESULT VBoxCredential::SetDeselected()
278{
279 Log(("VBoxCredential::SetDeselected\n"));
280
281 HRESULT hr = S_OK;
282 if (m_rgFieldStrings[SFI_PASSWORD])
283 {
284 // CoTaskMemFree (below) deals with NULL, but StringCchLength does not.
285 size_t lenPassword;
286 hr = StringCchLengthW(m_rgFieldStrings[SFI_PASSWORD], 128, &(lenPassword));
287 if (SUCCEEDED(hr))
288 {
289 SecureZeroMemory(m_rgFieldStrings[SFI_PASSWORD], lenPassword * sizeof(*m_rgFieldStrings[SFI_PASSWORD]));
290
291 CoTaskMemFree(m_rgFieldStrings[SFI_PASSWORD]);
292 hr = SHStrDupW(L"", &m_rgFieldStrings[SFI_PASSWORD]);
293 }
294
295 if (SUCCEEDED(hr) && m_pCredProvCredentialEvents)
296 {
297 m_pCredProvCredentialEvents->SetFieldString(this, SFI_PASSWORD, m_rgFieldStrings[SFI_PASSWORD]);
298 }
299 }
300
301 return hr;
302}
303
304
305/*
306 * Gets info for a particular field of a tile. Called by logonUI to get information to
307 * display the tile.
308 */
309HRESULT VBoxCredential::GetFieldState(DWORD dwFieldID,
310 CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
311 CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis)
312{
313 Log(("VBoxCredential::GetFieldState: dwFieldID=%ld, pcpfs=%p, pcpfis=%p\n", dwFieldID, pcpfs, pcpfis));
314
315 HRESULT hr;
316
317 if ( (dwFieldID < ARRAYSIZE(m_rgFieldStatePairs))
318 && pcpfs
319 && pcpfis)
320 {
321 *pcpfs = m_rgFieldStatePairs[dwFieldID].cpfs;
322 *pcpfis = m_rgFieldStatePairs[dwFieldID].cpfis;
323
324 hr = S_OK;
325 }
326 else
327 {
328 hr = E_INVALIDARG;
329 }
330 return hr;
331}
332
333
334/*
335 * Searches the account name based on a display (real) name (e.g. "John Doe" -> "jdoe").
336 * Result "ppwszAccoutName" needs to be freed with CoTaskMemFree!
337 */
338BOOL VBoxCredential::TranslateAccountName(PWSTR pwszDisplayName, PWSTR *ppwszAccoutName)
339{
340 Log(("VBoxCredential::TranslateAccountName\n"));
341
342 AssertPtr(pwszDisplayName);
343 Log(("VBoxCredential::TranslateAccountName: Getting account name for '%ls' ...\n", pwszDisplayName));
344
345 /** @todo Do we need ADS support (e.g. TranslateNameW) here? */
346 BOOL fFound = FALSE; /* Did we find the desired user? */
347 NET_API_STATUS nStatus;
348 DWORD dwLevel = 2; /* Detailed information about user accounts. */
349 DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH;
350 DWORD dwEntriesRead = 0;
351 DWORD dwTotalEntries = 0;
352 DWORD dwResumeHandle = 0;
353 LPUSER_INFO_2 pBuf = NULL;
354 LPUSER_INFO_2 pCurBuf = NULL;
355 do
356 {
357 nStatus = NetUserEnum(NULL, /* Server name, NULL for localhost. */
358 dwLevel,
359 FILTER_NORMAL_ACCOUNT,
360 (LPBYTE*)&pBuf,
361 dwPrefMaxLen,
362 &dwEntriesRead,
363 &dwTotalEntries,
364 &dwResumeHandle);
365 if ( (nStatus == NERR_Success)
366 || (nStatus == ERROR_MORE_DATA))
367 {
368 if ((pCurBuf = pBuf) != NULL)
369 {
370 for (DWORD i = 0; i < dwEntriesRead; i++)
371 {
372 /*
373 * Search for the "display name" - that might be
374 * "John Doe" or something similar the user recognizes easier
375 * and may not the same as the "account" name (e.g. "jdoe").
376 */
377 if ( pCurBuf
378 && pCurBuf->usri2_full_name
379 && StrCmpI(pwszDisplayName, pCurBuf->usri2_full_name) == 0)
380 {
381 /*
382 * Copy the real user name (e.g. "jdoe") to our
383 * output buffer.
384 */
385 LPWSTR pwszTemp;
386 HRESULT hr = SHStrDupW(pCurBuf->usri2_name, &pwszTemp);
387 if (hr == S_OK)
388 {
389 *ppwszAccoutName = pwszTemp;
390 fFound = TRUE;
391 }
392 else
393 Log(("VBoxCredential::TranslateAccountName: Error copying data, hr=%08x\n", hr));
394 break;
395 }
396 pCurBuf++;
397 }
398 }
399 if (pBuf != NULL)
400 {
401 NetApiBufferFree(pBuf);
402 pBuf = NULL;
403 }
404 }
405 } while (nStatus == ERROR_MORE_DATA && !fFound);
406
407 if (pBuf != NULL)
408 {
409 NetApiBufferFree(pBuf);
410 pBuf = NULL;
411 }
412
413 Log(("VBoxCredential::TranslateAccountName: Returned nStatus=%ld, fFound=%s\n",
414 nStatus, fFound ? "Yes" : "No"));
415 return fFound;
416
417#if 0
418 DWORD dwErr = NO_ERROR;
419 ULONG cbLen = 0;
420 if ( TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, NULL, &cbLen)
421 && cbLen > 0)
422 {
423 Log(("VBoxCredential::GetAccountName: Translated ADS name has %u characters\n", cbLen));
424
425 ppwszAccoutName = (PWSTR)RTMemAlloc(cbLen * sizeof(WCHAR));
426 AssertPtrReturn(pwszName, FALSE);
427 if (TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, ppwszAccoutName, &cbLen))
428 {
429 Log(("VBoxCredential::GetAccountName: Real ADS account name of '%ls' is '%ls'\n",
430 pwszName, ppwszAccoutName));
431 }
432 else
433 {
434 RTMemFree(ppwszAccoutName);
435 dwErr = GetLastError();
436 }
437 }
438 else
439 dwErr = GetLastError();
440 /* The above method for looking up in ADS failed, try another one. */
441 if (dwErr != NO_ERROR)
442 {
443 dwErr = NO_ERROR;
444
445 }
446#endif
447}
448
449
450/*
451 * Extracts the actual account name & domain from a (raw) account data string. This might
452 * be a principal or FQDN string.
453 */
454BOOL VBoxCredential::ExtractAccoutData(PWSTR pwszAccountData, PWSTR *ppwszAccoutName, PWSTR *ppwszDomain)
455{
456 Log(("VBoxCredential::ExtractAccoutData\n"));
457
458 AssertPtr(pwszAccountData);
459 Log(("VBoxCredential::ExtractAccoutData: Getting account name for '%ls' ...\n", pwszAccountData));
460 HRESULT hr = E_FAIL;
461
462 /* Try to figure out whether this is a principal name (user@domain). */
463 LPWSTR pPos = NULL;
464 if ( (pPos = StrChrW(pwszAccountData, L'@')) != NULL
465 && pPos != pwszAccountData)
466 {
467 DWORD cbSize = (pPos - pwszAccountData) * sizeof(WCHAR);
468 LPWSTR pwszName = (LPWSTR)CoTaskMemAlloc(cbSize + sizeof(WCHAR)); /* Space for terminating zero. */
469 LPWSTR pwszDomain = NULL;
470 AssertPtr(pwszName);
471 hr = StringCbCopyN(pwszName, cbSize + sizeof(WCHAR), pwszAccountData, cbSize);
472 if (SUCCEEDED(hr))
473 {
474 *ppwszAccoutName = pwszName;
475 *pPos++; /* Skip @, point to domain name (if any). */
476 if ( pPos != NULL
477 && *pPos != L'\0')
478 {
479 hr = SHStrDupW(pPos, &pwszDomain);
480 if (SUCCEEDED(hr))
481 {
482 *ppwszDomain = pwszDomain;
483 }
484 else
485 Log(("VBoxCredential::ExtractAccoutData/Principal: Error copying domain data, hr=%08x\n", hr));
486 }
487 else
488 {
489 hr = E_FAIL;
490 Log(("VBoxCredential::ExtractAccoutData/Principal: No domain name found!\n"));
491 }
492 }
493 else
494 Log(("VBoxCredential::ExtractAccoutData/Principal: Error copying account data, hr=%08x\n", hr));
495
496 if (hr != S_OK)
497 {
498 CoTaskMemFree(pwszName);
499 if (pwszDomain)
500 CoTaskMemFree(pwszDomain);
501 }
502 }
503 else
504 Log(("VBoxCredential::ExtractAccoutData/Principal: No valid principal account name found!\n"));
505
506 return (hr == S_OK);
507}
508
509
510/* Sets ppwsz to the string value of the field at the index dwFieldID. */
511HRESULT VBoxCredential::GetStringValue(DWORD dwFieldID,
512 PWSTR *ppwszString)
513{
514 /* Check to make sure dwFieldID is a legitimate index. */
515 HRESULT hr;
516 if ( dwFieldID < ARRAYSIZE(m_rgCredProvFieldDescriptors)
517 && ppwszString)
518 {
519 switch (dwFieldID)
520 {
521 /** @todo Add more specific field IDs here if needed. */
522
523 default:
524
525 /*
526 * Make a copy of the string and return that, the caller is responsible for freeing it.
527 * Note that there can be empty fields (like a missing domain name); handle them
528 * by writing an empty string.
529 */
530 hr = SHStrDupW(m_rgFieldStrings[dwFieldID] ? m_rgFieldStrings[dwFieldID] : L"",
531 ppwszString);
532 break;
533 }
534 if (SUCCEEDED(hr))
535 Log(("VBoxCredential::GetStringValue: dwFieldID=%ld, ppwszString=%ls\n", dwFieldID, *ppwszString));
536 }
537 else
538 hr = E_INVALIDARG;
539 return hr;
540}
541
542
543/*
544 * Sets pdwAdjacentTo to the index of the field the submit button should be
545 * adjacent to. We recommend that the submit button is placed next to the last
546 * field which the user is required to enter information in. Optional fields
547 * should be below the submit button.
548 */
549HRESULT VBoxCredential::GetSubmitButtonValue(DWORD dwFieldID,
550 DWORD* pdwAdjacentTo)
551{
552 Log(("VBoxCredential::GetSubmitButtonValue: dwFieldID=%ld\n", dwFieldID));
553
554 HRESULT hr;
555
556 /* Validate parameters. */
557 if ((SFI_SUBMIT_BUTTON == dwFieldID) && pdwAdjacentTo)
558 {
559 /* pdwAdjacentTo is a pointer to the fieldID you want the submit button to appear next to. */
560 *pdwAdjacentTo = SFI_PASSWORD;
561 Log(("VBoxCredential::GetSubmitButtonValue: dwFieldID=%ld, *pdwAdjacentTo=%ld\n", dwFieldID, *pdwAdjacentTo));
562 hr = S_OK;
563 }
564 else
565 {
566 hr = E_INVALIDARG;
567 }
568 return hr;
569}
570
571
572/*
573 * Sets the value of a field which can accept a string as a value.
574 * This is called on each keystroke when a user types into an edit field.
575 */
576HRESULT VBoxCredential::SetStringValue(DWORD dwFieldID,
577 PCWSTR pcwzString)
578{
579 Log(("VBoxCredential::SetStringValue: dwFieldID=%ld, pcwzString=%ls\n",
580 dwFieldID, pcwzString));
581
582 HRESULT hr;
583
584 /*
585 * We don't set any values into fields (e.g. the password, hidden
586 * by dots), instead keep it secret by resetting all credentials.
587 */
588#if 0
589 /* Validate parameters. */
590 if ( dwFieldID < ARRAYSIZE(m_rgCredProvFieldDescriptors)
591 && ( CPFT_EDIT_TEXT == m_rgCredProvFieldDescriptors[dwFieldID].cpft
592 || CPFT_PASSWORD_TEXT == m_rgCredProvFieldDescriptors[dwFieldID].cpft))
593 {
594 PWSTR* ppwszStored = &m_rgFieldStrings[dwFieldID];
595 CoTaskMemFree(*ppwszStored);
596 hr = SHStrDupW(pwz, ppwszStored);
597 }
598 else
599 {
600 hr = E_INVALIDARG;
601 }
602#else
603 hr = E_NOTIMPL;
604#endif
605 return hr;
606}
607
608
609/*
610 * The following methods are for logonUI to get the values of various UI elements and then communicate
611 * to the credential about what the user did in that field. However, these methods are not implemented
612 * because our tile doesn't contain these types of UI elements.
613 */
614HRESULT VBoxCredential::GetBitmapValue(DWORD dwFieldID,
615 HBITMAP* phbmp)
616{
617 UNREFERENCED_PARAMETER(dwFieldID);
618 UNREFERENCED_PARAMETER(phbmp);
619
620 /* We don't do own bitmaps */
621 return E_NOTIMPL;
622}
623
624
625HRESULT VBoxCredential::GetCheckboxValue(DWORD dwFieldID,
626 BOOL* pbChecked,
627 PWSTR* ppwszLabel)
628{
629 UNREFERENCED_PARAMETER(dwFieldID);
630 UNREFERENCED_PARAMETER(pbChecked);
631 UNREFERENCED_PARAMETER(ppwszLabel);
632 return E_NOTIMPL;
633}
634
635
636HRESULT VBoxCredential::GetComboBoxValueCount(DWORD dwFieldID,
637 DWORD* pcItems,
638 DWORD* pdwSelectedItem)
639{
640 UNREFERENCED_PARAMETER(dwFieldID);
641 UNREFERENCED_PARAMETER(pcItems);
642 UNREFERENCED_PARAMETER(pdwSelectedItem);
643 return E_NOTIMPL;
644}
645
646
647HRESULT VBoxCredential::GetComboBoxValueAt(DWORD dwFieldID,
648 DWORD dwItem,
649 PWSTR* ppwszItem)
650{
651 UNREFERENCED_PARAMETER(dwFieldID);
652 UNREFERENCED_PARAMETER(dwItem);
653 UNREFERENCED_PARAMETER(ppwszItem);
654 return E_NOTIMPL;
655}
656
657
658HRESULT VBoxCredential::SetCheckboxValue(DWORD dwFieldID,
659 BOOL bChecked)
660{
661 UNREFERENCED_PARAMETER(dwFieldID);
662 UNREFERENCED_PARAMETER(bChecked);
663 return E_NOTIMPL;
664}
665
666
667HRESULT VBoxCredential::SetComboBoxSelectedValue(DWORD dwFieldId,
668 DWORD dwSelectedItem)
669{
670 UNREFERENCED_PARAMETER(dwFieldId);
671 UNREFERENCED_PARAMETER(dwSelectedItem);
672 return E_NOTIMPL;
673}
674
675
676HRESULT VBoxCredential::CommandLinkClicked(DWORD dwFieldID)
677{
678 UNREFERENCED_PARAMETER(dwFieldID);
679 return E_NOTIMPL;
680}
681
682
683/*
684 * Collect the username and password into a serialized credential for the correct usage scenario
685 * LogonUI then passes these credentials back to the system to log on.
686 */
687HRESULT VBoxCredential::GetSerialization(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *pcpGetSerializationResponse,
688 CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpCredentialSerialization,
689 PWSTR *ppwszOptionalStatusText,
690 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
691{
692 Log(("VBoxCredential::GetSerialization: pcpGetSerializationResponse=%p, pcpCredentialSerialization=%p, ppwszOptionalStatusText=%p, pcpsiOptionalStatusIcon=%p\n",
693 pcpGetSerializationResponse, pcpCredentialSerialization, ppwszOptionalStatusText, pcpsiOptionalStatusIcon));
694
695 UNREFERENCED_PARAMETER(ppwszOptionalStatusText);
696 UNREFERENCED_PARAMETER(pcpsiOptionalStatusIcon);
697
698 KERB_INTERACTIVE_UNLOCK_LOGON kiul;
699 ZeroMemory(&kiul, sizeof(kiul));
700
701 /* Save a pointer to the interactive logon struct. */
702 KERB_INTERACTIVE_LOGON* pkil = &kiul.Logon;
703 AssertPtr(pkil);
704
705 HRESULT hr;
706
707 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH+1];
708 DWORD cch = ARRAYSIZE(wszComputerName);
709 if (GetComputerNameW(wszComputerName, &cch))
710 {
711 /* Is a domain name missing? Then use the name of the local computer. */
712 if (NULL == m_rgFieldStrings [SFI_DOMAINNAME])
713 hr = UnicodeStringInitWithString(wszComputerName, &pkil->LogonDomainName);
714 else
715 hr = UnicodeStringInitWithString(m_rgFieldStrings [SFI_DOMAINNAME],
716 &pkil->LogonDomainName);
717
718 /* Fill in the username and password. */
719 if (SUCCEEDED(hr))
720 {
721 hr = UnicodeStringInitWithString(m_rgFieldStrings[SFI_USERNAME], &pkil->UserName);
722 if (SUCCEEDED(hr))
723 {
724 hr = UnicodeStringInitWithString(m_rgFieldStrings[SFI_PASSWORD], &pkil->Password);
725 if (SUCCEEDED(hr))
726 {
727 /* Set credential type according to current usage scenario. */
728 AssertPtr(pkil);
729 switch (m_cpUS)
730 {
731 case CPUS_UNLOCK_WORKSTATION:
732 pkil->MessageType = KerbWorkstationUnlockLogon;
733 break;
734
735 case CPUS_LOGON:
736 pkil->MessageType = KerbInteractiveLogon;
737 break;
738
739 case CPUS_CREDUI:
740 pkil->MessageType = (KERB_LOGON_SUBMIT_TYPE)0; /* No message type required here. */
741 break;
742
743 default:
744 hr = E_FAIL;
745 break;
746 }
747
748 if (SUCCEEDED(hr))
749 {
750 /* Allocate copies of, and package, the strings in a binary blob. */
751 hr = KerbInteractiveUnlockLogonPack(kiul,
752 &pcpCredentialSerialization->rgbSerialization,
753 &pcpCredentialSerialization->cbSerialization);
754 }
755 if (SUCCEEDED(hr))
756 {
757 ULONG ulAuthPackage;
758 hr = RetrieveNegotiateAuthPackage(&ulAuthPackage);
759 if (SUCCEEDED(hr))
760 {
761 pcpCredentialSerialization->ulAuthenticationPackage = ulAuthPackage;
762 pcpCredentialSerialization->clsidCredentialProvider = CLSID_VBoxCredProvider;
763
764 /*
765 * At this point the credential has created the serialized credential used for logon
766 * By setting this to CPGSR_RETURN_CREDENTIAL_FINISHED we are letting logonUI know
767 * that we have all the information we need and it should attempt to submit the
768 * serialized credential.
769 */
770 *pcpGetSerializationResponse = CPGSR_RETURN_CREDENTIAL_FINISHED;
771 }
772 }
773 }
774 }
775 }
776 }
777 else
778 {
779 DWORD dwErr = GetLastError();
780 hr = HRESULT_FROM_WIN32(dwErr);
781 }
782
783 Log(("VBoxCredential::GetSerialization: Returned 0x%08x\n", hr));
784 return hr;
785}
786
787
788/*
789 * ReportResult is completely optional. Its purpose is to allow a credential to customize the string
790 * and the icon displayed in the case of a logon failure. For example, we have chosen to
791 * customize the error shown in the case of bad username/password and in the case of the account
792 * being disabled.
793 */
794HRESULT VBoxCredential::ReportResult(NTSTATUS ntsStatus,
795 NTSTATUS ntsSubstatus,
796 PWSTR* ppwszOptionalStatusText,
797 CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon)
798{
799 *ppwszOptionalStatusText = NULL;
800 *pcpsiOptionalStatusIcon = CPSI_NONE;
801
802 DWORD dwStatusInfo = (DWORD)-1;
803
804 /* Look for a match on status and substatus. */
805 for (DWORD i = 0; i < ARRAYSIZE(s_rgLogonStatusInfo); i++)
806 {
807 if (s_rgLogonStatusInfo[i].ntsStatus == ntsStatus && s_rgLogonStatusInfo[i].ntsSubstatus == ntsSubstatus)
808 {
809 dwStatusInfo = i;
810 break;
811 }
812 }
813
814 if ((DWORD)-1 != dwStatusInfo)
815 {
816 if (SUCCEEDED(SHStrDupW(s_rgLogonStatusInfo[dwStatusInfo].pwzMessage, ppwszOptionalStatusText)))
817 *pcpsiOptionalStatusIcon = s_rgLogonStatusInfo[dwStatusInfo].cpsi;
818 }
819
820 /* Try to lookup a text message for error code. */
821 LPVOID lpMessageBuffer = NULL;
822 HMODULE hNtDLL = LoadLibrary(L"NTDLL.DLL");
823 if (hNtDLL)
824 {
825 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
826 | FORMAT_MESSAGE_FROM_SYSTEM
827 | FORMAT_MESSAGE_FROM_HMODULE,
828 hNtDLL,
829 ntsStatus,
830 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
831 (LPTSTR) &lpMessageBuffer,
832 0,
833 NULL);
834 FreeLibrary(hNtDLL);
835 }
836
837 Log(("VBoxCredential::ReportResult: ntsStatus=%ld, ntsSubstatus=%ld, ppwszOptionalStatusText=%p, pcpsiOptionalStatusIcon=%p, dwStatusInfo=%ld, Message=%ls\n",
838 ntsStatus, ntsSubstatus, ppwszOptionalStatusText, pcpsiOptionalStatusIcon, dwStatusInfo, lpMessageBuffer ? lpMessageBuffer : L"<None>"));
839
840 /* Print to user-friendly message to release log. */
841 if (FAILED(ntsStatus))
842 {
843 if (lpMessageBuffer)
844 LogRel(("VBoxCredProv: %ls\n", lpMessageBuffer));
845 /* Login attempt failed; reset and clear all data. */
846 Reset();
847 }
848
849 if (lpMessageBuffer)
850 LocalFree(lpMessageBuffer);
851
852 /*
853 * Since NULL is a valid value for *ppwszOptionalStatusText and *pcpsiOptionalStatusIcon
854 * this function can't fail
855 */
856 return S_OK;
857}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette