VirtualBox

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

Last change on this file since 52002 was 49722, checked in by vboxsync, 11 years ago

VBoxCredProv: Fixed auto-logon breakage introduced in r85893, added some more (verbose) logging.

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