VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxCredProv/VBoxCredProvCredential.cpp@ 40348

Last change on this file since 40348 was 40348, checked in by vboxsync, 13 years ago

VBoxCredProv: Logging adjustment.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.8 KB
Line 
1/* $Id: VBoxCredProvCredential.cpp 40348 2012-03-05 09:10:24Z vboxsync $ */
2/** @file
3 * VBoxCredProvCredential - Class for keeping and handling the passed
4 * credentials.
5 */
6
7/*
8 * Copyright (C) 2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#ifndef WIN32_NO_STATUS
20#include <ntstatus.h>
21#define WIN32_NO_STATUS
22#endif
23
24#include "VBoxCredentialProvider.h"
25
26#include "VBoxCredProvProvider.h"
27#include "VBoxCredProvCredential.h"
28#include "VBoxCredProvUtils.h"
29
30#include <lm.h>
31
32#include <iprt/mem.h>
33#include <iprt/string.h>
34
35
36
37VBoxCredProvCredential::VBoxCredProvCredential(VBoxCredProvProvider *pProvider) :
38 m_cRefCount(1),
39 m_pEvents(NULL)
40{
41 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Created, pProvider=%p\n",
42 pProvider);
43
44 VBoxCredentialProviderAcquire();
45
46 AssertPtr(pProvider);
47 m_pProvider = pProvider;
48
49 ZeroMemory(m_pwszCredentials, sizeof(m_pwszCredentials));
50}
51
52
53VBoxCredProvCredential::~VBoxCredProvCredential(void)
54{
55 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Destroying\n");
56
57 VBoxCredentialProviderRelease();
58}
59
60
61/* IUnknown overrides. */
62ULONG VBoxCredProvCredential::AddRef(void)
63{
64 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Increasing reference to %ld\n",
65 m_cRefCount + 1);
66
67 return m_cRefCount++;
68}
69
70
71ULONG VBoxCredProvCredential::Release(void)
72{
73 Assert(m_cRefCount);
74
75 ULONG cRefCount = --m_cRefCount;
76 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Decreasing reference to %ld\n",
77 cRefCount);
78 if (!cRefCount)
79 {
80 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Calling destructor\n");
81 delete this;
82 }
83 return cRefCount;
84}
85
86
87HRESULT VBoxCredProvCredential::QueryInterface(REFIID interfaceID, void **ppvInterface)
88{
89 HRESULT hr = S_OK;;
90 if (ppvInterface)
91 {
92 if ( IID_IUnknown == interfaceID
93 || IID_ICredentialProviderCredential == interfaceID)
94 {
95 *ppvInterface = static_cast<IUnknown*>(this);
96 reinterpret_cast<IUnknown*>(*ppvInterface)->AddRef();
97 }
98 else
99 {
100 *ppvInterface = NULL;
101 hr = E_NOINTERFACE;
102 }
103 }
104 else
105 hr = E_INVALIDARG;
106
107 return hr;
108}
109
110
111/**
112 * Assigns or copies a RTUTF16 string to a UNICODE_STRING.
113 *
114 * When fCopy is false, this does *not* copy its contents
115 * and only assigns its code points to the destination!
116 * When fCopy is true, the actual string buffer gets copied.
117 *
118 * Does not take terminating \0 into account.
119 *
120 * @return HRESULT
121 * @param pUnicodeDest Unicode string assigning the UTF16 string to.
122 * @param pwszSource UTF16 string to assign.
123 * @param fCopy Whether to just assign or copy the actual buffer
124 * contents from source -> dest.
125 */
126HRESULT VBoxCredProvCredential::RTUTF16toToUnicode(PUNICODE_STRING pUnicodeDest, PRTUTF16 pwszSource,
127 bool fCopy)
128{
129 AssertPtrReturn(pwszSource, VERR_INVALID_POINTER);
130 AssertPtrReturn(pUnicodeDest, VERR_INVALID_POINTER);
131
132 size_t cbLen = RTUtf16Len(pwszSource) * sizeof(RTUTF16);
133
134 pUnicodeDest->Length = cbLen;
135 pUnicodeDest->MaximumLength = pUnicodeDest->Length;
136
137 if (fCopy)
138 {
139 CopyMemory(pUnicodeDest->Buffer, pwszSource, cbLen);
140 }
141 else /* Just assign the buffer. */
142 pUnicodeDest->Buffer = pwszSource;
143
144 return S_OK;
145}
146
147
148HRESULT VBoxCredProvCredential::AllocateLogonPackage(const KERB_INTERACTIVE_UNLOCK_LOGON& UnlockLogon,
149 PBYTE *ppPackage, DWORD *pcbPackage)
150{
151 AssertPtrReturn(ppPackage, E_INVALIDARG);
152 AssertPtrReturn(pcbPackage, E_INVALIDARG);
153
154 const KERB_INTERACTIVE_LOGON *pLogonIn = &UnlockLogon.Logon;
155
156 /*
157 * First, allocate enough space for the logon structure itself and separate
158 * string buffers right after it to store the actual user, password and domain
159 * credentials.
160 */
161 DWORD cbLogon = sizeof(KERB_INTERACTIVE_UNLOCK_LOGON)
162 + pLogonIn->LogonDomainName.Length +
163 + pLogonIn->UserName.Length +
164 + pLogonIn->Password.Length;
165
166#ifdef DEBUG
167 VBoxCredProvVerbose(3, "VBoxCredProvCredential: AllocateLogonPackage: Allocating %ld bytes (%d bytes credentials)\n",
168 cbLogon, cbLogon - sizeof(KERB_INTERACTIVE_UNLOCK_LOGON));
169#endif
170
171 KERB_INTERACTIVE_UNLOCK_LOGON *pLogon = (KERB_INTERACTIVE_UNLOCK_LOGON*)CoTaskMemAlloc(cbLogon);
172 if (!pLogon)
173 return E_OUTOFMEMORY;
174
175 /* Let our byte buffer point to the end of our allocated structure so that it can
176 * be used to store the credential data sequentially in a binary blob
177 * (without terminating \0). */
178 PBYTE pbBuffer = (PBYTE)pLogon + sizeof(KERB_INTERACTIVE_UNLOCK_LOGON);
179
180 /* The buffer of the packed destination string does not contain the actual
181 * string content but a relative offset starting at the given
182 * KERB_INTERACTIVE_UNLOCK_LOGON structure. */
183#define KERB_CRED_INIT_PACKED(StringDst, StringSrc, LogonOffset) \
184 StringDst.Length = StringSrc.Length; \
185 StringDst.MaximumLength = StringSrc.Length; \
186 StringDst.Buffer = (PWSTR)pbBuffer; \
187 CopyMemory(StringDst.Buffer, StringSrc.Buffer, StringDst.Length); \
188 StringDst.Buffer = (PWSTR)(pbBuffer - (PBYTE)LogonOffset); \
189 pbBuffer += StringDst.Length;
190
191 ZeroMemory(&pLogon->LogonId, sizeof(LUID));
192
193 KERB_INTERACTIVE_LOGON *pLogonOut = &pLogon->Logon;
194 pLogonOut->MessageType = pLogonIn->MessageType;
195
196 KERB_CRED_INIT_PACKED(pLogonOut->LogonDomainName, pLogonIn->LogonDomainName, pLogon);
197 KERB_CRED_INIT_PACKED(pLogonOut->UserName , pLogonIn->UserName, pLogon);
198 KERB_CRED_INIT_PACKED(pLogonOut->Password , pLogonIn->Password, pLogon);
199
200 *ppPackage = (PBYTE)pLogon;
201 *pcbPackage = cbLogon;
202
203#undef KERB_CRED_INIT_PACKED
204
205 return S_OK;
206}
207
208
209void VBoxCredProvCredential::Reset(void)
210{
211 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Wiping credentials ...\n");
212
213 VbglR3CredentialsDestroyUtf16(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
214 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
215 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME],
216 3 /* Passes */);
217 if (m_pEvents)
218 {
219 m_pEvents->SetFieldString(this, VBOXCREDPROV_FIELDID_USERNAME, NULL);
220 m_pEvents->SetFieldString(this, VBOXCREDPROV_FIELDID_PASSWORD, NULL);
221 m_pEvents->SetFieldString(this, VBOXCREDPROV_FIELDID_DOMAINNAME, NULL);
222 }
223}
224
225
226int VBoxCredProvCredential::RetrieveCredentials(void)
227{
228 int rc = VbglR3CredentialsQueryAvailability();
229 if (RT_SUCCESS(rc))
230 {
231 /*
232 * Set status to "terminating" to let the host know this module now
233 * tries to receive and use passed credentials so that credentials from
234 * the host won't be sent twice.
235 */
236 VBoxCredProvReportStatus(VBoxGuestFacilityStatus_Terminating);
237
238 rc = VbglR3CredentialsRetrieveUtf16(&m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
239 &m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
240 &m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]);
241
242 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Retrieving credentials returned rc=%Rrc\n", rc);
243 }
244
245 if (RT_SUCCESS(rc))
246 {
247 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Got credentials: User=%ls, Password=%ls, Domain=%ls\n",
248 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
249#ifdef DEBUG
250 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
251#else
252 L"XXX" /* Don't show any passwords in release mode. */,
253#endif
254 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]);
255
256 /*
257 * In case we got a "display name" (e.g. "John Doe")
258 * instead of the real user name (e.g. "jdoe") we have
259 * to translate the data first ...
260 */
261 PWSTR pwszAcount;
262 if (TranslateAccountName(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME], &pwszAcount))
263 {
264 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Translated account name %ls -> %ls\n",
265 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME], pwszAcount);
266
267 RTUtf16Free(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME]);
268 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME] = pwszAcount;
269 }
270 else
271 {
272 /*
273 * Oky, no display name, but maybe it's a
274 * principal name from which we have to extract the
275 * domain from? ([email protected] -> jdoe in
276 * domain my-domain.sub.net.com.)
277 */
278 PWSTR pwszDomain;
279 if (ExtractAccoutData(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
280 &pwszAcount, &pwszDomain))
281 {
282 /* Update user name. */
283 RTUtf16Free(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME]);
284 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME] = pwszAcount;
285
286 /* Update domain. */
287 RTUtf16Free(m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]);
288 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME] = pwszDomain;
289
290 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Extracted account data is pwszAccount=%ls, pwszDomain=%ls\n",
291 pwszAcount, pwszDomain);
292 }
293 }
294 }
295
296 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Checking credentials returned with rc=%Rrc\n", rc);
297 return rc;
298}
299
300
301/*
302 * Initializes one credential with the field information passed in.
303 * Set the value of the VBOXCREDPROV_FIELDID_USERNAME field to pwzUsername.
304 * Optionally takes a password for the SetSerialization case.
305 */
306HRESULT VBoxCredProvCredential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpUS)
307{
308 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Initialize: cpUS=%ld\n", cpUS);
309
310 m_cpUS = cpUS;
311
312 return S_OK;
313}
314
315
316/*
317 * LogonUI calls this in order to give us a callback in case we need to notify it of anything.
318 * Store this callback pointer for later use.
319 */
320HRESULT VBoxCredProvCredential::Advise(ICredentialProviderCredentialEvents* pcpce)
321{
322 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Advise\n");
323
324 if (m_pEvents != NULL)
325 m_pEvents->Release();
326 m_pEvents = pcpce;
327 m_pEvents->AddRef();
328 return S_OK;
329}
330
331
332/* LogonUI calls this to tell us to release the callback. */
333HRESULT VBoxCredProvCredential::UnAdvise(void)
334{
335 VBoxCredProvVerbose(0, "VBoxCredProvCredential: UnAdvise\n");
336
337 /*
338 * We're done with the current iteration, trigger a refresh of ourselves
339 * to reset credentials and to keep the logon UI clean (no stale entries anymore).
340 */
341 Reset();
342
343 /*
344 * Force a re-iteration of the provider (which will give zero credentials
345 * to try out because we just reset our one and only a line above.
346 */
347 if (m_pProvider)
348 m_pProvider->OnCredentialsProvided();
349
350 if (m_pEvents)
351 m_pEvents->Release();
352 m_pEvents = NULL;
353 return S_OK;
354}
355
356
357/*
358 * LogonUI calls this function when our tile is selected (zoomed).
359 * If you simply want fields to show/hide based on the selected state,
360 * there's no need to do anything here - you can set that up in the
361 * field definitions. But if you want to do something
362 * more complicated, like change the contents of a field when the tile is
363 * selected, you would do it here.
364 */
365HRESULT VBoxCredProvCredential::SetSelected(BOOL* pbAutoLogon)
366{
367 VBoxCredProvVerbose(0, "VBoxCredProvCredential: SetSelected\n");
368
369 /*
370 * Don't do auto logon here because it would retry too often with
371 * every credential field (user name, password, domain, ...) which makes
372 * winlogon wait before new login attempts can be made.
373 */
374 *pbAutoLogon = FALSE;
375 return S_OK;
376}
377
378
379/*
380 * Similarly to SetSelected, LogonUI calls this when your tile was selected
381 * and now no longer is. The most common thing to do here (which we do below)
382 * is to clear out the password field.
383 */
384HRESULT VBoxCredProvCredential::SetDeselected()
385{
386 VBoxCredProvVerbose(0, "VBoxCredProvCredential: SetDeselected\n");
387
388 VbglR3CredentialsDestroyUtf16(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
389 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
390 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME],
391 3 /* Passes */);
392
393 if (m_pEvents)
394 m_pEvents->SetFieldString(this, VBOXCREDPROV_FIELDID_PASSWORD, L"");
395
396 return S_OK;
397}
398
399
400/*
401 * Gets info for a particular field of a tile. Called by logonUI to get information to
402 * display the tile.
403 */
404HRESULT VBoxCredProvCredential::GetFieldState(DWORD dwFieldID,
405 CREDENTIAL_PROVIDER_FIELD_STATE* pFieldState,
406 CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pFieldstateInteractive)
407{
408 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetFieldState: dwFieldID=%ld\n", dwFieldID);
409
410 HRESULT hr = S_OK;
411
412 if ( (dwFieldID < VBOXCREDPROV_NUM_FIELDS)
413 && pFieldState
414 && pFieldstateInteractive)
415 {
416 *pFieldState = s_VBoxCredProvFields[dwFieldID].state;
417 *pFieldstateInteractive = s_VBoxCredProvFields[dwFieldID].stateInteractive;
418 }
419 else
420 hr = E_INVALIDARG;
421
422 return hr;
423}
424
425
426/*
427 * Searches the account name based on a display (real) name (e.g. "John Doe" -> "jdoe").
428 * Result "ppwszAccoutName" needs to be freed with CoTaskMemFree!
429 */
430BOOL VBoxCredProvCredential::TranslateAccountName(PWSTR pwszDisplayName, PWSTR *ppwszAccoutName)
431{
432 AssertPtrReturn(pwszDisplayName, FALSE);
433 VBoxCredProvVerbose(0, "VBoxCredProvCredential: TranslateAccountName: Getting account name for \"%ls\" ...\n",
434 pwszDisplayName);
435
436 /** @todo Do we need ADS support (e.g. TranslateNameW) here? */
437 BOOL fFound = FALSE; /* Did we find the desired user? */
438 NET_API_STATUS nStatus;
439 DWORD dwLevel = 2; /* Detailed information about user accounts. */
440 DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH;
441 DWORD dwEntriesRead = 0;
442 DWORD dwTotalEntries = 0;
443 DWORD dwResumeHandle = 0;
444 LPUSER_INFO_2 pBuf = NULL;
445 LPUSER_INFO_2 pCurBuf = NULL;
446 do
447 {
448 nStatus = NetUserEnum(NULL, /* Server name, NULL for localhost. */
449 dwLevel,
450 FILTER_NORMAL_ACCOUNT,
451 (LPBYTE*)&pBuf,
452 dwPrefMaxLen,
453 &dwEntriesRead,
454 &dwTotalEntries,
455 &dwResumeHandle);
456 if ( (nStatus == NERR_Success)
457 || (nStatus == ERROR_MORE_DATA))
458 {
459 if ((pCurBuf = pBuf) != NULL)
460 {
461 for (DWORD i = 0; i < dwEntriesRead; i++)
462 {
463 /*
464 * Search for the "display name" - that might be
465 * "John Doe" or something similar the user recognizes easier
466 * and may not the same as the "account" name (e.g. "jdoe").
467 */
468 if ( pCurBuf
469 && pCurBuf->usri2_full_name
470 && StrCmpI(pwszDisplayName, pCurBuf->usri2_full_name) == 0)
471 {
472 /*
473 * Copy the real user name (e.g. "jdoe") to our
474 * output buffer.
475 */
476 LPWSTR pwszTemp;
477 HRESULT hr = SHStrDupW(pCurBuf->usri2_name, &pwszTemp);
478 if (hr == S_OK)
479 {
480 *ppwszAccoutName = pwszTemp;
481 fFound = TRUE;
482 }
483 else
484 VBoxCredProvVerbose(0, "VBoxCredProvCredential: TranslateAccountName: Error copying data, hr=%08x\n", hr);
485 break;
486 }
487 pCurBuf++;
488 }
489 }
490 if (pBuf != NULL)
491 {
492 NetApiBufferFree(pBuf);
493 pBuf = NULL;
494 }
495 }
496 } while (nStatus == ERROR_MORE_DATA && !fFound);
497
498 if (pBuf != NULL)
499 {
500 NetApiBufferFree(pBuf);
501 pBuf = NULL;
502 }
503
504 VBoxCredProvVerbose(0, "VBoxCredProvCredential: TranslateAccountName: Returned nStatus=%ld, fFound=%s\n",
505 nStatus, fFound ? "Yes" : "No");
506 return fFound;
507
508#if 0
509 DWORD dwErr = NO_ERROR;
510 ULONG cbLen = 0;
511 if ( TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, NULL, &cbLen)
512 && cbLen > 0)
513 {
514 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetAccountName: Translated ADS name has %u characters\n", cbLen));
515
516 ppwszAccoutName = (PWSTR)RTMemAlloc(cbLen * sizeof(WCHAR));
517 AssertPtrReturn(pwszName, FALSE);
518 if (TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, ppwszAccoutName, &cbLen))
519 {
520 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetAccountName: Real ADS account name of '%ls' is '%ls'\n",
521 pwszName, ppwszAccoutName));
522 }
523 else
524 {
525 RTMemFree(ppwszAccoutName);
526 dwErr = GetLastError();
527 }
528 }
529 else
530 dwErr = GetLastError();
531 /* The above method for looking up in ADS failed, try another one. */
532 if (dwErr != NO_ERROR)
533 {
534 dwErr = NO_ERROR;
535
536 }
537#endif
538}
539
540
541/*
542 * Extracts the actual account name & domain from a (raw) account data string. This might
543 * be a principal or FQDN string.
544 */
545BOOL VBoxCredProvCredential::ExtractAccoutData(PWSTR pwszAccountData, PWSTR *ppwszAccoutName, PWSTR *ppwszDomain)
546{
547 AssertPtrReturn(pwszAccountData, FALSE);
548 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: Getting account name for \"%ls\" ...\n",
549 pwszAccountData);
550 HRESULT hr = E_FAIL;
551
552 /* Try to figure out whether this is a principal name (user@domain). */
553 LPWSTR pPos = NULL;
554 if ( (pPos = StrChrW(pwszAccountData, L'@')) != NULL
555 && pPos != pwszAccountData)
556 {
557 DWORD cbSize = (pPos - pwszAccountData) * sizeof(WCHAR);
558 LPWSTR pwszName = (LPWSTR)CoTaskMemAlloc(cbSize + sizeof(WCHAR)); /* Space for terminating zero. */
559 LPWSTR pwszDomain = NULL;
560 AssertPtr(pwszName);
561 hr = StringCbCopyN(pwszName, cbSize + sizeof(WCHAR), pwszAccountData, cbSize);
562 if (SUCCEEDED(hr))
563 {
564 *ppwszAccoutName = pwszName;
565 *pPos++; /* Skip @, point to domain name (if any). */
566 if ( pPos != NULL
567 && *pPos != L'\0')
568 {
569 hr = SHStrDupW(pPos, &pwszDomain);
570 if (SUCCEEDED(hr))
571 {
572 *ppwszDomain = pwszDomain;
573 }
574 else
575 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: Error copying domain data, hr=%08x\n", hr);
576 }
577 else
578 {
579 hr = E_FAIL;
580 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: No domain name found!\n");
581 }
582 }
583 else
584 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: Error copying account data, hr=%08x\n", hr);
585
586 if (hr != S_OK)
587 {
588 CoTaskMemFree(pwszName);
589 if (pwszDomain)
590 CoTaskMemFree(pwszDomain);
591 }
592 }
593 else
594 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: No valid principal account name found!\n");
595
596 return (hr == S_OK);
597}
598
599
600/* Sets ppwsz to the string value of the field at the index dwFieldID. */
601HRESULT VBoxCredProvCredential::GetStringValue(DWORD dwFieldID,
602 PWSTR *ppwszString)
603{
604 /* Check to make sure dwFieldID is a legitimate index. */
605 HRESULT hr;
606 if ( dwFieldID < VBOXCREDPROV_NUM_FIELDS
607 && ppwszString)
608 {
609 switch (dwFieldID)
610 {
611 case VBOXCREDPROV_FIELDID_SUBMIT_BUTTON:
612 /* Fill in standard value to make Winlogon happy. */
613 hr = SHStrDupW(L"Submit", ppwszString);
614 break;
615
616 default:
617
618 /*
619 * Make a copy of the string and return that, the caller is responsible for freeing it.
620 * Note that there can be empty fields (like a missing domain name); handle them
621 * by writing an empty string.
622 */
623 if ( m_pwszCredentials[dwFieldID]
624 && RTUtf16Len(m_pwszCredentials[dwFieldID]))
625 {
626 hr = SHStrDupW(m_pwszCredentials[dwFieldID], ppwszString);
627 }
628 else /* Fill in an empty value. */
629 hr = SHStrDupW(L"", ppwszString);
630 break;
631 }
632#ifdef DEBUG
633 if (SUCCEEDED(hr))
634 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetStringValue: dwFieldID=%ld, ppwszString=%ls\n",
635 dwFieldID, *ppwszString);
636#endif
637 }
638 else
639 hr = E_INVALIDARG;
640 return hr;
641}
642
643
644/*
645 * Sets pdwAdjacentTo to the index of the field the submit button should be
646 * adjacent to. We recommend that the submit button is placed next to the last
647 * field which the user is required to enter information in. Optional fields
648 * should be below the submit button.
649 */
650HRESULT VBoxCredProvCredential::GetSubmitButtonValue(DWORD dwFieldID,
651 DWORD* pdwAdjacentTo)
652{
653 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetSubmitButtonValue: dwFieldID=%ld\n", dwFieldID);
654
655 HRESULT hr = S_OK;
656
657 /* Validate parameters. */
658 if ( dwFieldID == VBOXCREDPROV_FIELDID_SUBMIT_BUTTON
659 && pdwAdjacentTo)
660 {
661 /* pdwAdjacentTo is a pointer to the fieldID you want the submit button to appear next to. */
662 *pdwAdjacentTo = VBOXCREDPROV_FIELDID_PASSWORD;
663 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetSubmitButtonValue: dwFieldID=%ld, *pdwAdjacentTo=%ld\n",
664 dwFieldID, *pdwAdjacentTo);
665 }
666 else
667 hr = E_INVALIDARG;
668
669 return hr;
670}
671
672
673/*
674 * Sets the value of a field which can accept a string as a value.
675 * This is called on each keystroke when a user types into an edit field.
676 */
677HRESULT VBoxCredProvCredential::SetStringValue(DWORD dwFieldID,
678 PCWSTR pcwzString)
679{
680 VBoxCredProvVerbose(0, "VBoxCredProvCredential: SetStringValue: dwFieldID=%ld, pcwzString=%ls\n",
681 dwFieldID, pcwzString);
682
683 /*
684 * We don't set any values into fields (e.g. the password, hidden
685 * by dots), instead keep it secret by resetting all credentials.
686 */
687 Reset();
688
689 return S_OK;
690}
691
692
693/*
694 * The following methods are for logonUI to get the values of various UI elements and then communicate
695 * to the credential about what the user did in that field. However, these methods are not implemented
696 * because our tile doesn't contain these types of UI elements.
697 */
698HRESULT VBoxCredProvCredential::GetBitmapValue(DWORD dwFieldID,
699 HBITMAP* phBitmap)
700{
701 NOREF(dwFieldID);
702 NOREF(phBitmap);
703
704 /* We don't do own bitmaps. */
705 return E_NOTIMPL;
706}
707
708
709HRESULT VBoxCredProvCredential::GetCheckboxValue(DWORD dwFieldID,
710 BOOL* pbChecked,
711 PWSTR* ppwszLabel)
712{
713 NOREF(dwFieldID);
714 NOREF(pbChecked);
715 NOREF(ppwszLabel);
716 return E_NOTIMPL;
717}
718
719
720HRESULT VBoxCredProvCredential::GetComboBoxValueCount(DWORD dwFieldID,
721 DWORD* pcItems,
722 DWORD* pdwSelectedItem)
723{
724 NOREF(dwFieldID);
725 NOREF(pcItems);
726 NOREF(pdwSelectedItem);
727 return E_NOTIMPL;
728}
729
730
731HRESULT VBoxCredProvCredential::GetComboBoxValueAt(DWORD dwFieldID,
732 DWORD dwItem,
733 PWSTR* ppwszItem)
734{
735 NOREF(dwFieldID);
736 NOREF(dwItem);
737 NOREF(ppwszItem);
738 return E_NOTIMPL;
739}
740
741
742HRESULT VBoxCredProvCredential::SetCheckboxValue(DWORD dwFieldID,
743 BOOL bChecked)
744{
745 NOREF(dwFieldID);
746 NOREF(bChecked);
747 return E_NOTIMPL;
748}
749
750
751HRESULT VBoxCredProvCredential::SetComboBoxSelectedValue(DWORD dwFieldId,
752 DWORD dwSelectedItem)
753{
754 NOREF(dwFieldId);
755 NOREF(dwSelectedItem);
756 return E_NOTIMPL;
757}
758
759
760HRESULT VBoxCredProvCredential::CommandLinkClicked(DWORD dwFieldID)
761{
762 NOREF(dwFieldID);
763 return E_NOTIMPL;
764}
765
766
767/*
768 * Collect the username and password into a serialized credential for the correct usage scenario
769 * LogonUI then passes these credentials back to the system to log on.
770 */
771HRESULT VBoxCredProvCredential::GetSerialization(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *pcpGetSerializationResponse,
772 CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpCredentialSerialization,
773 PWSTR *ppwszOptionalStatusText,
774 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
775{
776 NOREF(ppwszOptionalStatusText);
777 NOREF(pcpsiOptionalStatusIcon);
778
779 KERB_INTERACTIVE_UNLOCK_LOGON KerberosUnlockLogon;
780 ZeroMemory(&KerberosUnlockLogon, sizeof(KerberosUnlockLogon));
781
782 /* Save a pointer to the interactive logon struct. */
783 KERB_INTERACTIVE_LOGON* pKerberosLogon = &KerberosUnlockLogon.Logon;
784 AssertPtr(pKerberosLogon);
785
786 HRESULT hr;
787
788#ifdef DEBUG
789 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetSerialization: Username=%ls, Password=%ls, Domain=%ls\n",
790 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
791 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
792 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]);
793#endif
794
795 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
796 DWORD cch = ARRAYSIZE(wszComputerName);
797 if (GetComputerNameW(wszComputerName, &cch))
798 {
799 /* Is a domain name missing? Then use the name of the local computer. */
800 if ( m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]
801 && RTUtf16Len(m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]))
802 {
803 hr = RTUTF16toToUnicode(&pKerberosLogon->LogonDomainName,
804 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME],
805 false /* Just assign, no copy */);
806 }
807 else
808 hr = RTUTF16toToUnicode(&pKerberosLogon->LogonDomainName,
809 wszComputerName,
810 false /* Just assign, no copy */);
811
812 /* Fill in the username and password. */
813 if (SUCCEEDED(hr))
814 {
815 hr = RTUTF16toToUnicode(&pKerberosLogon->UserName,
816 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
817 false /* Just assign, no copy */);
818 if (SUCCEEDED(hr))
819 {
820 hr = RTUTF16toToUnicode(&pKerberosLogon->Password,
821 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
822 false /* Just assign, no copy */);
823 if (SUCCEEDED(hr))
824 {
825 /* Set credential type according to current usage scenario. */
826 AssertPtr(pKerberosLogon);
827 switch (m_cpUS)
828 {
829 case CPUS_UNLOCK_WORKSTATION:
830 pKerberosLogon->MessageType = KerbWorkstationUnlockLogon;
831 break;
832
833 case CPUS_LOGON:
834 pKerberosLogon->MessageType = KerbInteractiveLogon;
835 break;
836
837 case CPUS_CREDUI:
838 pKerberosLogon->MessageType = (KERB_LOGON_SUBMIT_TYPE)0; /* No message type required here. */
839 break;
840
841 default:
842 hr = E_FAIL;
843 break;
844 }
845
846 if (SUCCEEDED(hr))
847 {
848 /* Allocate copies of, and package, the strings in a binary blob. */
849 hr = AllocateLogonPackage(KerberosUnlockLogon,
850 &pcpCredentialSerialization->rgbSerialization,
851 &pcpCredentialSerialization->cbSerialization);
852 }
853
854 if (SUCCEEDED(hr))
855 {
856 ULONG ulAuthPackage;
857
858 HANDLE hLsa;
859 NTSTATUS s = LsaConnectUntrusted(&hLsa);
860 if (SUCCEEDED(HRESULT_FROM_NT(s)))
861 {
862 LSA_STRING lsaszKerberosName;
863 size_t cchKerberosName;
864 hr = StringCchLengthA(NEGOSSP_NAME_A, USHORT_MAX, &cchKerberosName);
865 if (SUCCEEDED(hr))
866 {
867 USHORT usLength;
868 hr = SizeTToUShort(cchKerberosName, &usLength);
869 if (SUCCEEDED(hr))
870 {
871 lsaszKerberosName.Buffer = (PCHAR)NEGOSSP_NAME_A;
872 lsaszKerberosName.Length = usLength;
873 lsaszKerberosName.MaximumLength = lsaszKerberosName.Length + 1;
874
875 }
876 }
877
878 if (SUCCEEDED(hr))
879 {
880 s = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName,
881 &ulAuthPackage);
882 if (FAILED(HRESULT_FROM_NT(s)))
883 hr = HRESULT_FROM_NT(s);
884 }
885
886 LsaDeregisterLogonProcess(hLsa);
887 }
888
889 if (SUCCEEDED(hr))
890 {
891 pcpCredentialSerialization->ulAuthenticationPackage = ulAuthPackage;
892 pcpCredentialSerialization->clsidCredentialProvider = CLSID_VBoxCredProvider;
893
894 /* We're done -- let the logon UI know. */
895 *pcpGetSerializationResponse = CPGSR_RETURN_CREDENTIAL_FINISHED;
896 }
897 }
898 }
899 }
900 }
901 }
902 else
903 hr = HRESULT_FROM_WIN32(GetLastError());
904
905 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetSerialization: Returned 0x%08x\n", hr);
906 return hr;
907}
908
909
910/*
911 * ReportResult is completely optional. Its purpose is to allow a credential to customize the string
912 * and the icon displayed in the case of a logon failure. For example, we have chosen to
913 * customize the error shown in the case of bad username/password and in the case of the account
914 * being disabled.
915 */
916HRESULT VBoxCredProvCredential::ReportResult(NTSTATUS ntsStatus,
917 NTSTATUS ntsSubstatus,
918 PWSTR *ppwszOptionalStatusText,
919 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
920{
921 /*
922 * Since NULL is a valid value for *ppwszOptionalStatusText and *pcpsiOptionalStatusIcon
923 * this function can't fail
924 */
925 return S_OK;
926}
927
Note: See TracBrowser for help on using the repository browser.

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