VirtualBox

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

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

Additions/VBoxCredProv: Also try to return the current values in VBoxCredProvProvider::GetFieldDescriptorAt() (except passwords), as Windows 10 also uses those values for the new logon UI.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.8 KB
Line 
1/* $Id: VBoxCredProvCredential.cpp 67814 2017-07-06 08:37:31Z 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) * sizeof(RTUTF16), 3 /* cPasses */);
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 L"XXX" /* Don't show any actual values in release mode. */,
456#endif
457 fNotifyUI, hr);
458 return hr;
459}
460
461/**
462 * Resets (wipes) stored credentials.
463 *
464 * @return HRESULT
465 */
466HRESULT VBoxCredProvCredential::Reset(void)
467{
468 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Reset: Wiping credentials user=%ls, pw=%ls, domain=%ls\n",
469 m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME] ? m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME] : L"<NULL>",
470#ifdef DEBUG
471 m_apwszFields[VBOXCREDPROV_FIELDID_PASSWORD] ? m_apwszFields[VBOXCREDPROV_FIELDID_PASSWORD] : L"<NULL>",
472#else
473 L"XXX" /* Don't show any passwords in release mode. */,
474#endif
475 m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME] ? m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME] : L"<NULL>");
476
477 /* Note: Do not reset the user name and domain name here,
478 * as they could still being queried (again) by LogonUI on failed login attempts. */
479 HRESULT hr = setField(VBOXCREDPROV_FIELDID_PASSWORD, NULL /* Use default value */, true /* fNotifyUI */);
480
481 m_fIsSelected = false;
482
483 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Reset\n");
484 return hr;
485}
486
487
488/**
489 * Checks and retrieves credentials provided by the host + does account lookup on eventually
490 * renamed user accounts.
491 *
492 * @return IPRT status code.
493 */
494int VBoxCredProvCredential::RetrieveCredentials(void)
495{
496 PRTUTF16 pwszUser = NULL;
497 PRTUTF16 pwszPassword = NULL;
498 PRTUTF16 pwszDomain = NULL;
499
500 int rc = VbglR3CredentialsQueryAvailability();
501 if (RT_SUCCESS(rc))
502 {
503 /*
504 * Set status to "terminating" to let the host know this module now
505 * tries to receive and use passed credentials so that credentials from
506 * the host won't be sent twice.
507 */
508 VBoxCredProvReportStatus(VBoxGuestFacilityStatus_Terminating);
509
510 rc = VbglR3CredentialsRetrieveUtf16(&pwszUser, &pwszPassword, &pwszDomain);
511
512 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Retrieved credentials with rc=%Rrc\n", rc);
513 }
514
515 if (RT_SUCCESS(rc))
516 {
517 /*
518 * In case we got a "display name" (e.g. "John Doe")
519 * instead of the real user name (e.g. "jdoe") we have
520 * to translate the data first ...
521 */
522 PWSTR pwszAcount;
523 if (TranslateAccountName(pwszUser, &pwszAcount))
524 {
525 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Translated account name %ls -> %ls\n",
526 pwszUser, pwszAcount);
527
528 RTMemWipeThoroughly(pwszUser, RTUtf16Len(pwszUser) + sizeof(RTUTF16), 3 /* Passes */);
529 RTUtf16Free(pwszUser);
530
531 pwszUser = pwszAcount;
532 }
533 else
534 {
535 /*
536 * Okay, no display name, but maybe it's a
537 * principal name from which we have to extract the domain from?
538 * ([email protected] -> jdoe in domain my-domain.sub.net.com.)
539 */
540 PWSTR pwszDomain;
541 if (ExtractAccoutData(pwszUser, &pwszAcount, &pwszDomain))
542 {
543 /* Update user name. */
544 if (pwszUser)
545 {
546 RTMemWipeThoroughly(pwszUser, RTUtf16Len(pwszUser) + sizeof(RTUTF16), 3 /* Passes */);
547 RTUtf16Free(pwszUser);
548 }
549 pwszUser = pwszAcount;
550
551 /* Update domain. */
552 if (pwszDomain)
553 {
554 RTMemWipeThoroughly(pwszDomain, RTUtf16Len(pwszDomain) + sizeof(RTUTF16), 3 /* Passes */);
555 RTUtf16Free(pwszDomain);
556 }
557 pwszDomain = pwszDomain;
558
559 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Extracted account data pwszAccount=%ls, pwszDomain=%ls\n",
560 pwszUser, pwszDomain);
561 }
562 }
563
564 m_fHaveCreds = true;
565 }
566
567 /* If credentials already were retrieved (also by a former call), don't try to retrieve new ones
568 * and just report back the already retrieved ones. */
569 if (m_fHaveCreds)
570 {
571 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Credentials already retrieved\n");
572
573 setField(VBOXCREDPROV_FIELDID_USERNAME, pwszUser, true /* fNotifyUI */);
574 setField(VBOXCREDPROV_FIELDID_PASSWORD, pwszPassword, true /* fNotifyUI */);
575 setField(VBOXCREDPROV_FIELDID_DOMAINNAME, pwszDomain, true /* fNotifyUI */);
576 }
577
578 VbglR3CredentialsDestroyUtf16(pwszUser, pwszPassword, pwszDomain, 3 /* cPasses */);
579
580 VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Returned rc=%Rrc\n", rc);
581 return rc;
582}
583
584
585/**
586 * Initializes this credential with the current credential provider
587 * usage scenario.
588 */
589HRESULT VBoxCredProvCredential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO enmUsageScenario)
590{
591 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Initialize: enmUsageScenario=%ld\n", enmUsageScenario);
592 m_enmUsageScenario = enmUsageScenario;
593 return S_OK;
594}
595
596
597/**
598 * Called by LogonUI when it needs this credential's advice.
599 *
600 * At the moment we only grab the credential provider events so that we can
601 * trigger a re-enumeration of the credentials later.
602 */
603HRESULT VBoxCredProvCredential::Advise(ICredentialProviderCredentialEvents *pEvents)
604{
605 VBoxCredProvVerbose(0, "VBoxCredProvCredential::Advise: pEvents=0x%p\n", pEvents);
606
607 if (m_pEvents)
608 {
609 m_pEvents->Release();
610 m_pEvents = NULL;
611 }
612
613 return pEvents->QueryInterface(IID_PPV_ARGS(&m_pEvents));
614}
615
616
617/**
618 * Called by LogonUI when it's finished with handling this credential.
619 *
620 * We only need to release the credential provider events, if any.
621 */
622HRESULT VBoxCredProvCredential::UnAdvise(void)
623{
624 VBoxCredProvVerbose(0, "VBoxCredProvCredential::UnAdvise\n");
625
626 if (m_pEvents)
627 {
628 m_pEvents->Release();
629 m_pEvents = NULL;
630 }
631
632 return S_OK;
633}
634
635
636/**
637 * Called by LogonUI when a user profile (tile) has been selected.
638 *
639 * As we don't want Winlogon to try logging in immediately we set pfAutoLogon
640 * to FALSE (if set).
641 */
642HRESULT VBoxCredProvCredential::SetSelected(PBOOL pfAutoLogon)
643{
644 VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetSelected\n");
645
646 /*
647 * Don't do auto logon here because it would retry too often with
648 * every credential field (user name, password, domain, ...) which makes
649 * winlogon wait before new login attempts can be made.
650 */
651 if (pfAutoLogon)
652 *pfAutoLogon = FALSE;
653
654 m_fIsSelected = true;
655
656 return S_OK;
657}
658
659
660/**
661 * Called by LogonUI when a user profile (tile) has been unselected again.
662 */
663HRESULT VBoxCredProvCredential::SetDeselected(void)
664{
665 VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetDeselected\n");
666
667 Reset();
668
669 return S_OK;
670}
671
672
673/**
674 * Called by LogonUI to retrieve the (interactive) state of a UI field.
675 */
676HRESULT VBoxCredProvCredential::GetFieldState(DWORD dwFieldID, CREDENTIAL_PROVIDER_FIELD_STATE *pFieldState,
677 CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE *pFieldstateInteractive)
678{
679 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetFieldState: dwFieldID=%ld\n", dwFieldID);
680
681 HRESULT hr = S_OK;
682
683 if (dwFieldID < VBOXCREDPROV_NUM_FIELDS)
684 {
685 if (pFieldState)
686 *pFieldState = s_VBoxCredProvDefaultFields[dwFieldID].state;
687
688 if (pFieldstateInteractive)
689 *pFieldstateInteractive = s_VBoxCredProvDefaultFields[dwFieldID].stateInteractive;
690 }
691 else
692 hr = E_INVALIDARG;
693
694 return hr;
695}
696
697
698/**
699 * Searches the account name based on a display (real) name (e.g. "John Doe" -> "jdoe").
700 * Result "ppwszAccoutName" needs to be freed with CoTaskMemFree!
701 */
702BOOL VBoxCredProvCredential::TranslateAccountName(PWSTR pwszDisplayName, PWSTR *ppwszAccoutName)
703{
704 AssertPtrReturn(pwszDisplayName, FALSE);
705 VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName: Getting account name for \"%ls\" ...\n",
706 pwszDisplayName);
707
708 /** @todo Do we need ADS support (e.g. TranslateNameW) here? */
709 BOOL fFound = FALSE; /* Did we find the desired user? */
710 NET_API_STATUS rcStatus;
711 DWORD dwLevel = 2; /* Detailed information about user accounts. */
712 DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH;
713 DWORD dwEntriesRead = 0;
714 DWORD dwTotalEntries = 0;
715 DWORD dwResumeHandle = 0;
716 LPUSER_INFO_2 pBuf = NULL;
717 LPUSER_INFO_2 pCurBuf = NULL;
718 do
719 {
720 rcStatus = NetUserEnum(NULL, /* Server name, NULL for localhost. */
721 dwLevel,
722 FILTER_NORMAL_ACCOUNT,
723 (LPBYTE*)&pBuf,
724 dwPrefMaxLen,
725 &dwEntriesRead,
726 &dwTotalEntries,
727 &dwResumeHandle);
728 if ( rcStatus == NERR_Success
729 || rcStatus == ERROR_MORE_DATA)
730 {
731 if ((pCurBuf = pBuf) != NULL)
732 {
733 for (DWORD i = 0; i < dwEntriesRead; i++)
734 {
735 /*
736 * Search for the "display name" - that might be
737 * "John Doe" or something similar the user recognizes easier
738 * and may not the same as the "account" name (e.g. "jdoe").
739 */
740 if ( pCurBuf
741 && pCurBuf->usri2_full_name
742 && StrCmpI(pwszDisplayName, pCurBuf->usri2_full_name) == 0)
743 {
744 /*
745 * Copy the real user name (e.g. "jdoe") to our
746 * output buffer.
747 */
748 LPWSTR pwszTemp;
749 HRESULT hr = SHStrDupW(pCurBuf->usri2_name, &pwszTemp);
750 if (hr == S_OK)
751 {
752 *ppwszAccoutName = pwszTemp;
753 fFound = TRUE;
754 }
755 else
756 VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName: Error copying data, hr=%08x\n", hr);
757 break;
758 }
759 pCurBuf++;
760 }
761 }
762 if (pBuf != NULL)
763 {
764 NetApiBufferFree(pBuf);
765 pBuf = NULL;
766 }
767 }
768 } while (rcStatus == ERROR_MORE_DATA && !fFound);
769
770 if (pBuf != NULL)
771 {
772 NetApiBufferFree(pBuf);
773 pBuf = NULL;
774 }
775
776 VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName returned rcStatus=%ld, fFound=%RTbool\n",
777 rcStatus, fFound);
778 return fFound;
779
780#if 0
781 DWORD dwErr = NO_ERROR;
782 ULONG cbLen = 0;
783 if ( TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, NULL, &cbLen)
784 && cbLen > 0)
785 {
786 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetAccountName: Translated ADS name has %u characters\n", cbLen));
787
788 ppwszAccoutName = (PWSTR)RTMemAlloc(cbLen * sizeof(WCHAR));
789 AssertPtrReturn(pwszName, FALSE);
790 if (TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, ppwszAccoutName, &cbLen))
791 {
792 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetAccountName: Real ADS account name of '%ls' is '%ls'\n",
793 pwszName, ppwszAccoutName));
794 }
795 else
796 {
797 RTMemFree(ppwszAccoutName);
798 dwErr = GetLastError();
799 }
800 }
801 else
802 dwErr = GetLastError();
803 /* The above method for looking up in ADS failed, try another one. */
804 if (dwErr != NO_ERROR)
805 {
806 dwErr = NO_ERROR;
807
808 }
809#endif
810}
811
812
813/**
814 * Extracts the actual account name & domain from a (raw) account data string.
815 *
816 * This might be a principal or FQDN string.
817 */
818BOOL VBoxCredProvCredential::ExtractAccoutData(PWSTR pwszAccountData, PWSTR *ppwszAccoutName, PWSTR *ppwszDomain)
819{
820 AssertPtrReturn(pwszAccountData, FALSE);
821 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: Getting account name for \"%ls\" ...\n",
822 pwszAccountData);
823 HRESULT hr = E_FAIL;
824
825 /* Try to figure out whether this is a principal name (user@domain). */
826 LPWSTR pPos = NULL;
827 if ( (pPos = StrChrW(pwszAccountData, L'@')) != NULL
828 && pPos != pwszAccountData)
829 {
830 size_t cbSize = (pPos - pwszAccountData) * sizeof(WCHAR);
831 LPWSTR pwszName = (LPWSTR)CoTaskMemAlloc(cbSize + sizeof(WCHAR)); /* Space for terminating zero. */
832 LPWSTR pwszDomain = NULL;
833 AssertPtr(pwszName);
834 hr = StringCbCopyN(pwszName, cbSize + sizeof(WCHAR), pwszAccountData, cbSize);
835 if (SUCCEEDED(hr))
836 {
837 *ppwszAccoutName = pwszName;
838 pPos++; /* Skip @, point to domain name (if any). */
839 if ( pPos != NULL
840 && *pPos != L'\0')
841 {
842 hr = SHStrDupW(pPos, &pwszDomain);
843 if (SUCCEEDED(hr))
844 {
845 *ppwszDomain = pwszDomain;
846 }
847 else
848 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: Error copying domain data, hr=%08x\n", hr);
849 }
850 else
851 {
852 hr = E_FAIL;
853 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: No domain name found!\n");
854 }
855 }
856 else
857 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: Error copying account data, hr=%08x\n", hr);
858
859 if (hr != S_OK)
860 {
861 CoTaskMemFree(pwszName);
862 if (pwszDomain)
863 CoTaskMemFree(pwszDomain);
864 }
865 }
866 else
867 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: No valid principal account name found!\n");
868
869 return (hr == S_OK);
870}
871
872
873/**
874 * Returns the current value of a specified LogonUI field.
875 *
876 * @return IPRT status code.
877 * @param dwFieldID Field ID to get value for.
878 * @param ppwszString Pointer that receives the actual value of the specified field.
879 */
880HRESULT VBoxCredProvCredential::GetStringValue(DWORD dwFieldID, PWSTR *ppwszString)
881{
882 HRESULT hr;
883 if ( dwFieldID < VBOXCREDPROV_NUM_FIELDS
884 && ppwszString)
885 {
886 switch (dwFieldID)
887 {
888 case VBOXCREDPROV_FIELDID_SUBMIT_BUTTON:
889 /* Fill in standard value to make Winlogon happy. */
890 hr = SHStrDupW(L"Submit", ppwszString);
891 break;
892
893 default:
894 if ( m_apwszFields[dwFieldID]
895 && RTUtf16Len(m_apwszFields[dwFieldID]))
896 hr = SHStrDupW(m_apwszFields[dwFieldID], ppwszString);
897 else /* Fill in an empty value. */
898 hr = SHStrDupW(L"", ppwszString);
899 break;
900 }
901 }
902 else
903 hr = E_INVALIDARG;
904
905 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetStringValue: m_fIsSelected=%RTbool, dwFieldID=%ld, ppwszString=%ls, hr=%Rhrc\n",
906 m_fIsSelected, dwFieldID,
907#ifdef DEBUG
908 ppwszString ? *ppwszString : L"<NULL>",
909#else /* Never show any (sensitive) data in release mode! */
910 L"XXX",
911#endif
912 hr);
913 return hr;
914}
915
916
917/**
918 * Returns back the field ID of which the submit button should be put next to.
919 *
920 * We always want to be the password field put next to the submit button
921 * currently.
922 *
923 * @return HRESULT
924 * @param dwFieldID Field ID of the submit button.
925 * @param pdwAdjacentTo Field ID where to put the submit button next to.
926 */
927HRESULT VBoxCredProvCredential::GetSubmitButtonValue(DWORD dwFieldID, DWORD *pdwAdjacentTo)
928{
929 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSubmitButtonValue: dwFieldID=%ld\n",
930 dwFieldID);
931
932 HRESULT hr = S_OK;
933
934 /* Validate parameters. */
935 if ( dwFieldID == VBOXCREDPROV_FIELDID_SUBMIT_BUTTON
936 && pdwAdjacentTo)
937 {
938 /* pdwAdjacentTo is a pointer to the fieldID you want the submit button to appear next to. */
939 *pdwAdjacentTo = VBOXCREDPROV_FIELDID_PASSWORD;
940 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSubmitButtonValue: dwFieldID=%ld, *pdwAdjacentTo=%ld\n",
941 dwFieldID, *pdwAdjacentTo);
942 }
943 else
944 hr = E_INVALIDARG;
945
946 return hr;
947}
948
949
950/**
951 * Sets the value of a specified field. Currently not used.
952 *
953 * @return HRESULT
954 * @param dwFieldID Field to set value for.
955 * @param pwszValue Actual value to set.
956 */
957HRESULT VBoxCredProvCredential::SetStringValue(DWORD dwFieldID, PCWSTR pwszValue)
958{
959 RT_NOREF(dwFieldID, pwszValue);
960
961 /* Do more things here later. */
962 HRESULT hr = S_OK;
963
964 VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetStringValue: dwFieldID=%ld, pcwzString=%ls, hr=%Rhrc\n",
965 dwFieldID,
966#ifdef DEBUG
967 pwszValue ? pwszValue : L"<NULL>",
968#else /* Never show any (sensitive) data in release mode! */
969 L"XXX",
970#endif
971 hr);
972
973 return hr;
974}
975
976
977HRESULT VBoxCredProvCredential::GetBitmapValue(DWORD dwFieldID, HBITMAP *phBitmap)
978{
979 NOREF(dwFieldID);
980 NOREF(phBitmap);
981
982 /* We don't do own bitmaps. */
983 return E_NOTIMPL;
984}
985
986
987HRESULT VBoxCredProvCredential::GetCheckboxValue(DWORD dwFieldID, BOOL *pfChecked, PWSTR *ppwszLabel)
988{
989 NOREF(dwFieldID);
990 NOREF(pfChecked);
991 NOREF(ppwszLabel);
992 return E_NOTIMPL;
993}
994
995
996HRESULT VBoxCredProvCredential::GetComboBoxValueCount(DWORD dwFieldID, DWORD *pcItems, DWORD *pdwSelectedItem)
997{
998 NOREF(dwFieldID);
999 NOREF(pcItems);
1000 NOREF(pdwSelectedItem);
1001 return E_NOTIMPL;
1002}
1003
1004
1005HRESULT VBoxCredProvCredential::GetComboBoxValueAt(DWORD dwFieldID, DWORD dwItem, PWSTR *ppwszItem)
1006{
1007 NOREF(dwFieldID);
1008 NOREF(dwItem);
1009 NOREF(ppwszItem);
1010 return E_NOTIMPL;
1011}
1012
1013
1014HRESULT VBoxCredProvCredential::SetCheckboxValue(DWORD dwFieldID, BOOL fChecked)
1015{
1016 NOREF(dwFieldID);
1017 NOREF(fChecked);
1018 return E_NOTIMPL;
1019}
1020
1021
1022HRESULT VBoxCredProvCredential::SetComboBoxSelectedValue(DWORD dwFieldId, DWORD dwSelectedItem)
1023{
1024 NOREF(dwFieldId);
1025 NOREF(dwSelectedItem);
1026 return E_NOTIMPL;
1027}
1028
1029
1030HRESULT VBoxCredProvCredential::CommandLinkClicked(DWORD dwFieldID)
1031{
1032 NOREF(dwFieldID);
1033 return E_NOTIMPL;
1034}
1035
1036
1037/**
1038 * Does the actual authentication stuff to attempt a login.
1039 *
1040 * @return HRESULT
1041 * @param pcpGetSerializationResponse Credential serialization response.
1042 * @param pcpCredentialSerialization Details about the current credential.
1043 * @param ppwszOptionalStatusText Text to set. Optional.
1044 * @param pcpsiOptionalStatusIcon Status icon to set. Optional.
1045 */
1046HRESULT VBoxCredProvCredential::GetSerialization(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *pcpGetSerializationResponse,
1047 CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpCredentialSerialization,
1048 PWSTR *ppwszOptionalStatusText,
1049 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
1050{
1051 NOREF(ppwszOptionalStatusText);
1052 NOREF(pcpsiOptionalStatusIcon);
1053
1054 KERB_INTERACTIVE_UNLOCK_LOGON KerberosUnlockLogon;
1055 RT_BZERO(&KerberosUnlockLogon, sizeof(KerberosUnlockLogon));
1056
1057 /* Save a pointer to the interactive logon struct. */
1058 KERB_INTERACTIVE_LOGON *pLogon = &KerberosUnlockLogon.Logon;
1059
1060#ifdef DEBUG /* Note: NEVER print this in release mode! */
1061 VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSerialization: Username=%ls, Password=%ls, Domain=%ls\n",
1062 m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME],
1063 m_apwszFields[VBOXCREDPROV_FIELDID_PASSWORD],
1064 m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME]);
1065#endif
1066
1067 HRESULT hr = kerberosLogonCreate(pLogon,
1068 m_enmUsageScenario,
1069 m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME],
1070 m_apwszFields[VBOXCREDPROV_FIELDID_PASSWORD],
1071 m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME]);
1072 if (SUCCEEDED(hr))
1073 {
1074 hr = kerberosLogonSerialize(pLogon,
1075 &pcpCredentialSerialization->rgbSerialization,
1076 &pcpCredentialSerialization->cbSerialization);
1077 if (SUCCEEDED(hr))
1078 {
1079 HANDLE hLSA;
1080 NTSTATUS s = LsaConnectUntrusted(&hLSA);
1081 hr = HRESULT_FROM_NT(s);
1082
1083 if (SUCCEEDED(hr))
1084 {
1085 LSA_STRING lsaszKerberosName;
1086 size_t cchKerberosName;
1087 hr = StringCchLengthA(NEGOSSP_NAME_A, USHORT_MAX, &cchKerberosName);
1088 if (SUCCEEDED(hr))
1089 {
1090 USHORT usLength;
1091 hr = SizeTToUShort(cchKerberosName, &usLength);
1092 if (SUCCEEDED(hr))
1093 {
1094 lsaszKerberosName.Buffer = (PCHAR)NEGOSSP_NAME_A;
1095 lsaszKerberosName.Length = usLength;
1096 lsaszKerberosName.MaximumLength = lsaszKerberosName.Length + 1;
1097
1098 ULONG ulAuthPackage = 0;
1099
1100 s = LsaLookupAuthenticationPackage(hLSA, &lsaszKerberosName, &ulAuthPackage);
1101 hr = HRESULT_FROM_NT(s);
1102
1103 if (SUCCEEDED(hr))
1104 {
1105 pcpCredentialSerialization->ulAuthenticationPackage = ulAuthPackage;
1106 pcpCredentialSerialization->clsidCredentialProvider = CLSID_VBoxCredProvider;
1107
1108 /* We're done -- let the logon UI know. */
1109 *pcpGetSerializationResponse = CPGSR_RETURN_CREDENTIAL_FINISHED;
1110
1111 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: Finished for user '%ls' (domain '%s')\n",
1112 m_apwszFields[VBOXCREDPROV_FIELDID_USERNAME],
1113 m_apwszFields[VBOXCREDPROV_FIELDID_DOMAINNAME]);
1114 }
1115 else
1116 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: LsaLookupAuthenticationPackage failed with ntStatus=%ld\n", s);
1117 }
1118 }
1119
1120 LsaDeregisterLogonProcess(hLSA);
1121 }
1122 else
1123 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: LsaConnectUntrusted failed with ntStatus=%ld\n", s);
1124 }
1125 else
1126 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: kerberosLogonSerialize failed with hr=0x%08x\n", hr);
1127
1128 kerberosLogonDestroy(pLogon);
1129 pLogon = NULL;
1130 }
1131 else
1132 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization: kerberosLogonCreate failed with hr=0x%08x\n", hr);
1133
1134 VBoxCredProvVerbose(1, "VBoxCredProvCredential::GetSerialization returned hr=0x%08x\n", hr);
1135 return hr;
1136}
1137
1138
1139/**
1140 * Called by LogonUI after a logon attempt was made -- here we could set an additional status
1141 * text and/or icon.
1142 *
1143 * Currently not used.
1144 *
1145 * @return HRESULT
1146 * @param ntStatus NT status of logon attempt reported by Winlogon.
1147 * @param ntSubStatus NT substatus of logon attempt reported by Winlogon.
1148 * @param ppwszOptionalStatusText Pointer that receives the optional status text.
1149 * @param pcpsiOptionalStatusIcon Pointer that receives the optional status icon.
1150 */
1151HRESULT VBoxCredProvCredential::ReportResult(NTSTATUS ntStatus,
1152 NTSTATUS ntSubStatus,
1153 PWSTR *ppwszOptionalStatusText,
1154 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
1155{
1156 RT_NOREF(ntStatus, ntSubStatus, ppwszOptionalStatusText, pcpsiOptionalStatusIcon);
1157 VBoxCredProvVerbose(0, "VBoxCredProvCredential::ReportResult: ntStatus=%ld, ntSubStatus=%ld\n",
1158 ntStatus, ntSubStatus);
1159 return E_NOTIMPL;
1160}
1161
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