VirtualBox

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

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

VBoxCredProv: Don't retrieve new credentials if current ones were not processed yet.

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