VirtualBox

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

Last change on this file since 68098 was 68098, checked in by vboxsync, 8 years ago

VBoxCredProv/VBoxCredProvCredential.cpp: Logging domain name requires Unicode logging.

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