VirtualBox

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

Last change on this file since 69360 was 68099, checked in by vboxsync, 7 years ago

VBoxCredProv/VBoxCredProvCredential.cpp: Emphasize which string is what and free those accordingly in VBoxCredProvCredential::RetrieveCredentials(). Updated documentation.

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