VirtualBox

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

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

Additions/VBoxCredProv: Renamed resetField() to setField() and implemented the ability to notify LogonUI + set a new value if needed. Fixed default value handling / resetting.

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