VirtualBox

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

Last change on this file since 95933 was 95933, checked in by vboxsync, 3 years ago

Add/Nt/VBoxCredProv: doxygen fix. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.0 KB
Line 
1/* $Id: VBoxCredProvCredential.cpp 95933 2022-07-29 01:19:49Z vboxsync $ */
2/** @file
3 * VBoxCredProvCredential - Class for keeping and handling the passed credentials.
4 */
5
6/*
7 * Copyright (C) 2012-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#ifndef WIN32_NO_STATUS
23# include <ntstatus.h>
24# define WIN32_NO_STATUS
25#endif
26#include <iprt/win/intsafe.h>
27
28#include "VBoxCredentialProvider.h"
29
30#include "VBoxCredProvProvider.h"
31#include "VBoxCredProvCredential.h"
32#include "VBoxCredProvUtils.h"
33
34#include <lm.h>
35
36#include <iprt/initterm.h>
37#include <iprt/mem.h>
38#include <iprt/string.h>
39#include <iprt/utf16.h>
40
41
42
43
44VBoxCredProvCredential::VBoxCredProvCredential(void)
45 : m_cRefs(1)
46 , m_enmUsageScenario(CPUS_INVALID)
47 , m_pEvents(NULL)
48 , m_fHaveCreds(false)
49{
50 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Created\n");
51 VBoxCredentialProviderAcquire();
52
53 for (unsigned i = 0; i < VBOXCREDPROV_NUM_FIELDS; i++)
54 {
55 const VBOXCREDPROV_FIELD *pField = &s_VBoxCredProvDefaultFields[i];
56
57 m_apwszFields[i] = RTUtf16Dup(pField->desc.pszLabel ? pField->desc.pszLabel : L"");
58 AssertPtr(m_apwszFields[i]);
59 }
60}
61
62
63VBoxCredProvCredential::~VBoxCredProvCredential(void)
64{
65 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Destroying\n");
66
67 Reset();
68
69 for (unsigned i = 0; i < VBOXCREDPROV_NUM_FIELDS; i++)
70 {
71 if (m_apwszFields[i])
72 {
73 RTUtf16Free(m_apwszFields[i]);
74 m_apwszFields[i] = NULL;
75 }
76 }
77
78 VBoxCredentialProviderRelease();
79}
80
81
82ULONG VBoxCredProvCredential::AddRef(void)
83{
84 LONG cRefs = InterlockedIncrement(&m_cRefs);
85 VBoxCredProvVerbose(0, "VBoxCredProvCredential::AddRef: Returning refcount=%ld\n",
86 cRefs);
87 return cRefs;
88}
89
90
91ULONG VBoxCredProvCredential::Release(void)
92{
93 LONG cRefs = InterlockedDecrement(&m_cRefs);
94 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Release: Returning refcount=%ld\n",
95 cRefs);
96 if (!cRefs)
97 {
98 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Calling destructor\n");
99 delete this;
100 }
101 return cRefs;
102}
103
104
105HRESULT VBoxCredProvCredential::QueryInterface(REFIID interfaceID, void **ppvInterface)
106{
107 HRESULT hr = S_OK;;
108 if (ppvInterface)
109 {
110 if ( IID_IUnknown == interfaceID
111 || IID_ICredentialProviderCredential == interfaceID)
112 {
113 *ppvInterface = static_cast<IUnknown*>(this);
114 reinterpret_cast<IUnknown*>(*ppvInterface)->AddRef();
115 }
116 else
117 {
118 *ppvInterface = NULL;
119 hr = E_NOINTERFACE;
120 }
121 }
122 else
123 hr = E_INVALIDARG;
124
125 return hr;
126}
127
128
129/**
130 * Assigns or copies a RTUTF16 string to a UNICODE_STRING.
131 *
132 * When fCopy is false, this does *not* copy its contents
133 * and only assigns its code points to the destination!
134 * When fCopy is true, the actual string buffer gets copied.
135 *
136 * Does not take terminating \0 into account.
137 *
138 * @return HRESULT
139 * @param pUnicodeDest Unicode string assigning the UTF16 string to.
140 * @param pwszSource UTF16 string to assign.
141 * @param fCopy Whether to just assign or copy the actual buffer
142 * contents from source -> dest.
143 */
144HRESULT VBoxCredProvCredential::RTUTF16ToUnicode(PUNICODE_STRING pUnicodeDest, PRTUTF16 pwszSource, bool fCopy)
145{
146 AssertPtrReturn(pUnicodeDest, E_POINTER);
147 AssertPtrReturn(pwszSource, E_POINTER);
148
149 size_t cbLen = RTUtf16Len(pwszSource) * sizeof(RTUTF16);
150 AssertReturn(cbLen <= USHORT_MAX, E_INVALIDARG);
151
152 HRESULT hr;
153
154 if (fCopy)
155 {
156 if (cbLen <= pUnicodeDest->MaximumLength)
157 {
158 memcpy(pUnicodeDest->Buffer, pwszSource, cbLen);
159 pUnicodeDest->Length = (USHORT)cbLen;
160 hr = S_OK;
161 }
162 else
163 hr = E_INVALIDARG;
164 }
165 else /* Just assign the buffer. */
166 {
167 pUnicodeDest->Buffer = pwszSource;
168 pUnicodeDest->Length = (USHORT)cbLen;
169 hr = S_OK;
170 }
171
172 return hr;
173}
174
175
176/**
177 * Copies an UTF16 string into a PUNICODE_STRING by allocating space for it.
178 *
179 * @return HRESULT
180 * @param pUnicodeDest Where to store the copied (allocated) unicode string.
181 * @param pwszSource UTF16 string to copy.
182 */
183HRESULT VBoxCredProvCredential::RTUTF16ToUnicodeA(PUNICODE_STRING pUnicodeDest, PRTUTF16 pwszSource)
184{
185 AssertPtrReturn(pUnicodeDest, E_POINTER);
186 AssertPtrReturn(pwszSource, E_POINTER);
187
188 size_t cbLen = RTUtf16Len(pwszSource) * sizeof(RTUTF16);
189
190 pUnicodeDest->Buffer = (LPWSTR)CoTaskMemAlloc(cbLen);
191
192 if (!pUnicodeDest->Buffer)
193 return E_OUTOFMEMORY;
194
195 pUnicodeDest->MaximumLength = (USHORT)cbLen;
196 pUnicodeDest->Length = 0;
197
198 return RTUTF16ToUnicode(pUnicodeDest, pwszSource, true /* fCopy */);
199}
200
201
202/**
203 * Frees a formerly allocated PUNICODE_STRING.
204 *
205 * @param pUnicode String to free.
206 */
207void VBoxCredProvCredential::UnicodeStringFree(PUNICODE_STRING pUnicode)
208{
209 if (!pUnicode)
210 return;
211
212 if (pUnicode->Buffer)
213 {
214 Assert(pUnicode->MaximumLength);
215
216 /* Make sure to wipe contents before free'ing. */
217 RTMemWipeThoroughly(pUnicode->Buffer, pUnicode->MaximumLength /* MaximumLength is bytes! */, 3 /* Passes */);
218
219 CoTaskMemFree(pUnicode->Buffer);
220 pUnicode->Buffer = NULL;
221 }
222
223 pUnicode->Length = 0;
224 pUnicode->MaximumLength = 0;
225}
226
227
228/**
229 * Creates a KERB_INTERACTIVE_LOGON structure with the given parameters.
230 * Must be destroyed with kerberosLogonDestroy().
231 *
232 * @return HRESULT
233 * @param pLogon Structure to create.
234 * @param enmUsage Intended usage of the structure.
235 * @param pwszUser User name to use.
236 * @param pwszPassword Password to use.
237 * @param pwszDomain Domain to use. Optional and can be NULL.
238 */
239HRESULT VBoxCredProvCredential::kerberosLogonCreate(KERB_INTERACTIVE_LOGON *pLogon,
240 CREDENTIAL_PROVIDER_USAGE_SCENARIO enmUsage,
241 PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain)
242{
243 AssertPtrReturn(pLogon, E_INVALIDARG);
244 AssertPtrReturn(pwszUser, E_INVALIDARG);
245 AssertPtrReturn(pwszPassword, E_INVALIDARG);
246 /* pwszDomain is optional. */
247
248 HRESULT hr;
249
250 /* Do we have a domain name set? */
251 if ( pwszDomain
252 && RTUtf16Len(pwszDomain))
253 {
254 hr = RTUTF16ToUnicodeA(&pLogon->LogonDomainName, pwszDomain);
255 }
256 else /* No domain (FQDN) given, try local computer name. */
257 {
258 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
259 DWORD cch = ARRAYSIZE(wszComputerName);
260 if (GetComputerNameW(wszComputerName, &cch))
261 {
262 /* Is a domain name missing? Then use the name of the local computer. */
263 hr = RTUTF16ToUnicodeA(&pLogon->LogonDomainName, wszComputerName);
264
265 VBoxCredProvVerbose(0, "VBoxCredProvCredential::kerberosLogonInit: Local computer name=%ls\n",
266 wszComputerName);
267 }
268 else
269 hr = HRESULT_FROM_WIN32(GetLastError());
270 }
271
272 /* Fill in the username and password. */
273 if (SUCCEEDED(hr))
274 {
275 hr = RTUTF16ToUnicodeA(&pLogon->UserName, pwszUser);
276 if (SUCCEEDED(hr))
277 {
278 hr = RTUTF16ToUnicodeA(&pLogon->Password, pwszPassword);
279 if (SUCCEEDED(hr))
280 {
281 /* Set credential type according to current usage scenario. */
282 switch (enmUsage)
283 {
284 case CPUS_UNLOCK_WORKSTATION:
285 pLogon->MessageType = KerbWorkstationUnlockLogon;
286 break;
287
288 case CPUS_LOGON:
289 pLogon->MessageType = KerbInteractiveLogon;
290 break;
291
292 case CPUS_CREDUI:
293 pLogon->MessageType = (KERB_LOGON_SUBMIT_TYPE)0; /* No message type required here. */
294 break;
295
296 default:
297 VBoxCredProvVerbose(0, "VBoxCredProvCredential::kerberosLogonInit: Unknown usage scenario=%ld\n",
298 enmUsage);
299 hr = E_FAIL;
300 break;
301 }
302 }
303 }
304 }
305
306 return hr;
307}
308
309
310/**
311 * Destroys a formerly created KERB_INTERACTIVE_LOGON structure.
312 *
313 * @param pLogon Structure to destroy.
314 */
315void VBoxCredProvCredential::kerberosLogonDestroy(KERB_INTERACTIVE_LOGON *pLogon)
316{
317 if (!pLogon)
318 return;
319
320 UnicodeStringFree(&pLogon->UserName);
321 UnicodeStringFree(&pLogon->Password);
322 UnicodeStringFree(&pLogon->LogonDomainName);
323}
324
325
326HRESULT VBoxCredProvCredential::kerberosLogonSerialize(const KERB_INTERACTIVE_LOGON *pLogonIn,
327 PBYTE *ppPackage, DWORD *pcbPackage)
328{
329 AssertPtrReturn(pLogonIn, E_INVALIDARG);
330 AssertPtrReturn(ppPackage, E_INVALIDARG);
331 AssertPtrReturn(pcbPackage, E_INVALIDARG);
332
333 /*
334 * First, allocate enough space for the logon structure itself and separate
335 * string buffers right after it to store the actual user, password and domain
336 * credentials.
337 */
338 DWORD cbLogon = sizeof(KERB_INTERACTIVE_UNLOCK_LOGON)
339 + pLogonIn->LogonDomainName.Length
340 + pLogonIn->UserName.Length
341 + pLogonIn->Password.Length;
342
343#ifdef DEBUG /* Do not reveal any hints to credential data in release mode. */
344 VBoxCredProvVerbose(1, "VBoxCredProvCredential::AllocateLogonPackage: Allocating %ld bytes (%zu bytes credentials)\n",
345 cbLogon, cbLogon - sizeof(KERB_INTERACTIVE_UNLOCK_LOGON));
346#endif
347
348 KERB_INTERACTIVE_UNLOCK_LOGON *pLogon = (KERB_INTERACTIVE_UNLOCK_LOGON*)CoTaskMemAlloc(cbLogon);
349 if (!pLogon)
350 return E_OUTOFMEMORY;
351
352 /* Make sure to zero everything first. */
353 RT_BZERO(pLogon, cbLogon);
354
355 /* Let our byte buffer point to the end of our allocated structure so that it can
356 * be used to store the credential data sequentially in a binary blob
357 * (without terminating \0). */
358 PBYTE pbBuffer = (PBYTE)pLogon + sizeof(KERB_INTERACTIVE_UNLOCK_LOGON);
359
360 /* The buffer of the packed destination string does not contain the actual
361 * string content but a relative offset starting at the given
362 * KERB_INTERACTIVE_UNLOCK_LOGON structure. */
363#define KERB_CRED_INIT_PACKED(StringDst, StringSrc, LogonOffset) \
364 StringDst.Length = StringSrc.Length; \
365 StringDst.MaximumLength = StringSrc.Length; \
366 if (StringDst.Length) \
367 { \
368 StringDst.Buffer = (PWSTR)pbBuffer; \
369 memcpy(StringDst.Buffer, StringSrc.Buffer, StringDst.Length); \
370 StringDst.Buffer = (PWSTR)(pbBuffer - (PBYTE)LogonOffset); \
371 pbBuffer += StringDst.Length; \
372 }
373
374 KERB_INTERACTIVE_LOGON *pLogonOut = &pLogon->Logon;
375
376 pLogonOut->MessageType = pLogonIn->MessageType;
377
378 KERB_CRED_INIT_PACKED(pLogonOut->LogonDomainName, pLogonIn->LogonDomainName, pLogon);
379 KERB_CRED_INIT_PACKED(pLogonOut->UserName , pLogonIn->UserName, pLogon);
380 KERB_CRED_INIT_PACKED(pLogonOut->Password , pLogonIn->Password, pLogon);
381
382 *ppPackage = (PBYTE)pLogon;
383 *pcbPackage = cbLogon;
384
385#undef KERB_CRED_INIT_PACKED
386
387 return S_OK;
388}
389
390
391/**
392 * Returns the current value of a specific credential provider field.
393 *
394 * @return Pointer (const) to the credential provider field requested, or NULL if not found / invalid.
395 * @param dwFieldID Field ID of the credential provider field to get.
396 */
397PCRTUTF16 VBoxCredProvCredential::getField(DWORD dwFieldID)
398{
399 if (dwFieldID >= VBOXCREDPROV_NUM_FIELDS)
400 return NULL;
401
402 /* Paranoia: Don't ever reveal passwords. */
403 if (dwFieldID == VBOXCREDPROV_FIELDID_PASSWORD)
404 return NULL;
405
406 return m_apwszFields[dwFieldID];
407}
408
409
410/**
411 * Sets a credential provider field by first zero'ing out its current content in a (hopefully) secure manner,
412 * then applying either the field's default or a new value.
413 *
414 * @return HRESULT
415 * @param dwFieldID Field ID of the credential provider field to reset.
416 * @param pcwszString String to set for the given field. Specify NULL for setting the provider's default value.
417 * @param fNotifyUI Whether to notify the LogonUI about the reset.
418 */
419HRESULT VBoxCredProvCredential::setField(DWORD dwFieldID, const PRTUTF16 pcwszString, bool fNotifyUI)
420{
421 if (dwFieldID >= VBOXCREDPROV_NUM_FIELDS)
422 return E_INVALIDARG;
423
424 HRESULT hr = S_OK;
425
426 PRTUTF16 pwszField = m_apwszFields[dwFieldID];
427 if (pwszField)
428 {
429 /* First, wipe the existing value thoroughly. */
430 RTMemWipeThoroughly(pwszField, (RTUtf16Len(pwszField) + 1) * sizeof(RTUTF16), 3 /* Passes */);
431
432 /* Second, free the string. */
433 RTUtf16Free(pwszField);
434 }
435
436 /* Either fill in the default value or the one specified in pcwszString. */
437 pwszField = RTUtf16Dup(pcwszString ? pcwszString : s_VBoxCredProvDefaultFields[dwFieldID].desc.pszLabel);
438 if (pwszField)
439 {
440 m_apwszFields[dwFieldID] = pwszField; /* Update the pointer. */
441
442 if ( m_pEvents
443 && fNotifyUI) /* Let the logon UI know if wanted. */
444 {
445 hr = m_pEvents->SetFieldString(this, dwFieldID, pwszField);
446 }
447 }
448 else
449 hr = E_OUTOFMEMORY;
450
451 VBoxCredProvVerbose(0, "VBoxCredProvCredential::setField: Setting field dwFieldID=%ld to '%ls', fNotifyUI=%RTbool, hr=0x%08x\n",
452 dwFieldID,
453#ifdef DEBUG
454 pwszField,
455#else
456 /* Don't show any passwords in release mode. */
457 dwFieldID == VBOXCREDPROV_FIELDID_PASSWORD ? L"XXX" : pwszField,
458#endif
459 fNotifyUI, hr);
460 return hr;
461}
462
463/**
464 * Resets (wipes) stored credentials.
465 *
466 * @return HRESULT
467 */
468HRESULT VBoxCredProvCredential::Reset(void)
469{
470 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Reset: Wiping credentials user=%ls, pw=%ls, domain=%ls\n",
471 m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME] ? m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME] : L"<NULL>",
472#ifdef DEBUG
473 m_apwszFields[VBOXCREDPROV_FIELDID_PASSWORD] ? m_apwszFields[VBOXCREDPROV_FIELDID_PASSWORD] : L"<NULL>",
474#else
475 L"XXX" /* Don't show any passwords in release mode. */,
476#endif
477 m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME] ? m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME] : L"<NULL>");
478
479 /* Note: Do not reset the user name and domain name here,
480 * as they could still being queried (again) by LogonUI on failed login attempts. */
481 HRESULT hr = setField(VBOXCREDPROV_FIELDID_PASSWORD, NULL /* Use default value */, true /* fNotifyUI */);
482
483 m_fIsSelected = false;
484
485 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Reset\n");
486 return hr;
487}
488
489
490/**
491 * Checks and retrieves credentials provided by the host + does account lookup on eventually
492 * renamed user accounts.
493 *
494 * @return IPRT status code.
495 */
496int VBoxCredProvCredential::RetrieveCredentials(void)
497{
498 PRTUTF16 pwszUser = NULL;
499 PRTUTF16 pwszPassword = NULL;
500 PRTUTF16 pwszDomain = NULL;
501
502 int rc = VbglR3CredentialsQueryAvailability();
503 if (RT_SUCCESS(rc))
504 {
505 /*
506 * Set status to "terminating" to let the host know this module now
507 * tries to receive and use passed credentials so that credentials from
508 * the host won't be sent twice.
509 */
510 VBoxCredProvReportStatus(VBoxGuestFacilityStatus_Terminating);
511
512 rc = VbglR3CredentialsRetrieveUtf16(&pwszUser, &pwszPassword, &pwszDomain);
513
514 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Retrieved credentials with rc=%Rrc\n", rc);
515 }
516
517 if (RT_SUCCESS(rc))
518 {
519 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Received credentials for user '%ls'\n", pwszUser);
520
521 /*
522 * In case we got a "display name" (e.g. "John Doe")
523 * instead of the real user name (e.g. "jdoe") we have
524 * to translate the data first ...
525 */
526 PWSTR pwszExtractedName = NULL;
527 if ( TranslateAccountName(pwszUser, &pwszExtractedName)
528 && pwszExtractedName)
529 {
530 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Translated account name '%ls' -> '%ls'\n",
531 pwszUser, pwszExtractedName);
532
533 RTMemWipeThoroughly(pwszUser, (RTUtf16Len(pwszUser) + 1) * sizeof(RTUTF16), 3 /* Passes */);
534 RTUtf16Free(pwszUser);
535
536 pwszUser = RTUtf16Dup(pwszExtractedName);
537
538 CoTaskMemFree(pwszExtractedName);
539 pwszExtractedName = NULL;
540 }
541 else
542 {
543 /*
544 * Okay, no display name, but maybe it's a
545 * principal name from which we have to extract the domain from?
546 * (jdoe@my-domain.sub.net.com -> jdoe in domain my-domain.sub.net.com.)
547 */
548 PWSTR pwszExtractedDomain = NULL;
549 if (ExtractAccountData(pwszUser, &pwszExtractedName, &pwszExtractedDomain))
550 {
551 /* Update user name. */
552 if (pwszExtractedName)
553 {
554 if (pwszUser)
555 {
556 RTMemWipeThoroughly(pwszUser, (RTUtf16Len(pwszUser) + 1) * sizeof(RTUTF16), 3 /* Passes */);
557 RTUtf16Free(pwszUser);
558 }
559
560 pwszUser = RTUtf16Dup(pwszExtractedName);
561
562 CoTaskMemFree(pwszExtractedName);
563 pwszExtractedName = NULL;
564 }
565
566 /* Update domain. */
567 if (pwszExtractedDomain)
568 {
569 if (pwszDomain)
570 {
571 RTMemWipeThoroughly(pwszDomain, (RTUtf16Len(pwszDomain) + 1) * sizeof(RTUTF16), 3 /* Passes */);
572 RTUtf16Free(pwszDomain);
573 }
574
575 pwszDomain = RTUtf16Dup(pwszExtractedDomain);
576
577 CoTaskMemFree(pwszExtractedDomain);
578 pwszExtractedDomain = NULL;
579 }
580
581 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Extracted account name '%ls' + domain '%ls'\n",
582 pwszUser ? pwszUser : L"<NULL>", pwszDomain ? pwszDomain : L"<NULL>");
583 }
584 }
585
586 m_fHaveCreds = true;
587 }
588
589 if (m_fHaveCreds)
590 {
591 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Setting fields\n");
592
593 setField(VBOXCREDPROV_FIELDID_USERNAME, pwszUser, true /* fNotifyUI */);
594 setField(VBOXCREDPROV_FIELDID_PASSWORD, pwszPassword, true /* fNotifyUI */);
595 setField(VBOXCREDPROV_FIELDID_DOMAINNAME, pwszDomain, true /* fNotifyUI */);
596 }
597
598 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Wiping ...\n");
599
600 VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain, 3 /* cPasses */);
601
602 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Returned rc=%Rrc\n", rc);
603 return rc;
604}
605
606
607/**
608 * Initializes this credential with the current credential provider
609 * usage scenario.
610 */
611HRESULT VBoxCredProvCredential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO enmUsageScenario)
612{
613 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Initialize: enmUsageScenario=%ld\n", enmUsageScenario);
614 m_enmUsageScenario = enmUsageScenario;
615 return S_OK;
616}
617
618
619/**
620 * Called by LogonUI when it needs this credential's advice.
621 *
622 * At the moment we only grab the credential provider events so that we can
623 * trigger a re-enumeration of the credentials later.
624 */
625HRESULT VBoxCredProvCredential::Advise(ICredentialProviderCredentialEvents *pEvents)
626{
627 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Advise: pEvents=0x%p\n", pEvents);
628
629 if (m_pEvents)
630 {
631 m_pEvents->Release();
632 m_pEvents = NULL;
633 }
634
635 return pEvents->QueryInterface(IID_PPV_ARGS(&m_pEvents));
636}
637
638
639/**
640 * Called by LogonUI when it's finished with handling this credential.
641 *
642 * We only need to release the credential provider events, if any.
643 */
644HRESULT VBoxCredProvCredential::UnAdvise(void)
645{
646 VBoxCredProvVerbose(0, "VBoxCredProvCredential::UnAdvise\n");
647
648 if (m_pEvents)
649 {
650 m_pEvents->Release();
651 m_pEvents = NULL;
652 }
653
654 return S_OK;
655}
656
657
658/**
659 * Called by LogonUI when a user profile (tile) has been selected.
660 *
661 * As we don't want Winlogon to try logging in immediately we set pfAutoLogon
662 * to FALSE (if set).
663 */
664HRESULT VBoxCredProvCredential::SetSelected(PBOOL pfAutoLogon)
665{
666 VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetSelected\n");
667
668 /*
669 * Don't do auto logon here because it would retry too often with
670 * every credential field (user name, password, domain, ...) which makes
671 * winlogon wait before new login attempts can be made.
672 */
673 if (pfAutoLogon)
674 *pfAutoLogon = FALSE;
675
676 m_fIsSelected = true;
677
678 return S_OK;
679}
680
681
682/**
683 * Called by LogonUI when a user profile (tile) has been unselected again.
684 */
685HRESULT VBoxCredProvCredential::SetDeselected(void)
686{
687 VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetDeselected\n");
688
689 Reset();
690
691 return S_OK;
692}
693
694
695/**
696 * Called by LogonUI to retrieve the (interactive) state of a UI field.
697 */
698HRESULT VBoxCredProvCredential::GetFieldState(DWORD dwFieldID, CREDENTIAL_PROVIDER_FIELD_STATE *pFieldState,
699 CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE *pFieldstateInteractive)
700{
701 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetFieldState: dwFieldID=%ld\n", dwFieldID);
702
703 HRESULT hr = S_OK;
704
705 if (dwFieldID < VBOXCREDPROV_NUM_FIELDS)
706 {
707 if (pFieldState)
708 *pFieldState = s_VBoxCredProvDefaultFields[dwFieldID].state;
709
710 if (pFieldstateInteractive)
711 *pFieldstateInteractive = s_VBoxCredProvDefaultFields[dwFieldID].stateInteractive;
712 }
713 else
714 hr = E_INVALIDARG;
715
716 return hr;
717}
718
719
720/**
721 * Searches the account name based on a display (real) name (e.g. "John Doe" -> "jdoe").
722 *
723 * @return TRUE if translation of the account name was successful, FALSE if not.
724 * @param pwszDisplayName Display name to extract account name from.
725 * @param ppwszAccoutName Where to store the extracted account name on success.
726 * Needs to be free'd with CoTaskMemFree().
727 */
728BOOL VBoxCredProvCredential::TranslateAccountName(PWSTR pwszDisplayName, PWSTR *ppwszAccoutName)
729{
730 AssertPtrReturn(pwszDisplayName, FALSE);
731 VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName: Getting account name for \"%ls\" ...\n",
732 pwszDisplayName);
733
734 /** @todo Do we need ADS support (e.g. TranslateNameW) here? */
735 BOOL fFound = FALSE; /* Did we find the desired user? */
736 NET_API_STATUS rcStatus;
737 DWORD dwLevel = 2; /* Detailed information about user accounts. */
738 DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH;
739 DWORD dwEntriesRead = 0;
740 DWORD dwTotalEntries = 0;
741 DWORD dwResumeHandle = 0;
742 LPUSER_INFO_2 pBuf = NULL;
743 LPUSER_INFO_2 pCurBuf = NULL;
744 do
745 {
746 rcStatus = NetUserEnum(NULL, /* Server name, NULL for localhost. */
747 dwLevel,
748 FILTER_NORMAL_ACCOUNT,
749 (LPBYTE*)&pBuf,
750 dwPrefMaxLen,
751 &dwEntriesRead,
752 &dwTotalEntries,
753 &dwResumeHandle);
754 if ( rcStatus == NERR_Success
755 || rcStatus == ERROR_MORE_DATA)
756 {
757 if ((pCurBuf = pBuf) != NULL)
758 {
759 for (DWORD i = 0; i < dwEntriesRead; i++)
760 {
761 /*
762 * Search for the "display name" - that might be
763 * "John Doe" or something similar the user recognizes easier
764 * and may not the same as the "account" name (e.g. "jdoe").
765 */
766 if ( pCurBuf
767 && pCurBuf->usri2_full_name
768 && StrCmpI(pwszDisplayName, pCurBuf->usri2_full_name) == 0)
769 {
770 /*
771 * Copy the real user name (e.g. "jdoe") to our
772 * output buffer.
773 */
774 LPWSTR pwszTemp;
775 HRESULT hr = SHStrDupW(pCurBuf->usri2_name, &pwszTemp);
776 if (hr == S_OK)
777 {
778 *ppwszAccoutName = pwszTemp;
779 fFound = TRUE;
780 }
781 else
782 VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName: Error copying data, hr=%08x\n", hr);
783 break;
784 }
785 pCurBuf++;
786 }
787 }
788 if (pBuf != NULL)
789 {
790 NetApiBufferFree(pBuf);
791 pBuf = NULL;
792 }
793 }
794 } while (rcStatus == ERROR_MORE_DATA && !fFound);
795
796 if (pBuf != NULL)
797 {
798 NetApiBufferFree(pBuf);
799 pBuf = NULL;
800 }
801
802 VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName returned rcStatus=%ld, fFound=%RTbool\n",
803 rcStatus, fFound);
804 return fFound;
805
806#if 0
807 DWORD dwErr = NO_ERROR;
808 ULONG cbLen = 0;
809 if ( TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, NULL, &cbLen)
810 && cbLen > 0)
811 {
812 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetAccountName: Translated ADS name has %u characters\n", cbLen));
813
814 ppwszAccoutName = (PWSTR)RTMemAlloc(cbLen * sizeof(WCHAR));
815 AssertPtrReturn(pwszName, FALSE);
816 if (TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, ppwszAccoutName, &cbLen))
817 {
818 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetAccountName: Real ADS account name of '%ls' is '%ls'\n",
819 pwszName, ppwszAccoutName));
820 }
821 else
822 {
823 RTMemFree(ppwszAccoutName);
824 dwErr = GetLastError();
825 }
826 }
827 else
828 dwErr = GetLastError();
829 /* The above method for looking up in ADS failed, try another one. */
830 if (dwErr != NO_ERROR)
831 {
832 dwErr = NO_ERROR;
833
834 }
835#endif
836}
837
838
839/**
840 * Extracts the actual account name & domain from a (raw) account data string.
841 *
842 * This might be a principal or FQDN string.
843 *
844 * @return success indicator. Will fail if input not in a user\@domain format.
845 * @param pwszAccountData Raw account data string to extract data from.
846 * @param ppwszAccountName Where to store the extracted account name on
847 * success. Needs to be freed with CoTaskMemFree().
848 * @param ppwszDomain Where to store the extracted domain name on
849 * success. Needs to be freed with CoTaskMemFree().
850 */
851/*static*/ bool VBoxCredProvCredential::ExtractAccountData(PWSTR pwszAccountData, PWSTR *ppwszAccountName, PWSTR *ppwszDomain)
852{
853 AssertPtrReturn(pwszAccountData, FALSE);
854 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: Getting account name for \"%ls\" ...\n",
855 pwszAccountData);
856
857/** @todo r=bird: The original code seemed a little confused about whether
858 * the domain stuff was optional or not, as it declared pwszDomain
859 * very early and freed it in the error path. Not entirely sure what
860 * to make of that... */
861
862 /* Try to figure out whether this is a principal name (user@domain). */
863 LPWSTR const pwszAt = StrChrW(pwszAccountData, L'@');
864 if (pwszAt && pwszAt != pwszAccountData)
865 {
866 if (pwszAt[1])
867 {
868 size_t cwcUser = (size_t)(pwszAt - pwszAccountData) + 1;
869 LPWSTR pwszName = (LPWSTR)CoTaskMemAlloc(cwcUser * sizeof(WCHAR));
870 if (pwszName)
871 {
872 int rc = RTUtf16CopyEx(pwszName, cwcUser, pwszAccountData, cwcUser - 1);
873 if (RT_SUCCESS(rc))
874 {
875 LPWSTR pwszDomain = NULL;
876 HRESULT hr = SHStrDupW(&pwszAt[1], &pwszDomain);
877 if (SUCCEEDED(hr))
878 {
879 *ppwszAccountName = pwszName;
880 *ppwszDomain = pwszDomain;
881 return true;
882 }
883
884 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccountData: Error copying domain data, hr=%08x\n", hr);
885 }
886 else
887 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccountData: Error copying account data, rc=%Rrc\n", rc);
888 CoTaskMemFree(pwszName);
889 }
890 else
891 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccountData: allocation failure.\n");
892 }
893 else
894 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccountData: No domain name found!\n");
895 }
896 else
897 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccountData: No valid principal account name found!\n");
898
899 return false;
900}
901
902
903/**
904 * Returns the current value of a specified LogonUI field.
905 *
906 * @return IPRT status code.
907 * @param dwFieldID Field ID to get value for.
908 * @param ppwszString Pointer that receives the actual value of the specified field.
909 */
910HRESULT VBoxCredProvCredential::GetStringValue(DWORD dwFieldID, PWSTR *ppwszString)
911{
912 HRESULT hr;
913
914 PWSTR pwszString = NULL;
915
916 if (dwFieldID < VBOXCREDPROV_NUM_FIELDS)
917 {
918 switch (dwFieldID)
919 {
920 case VBOXCREDPROV_FIELDID_SUBMIT_BUTTON:
921 {
922 /* Fill in standard value to make Winlogon happy. */
923 hr = SHStrDupW(L"Submit", &pwszString);
924 break;
925 }
926
927 default:
928 {
929 if ( m_apwszFields[dwFieldID]
930 && m_apwszFields[dwFieldID][0])
931 hr = SHStrDupW(m_apwszFields[dwFieldID], &pwszString);
932 else /* Fill in an empty value. */
933 hr = SHStrDupW(L"", &pwszString);
934 break;
935 }
936 }
937 }
938 else
939 hr = E_INVALIDARG;
940
941 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetStringValue: m_fIsSelected=%RTbool, dwFieldID=%ld, pwszString=%ls, hr=%Rhrc\n",
942 m_fIsSelected, dwFieldID,
943#ifdef DEBUG
944 pwszString ? pwszString : L"<NULL>",
945#else
946 /* Don't show any passwords in release mode. */
947 dwFieldID == VBOXCREDPROV_FIELDID_PASSWORD ? L"XXX" : (pwszString ? pwszString : L"<NULL>"),
948#endif
949 hr);
950
951 if (ppwszString)
952 *ppwszString = pwszString;
953 else if (pwszString)
954 CoTaskMemFree(pwszString);
955
956 return hr;
957}
958
959
960/**
961 * Returns back the field ID of which the submit button should be put next to.
962 *
963 * We always want to be the password field put next to the submit button
964 * currently.
965 *
966 * @return HRESULT
967 * @param dwFieldID Field ID of the submit button.
968 * @param pdwAdjacentTo Field ID where to put the submit button next to.
969 */
970HRESULT VBoxCredProvCredential::GetSubmitButtonValue(DWORD dwFieldID, DWORD *pdwAdjacentTo)
971{
972 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSubmitButtonValue: dwFieldID=%ld\n",
973 dwFieldID);
974
975 HRESULT hr = S_OK;
976
977 /* Validate parameters. */
978 if ( dwFieldID == VBOXCREDPROV_FIELDID_SUBMIT_BUTTON
979 && pdwAdjacentTo)
980 {
981 /* pdwAdjacentTo is a pointer to the fieldID you want the submit button to appear next to. */
982 *pdwAdjacentTo = VBOXCREDPROV_FIELDID_PASSWORD;
983 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSubmitButtonValue: dwFieldID=%ld, *pdwAdjacentTo=%ld\n",
984 dwFieldID, *pdwAdjacentTo);
985 }
986 else
987 hr = E_INVALIDARG;
988
989 return hr;
990}
991
992
993/**
994 * Sets the value of a specified field. Currently not used.
995 *
996 * @return HRESULT
997 * @param dwFieldID Field to set value for.
998 * @param pwszValue Actual value to set.
999 */
1000HRESULT VBoxCredProvCredential::SetStringValue(DWORD dwFieldID, PCWSTR pwszValue)
1001{
1002 RT_NOREF(dwFieldID, pwszValue);
1003
1004 /* Do more things here later. */
1005 HRESULT hr = S_OK;
1006
1007 VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetStringValue: dwFieldID=%ld, pcwzString=%ls, hr=%Rhrc\n",
1008 dwFieldID,
1009#ifdef DEBUG
1010 pwszValue ? pwszValue : L"<NULL>",
1011#else /* Never show any (sensitive) data in release mode! */
1012 L"XXX",
1013#endif
1014 hr);
1015
1016 return hr;
1017}
1018
1019
1020HRESULT VBoxCredProvCredential::GetBitmapValue(DWORD dwFieldID, HBITMAP *phBitmap)
1021{
1022 NOREF(dwFieldID);
1023 NOREF(phBitmap);
1024
1025 /* We don't do own bitmaps. */
1026 return E_NOTIMPL;
1027}
1028
1029
1030HRESULT VBoxCredProvCredential::GetCheckboxValue(DWORD dwFieldID, BOOL *pfChecked, PWSTR *ppwszLabel)
1031{
1032 NOREF(dwFieldID);
1033 NOREF(pfChecked);
1034 NOREF(ppwszLabel);
1035 return E_NOTIMPL;
1036}
1037
1038
1039HRESULT VBoxCredProvCredential::GetComboBoxValueCount(DWORD dwFieldID, DWORD *pcItems, DWORD *pdwSelectedItem)
1040{
1041 NOREF(dwFieldID);
1042 NOREF(pcItems);
1043 NOREF(pdwSelectedItem);
1044 return E_NOTIMPL;
1045}
1046
1047
1048HRESULT VBoxCredProvCredential::GetComboBoxValueAt(DWORD dwFieldID, DWORD dwItem, PWSTR *ppwszItem)
1049{
1050 NOREF(dwFieldID);
1051 NOREF(dwItem);
1052 NOREF(ppwszItem);
1053 return E_NOTIMPL;
1054}
1055
1056
1057HRESULT VBoxCredProvCredential::SetCheckboxValue(DWORD dwFieldID, BOOL fChecked)
1058{
1059 NOREF(dwFieldID);
1060 NOREF(fChecked);
1061 return E_NOTIMPL;
1062}
1063
1064
1065HRESULT VBoxCredProvCredential::SetComboBoxSelectedValue(DWORD dwFieldId, DWORD dwSelectedItem)
1066{
1067 NOREF(dwFieldId);
1068 NOREF(dwSelectedItem);
1069 return E_NOTIMPL;
1070}
1071
1072
1073HRESULT VBoxCredProvCredential::CommandLinkClicked(DWORD dwFieldID)
1074{
1075 NOREF(dwFieldID);
1076 return E_NOTIMPL;
1077}
1078
1079
1080/**
1081 * Does the actual authentication stuff to attempt a login.
1082 *
1083 * @return HRESULT
1084 * @param pcpGetSerializationResponse Credential serialization response.
1085 * @param pcpCredentialSerialization Details about the current credential.
1086 * @param ppwszOptionalStatusText Text to set. Optional.
1087 * @param pcpsiOptionalStatusIcon Status icon to set. Optional.
1088 */
1089HRESULT VBoxCredProvCredential::GetSerialization(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *pcpGetSerializationResponse,
1090 CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpCredentialSerialization,
1091 PWSTR *ppwszOptionalStatusText,
1092 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
1093{
1094 NOREF(ppwszOptionalStatusText);
1095 NOREF(pcpsiOptionalStatusIcon);
1096
1097 KERB_INTERACTIVE_UNLOCK_LOGON KerberosUnlockLogon;
1098 RT_BZERO(&KerberosUnlockLogon, sizeof(KerberosUnlockLogon));
1099
1100 /* Save a pointer to the interactive logon struct. */
1101 KERB_INTERACTIVE_LOGON *pLogon = &KerberosUnlockLogon.Logon;
1102
1103#ifdef DEBUG /* Note: NEVER print this in release mode! */
1104 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSerialization: Username=%ls, Password=%ls, Domain=%ls\n",
1105 m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME],
1106 m_apwszFields[VBOXCREDPROV_FIELDID_PASSWORD],
1107 m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME]);
1108#endif
1109
1110 HRESULT hr = kerberosLogonCreate(pLogon,
1111 m_enmUsageScenario,
1112 m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME],
1113 m_apwszFields[VBOXCREDPROV_FIELDID_PASSWORD],
1114 m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME]);
1115 if (SUCCEEDED(hr))
1116 {
1117 hr = kerberosLogonSerialize(pLogon,
1118 &pcpCredentialSerialization->rgbSerialization,
1119 &pcpCredentialSerialization->cbSerialization);
1120 if (SUCCEEDED(hr))
1121 {
1122 HANDLE hLSA;
1123 NTSTATUS s = LsaConnectUntrusted(&hLSA);
1124 hr = HRESULT_FROM_NT(s);
1125
1126 if (SUCCEEDED(hr))
1127 {
1128#if 0 /* eeek. leaving this as an example of how not to handle a string constant. */
1129 size_t cchKerberosName;
1130 hr = StringCchLengthA(NEGOSSP_NAME_A, USHORT_MAX, &cchKerberosName);
1131 if (SUCCEEDED(hr))
1132 {
1133 USHORT usLength;
1134 hr = SizeTToUShort(cchKerberosName, &usLength);
1135 if (SUCCEEDED(hr))
1136#endif
1137 {
1138 LSA_STRING lsaszKerberosName;
1139 lsaszKerberosName.Buffer = (PCHAR)NEGOSSP_NAME_A;
1140 lsaszKerberosName.Length = sizeof(NEGOSSP_NAME_A) - 1;
1141 lsaszKerberosName.MaximumLength = sizeof(NEGOSSP_NAME_A);
1142
1143 ULONG ulAuthPackage = 0;
1144
1145 s = LsaLookupAuthenticationPackage(hLSA, &lsaszKerberosName, &ulAuthPackage);
1146 hr = HRESULT_FROM_NT(s);
1147
1148 if (SUCCEEDED(hr))
1149 {
1150 pcpCredentialSerialization->ulAuthenticationPackage = ulAuthPackage;
1151 pcpCredentialSerialization->clsidCredentialProvider = CLSID_VBoxCredProvider;
1152
1153 /* We're done -- let the logon UI know. */
1154 *pcpGetSerializationResponse = CPGSR_RETURN_CREDENTIAL_FINISHED;
1155
1156 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: Finished for user '%ls' (domain '%ls')\n",
1157 m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME],
1158 m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME]);
1159 }
1160 else
1161 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: LsaLookupAuthenticationPackage failed with ntStatus=%ld\n", s);
1162 }
1163#if 0
1164 }
1165#endif
1166 LsaDeregisterLogonProcess(hLSA);
1167 }
1168 else
1169 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: LsaConnectUntrusted failed with ntStatus=%ld\n", s);
1170 }
1171 else
1172 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: kerberosLogonSerialize failed with hr=0x%08x\n", hr);
1173
1174 kerberosLogonDestroy(pLogon);
1175 pLogon = NULL;
1176 }
1177 else
1178 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: kerberosLogonCreate failed with hr=0x%08x\n", hr);
1179
1180 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization returned hr=0x%08x\n", hr);
1181 return hr;
1182}
1183
1184
1185/**
1186 * Called by LogonUI after a logon attempt was made -- here we could set an additional status
1187 * text and/or icon.
1188 *
1189 * Currently not used.
1190 *
1191 * @return HRESULT
1192 * @param ntStatus NT status of logon attempt reported by Winlogon.
1193 * @param ntSubStatus NT substatus of logon attempt reported by Winlogon.
1194 * @param ppwszOptionalStatusText Pointer that receives the optional status text.
1195 * @param pcpsiOptionalStatusIcon Pointer that receives the optional status icon.
1196 */
1197HRESULT VBoxCredProvCredential::ReportResult(NTSTATUS ntStatus,
1198 NTSTATUS ntSubStatus,
1199 PWSTR *ppwszOptionalStatusText,
1200 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
1201{
1202 RT_NOREF(ntStatus, ntSubStatus, ppwszOptionalStatusText, pcpsiOptionalStatusIcon);
1203 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ReportResult: ntStatus=%ld, ntSubStatus=%ld\n",
1204 ntStatus, ntSubStatus);
1205 return E_NOTIMPL;
1206}
1207
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