VirtualBox

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

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

VBoxCredProv: Fixed reference counting + wiping credentials, logging adjustments.

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