VirtualBox

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

Last change on this file since 40272 was 40271, checked in by vboxsync, 13 years ago

VBoxCredProv: fixed svn properties

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.8 KB
Line 
1/* $Id: VBoxCredProvCredential.cpp 40271 2012-02-28 11:22:04Z vboxsync $ */
2/** @file
3 * VBoxCredProvCredential - Class for keeping and handling the passed
4 * credentials.
5 */
6
7/*
8 * Copyright (C) 2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19#ifndef WIN32_NO_STATUS
20#include <ntstatus.h>
21#define WIN32_NO_STATUS
22#endif
23
24#include "VBoxCredentialProvider.h"
25
26#include "VBoxCredProvProvider.h"
27#include "VBoxCredProvCredential.h"
28#include "VBoxCredProvUtils.h"
29
30#include <lm.h>
31
32#include <iprt/mem.h>
33#include <iprt/string.h>
34
35
36
37VBoxCredProvCredential::VBoxCredProvCredential(VBoxCredProvProvider *pProvider) :
38 m_cRefCount(1),
39 m_pEvents(NULL)
40{
41 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Created, pProvider=%p\n",
42 pProvider);
43
44 VBoxCredentialProviderAcquire();
45
46 AssertPtr(pProvider);
47 m_pProvider = pProvider;
48
49 ZeroMemory(m_pwszCredentials, sizeof(m_pwszCredentials));
50}
51
52
53VBoxCredProvCredential::~VBoxCredProvCredential(void)
54{
55 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Destroying\n");
56
57 VBoxCredentialProviderRelease();
58}
59
60
61/* IUnknown overrides. */
62ULONG VBoxCredProvCredential::AddRef(void)
63{
64 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Increasing reference to %ld\n",
65 m_cRefCount + 1);
66
67 return m_cRefCount++;
68}
69
70
71ULONG VBoxCredProvCredential::Release(void)
72{
73 Assert(m_cRefCount);
74
75 ULONG cRefCount = --m_cRefCount;
76 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Decreasing reference to %ld\n",
77 cRefCount);
78 if (!cRefCount)
79 {
80 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Calling destructor\n");
81 delete this;
82 }
83 return cRefCount;
84}
85
86
87HRESULT VBoxCredProvCredential::QueryInterface(REFIID interfaceID, void **ppvInterface)
88{
89 HRESULT hr = S_OK;;
90 if (ppvInterface)
91 {
92 if ( IID_IUnknown == interfaceID
93 || IID_ICredentialProviderCredential == interfaceID)
94 {
95 *ppvInterface = static_cast<IUnknown*>(this);
96 reinterpret_cast<IUnknown*>(*ppvInterface)->AddRef();
97 }
98 else
99 {
100 *ppvInterface = NULL;
101 hr = E_NOINTERFACE;
102 }
103 }
104 else
105 hr = E_INVALIDARG;
106
107 return hr;
108}
109
110
111/**
112 * Assigns or copies a RTUTF16 string to a UNICODE_STRING.
113 *
114 * When fCopy is false, this does *not* copy its contents
115 * and only assigns its code points to the destination!
116 * When fCopy is true, the actual string buffer gets copied.
117 *
118 * Does not take terminating \0 into account.
119 *
120 * @return HRESULT
121 * @param pUnicodeDest Unicode string assigning the UTF16 string to.
122 * @param pwszSource UTF16 string to assign.
123 * @param fCopy Whether to just assign or copy the actual buffer
124 * contents from source -> dest.
125 */
126HRESULT VBoxCredProvCredential::RTUTF16toToUnicode(PUNICODE_STRING pUnicodeDest, PRTUTF16 pwszSource,
127 bool fCopy)
128{
129 AssertPtrReturn(pwszSource, VERR_INVALID_POINTER);
130 AssertPtrReturn(pUnicodeDest, VERR_INVALID_POINTER);
131
132 size_t cbLen = RTUtf16Len(pwszSource) * sizeof(RTUTF16);
133
134 pUnicodeDest->Length = cbLen;
135 pUnicodeDest->MaximumLength = pUnicodeDest->Length;
136
137 if (fCopy)
138 {
139 CopyMemory(pUnicodeDest->Buffer, pwszSource, cbLen);
140 }
141 else /* Just assign the buffer. */
142 pUnicodeDest->Buffer = pwszSource;
143
144 return S_OK;
145}
146
147
148HRESULT VBoxCredProvCredential::AllocateLogonPackage(const KERB_INTERACTIVE_UNLOCK_LOGON& UnlockLogon,
149 PBYTE *ppPackage, DWORD *pcbPackage)
150{
151 AssertPtrReturn(ppPackage, E_INVALIDARG);
152 AssertPtrReturn(pcbPackage, E_INVALIDARG);
153
154 const KERB_INTERACTIVE_LOGON *pLogonIn = &UnlockLogon.Logon;
155
156 /*
157 * First, allocate enough space for the logon structure itself and separate
158 * string buffers right after it to store the actual user, password and domain
159 * credentials.
160 */
161 DWORD cbLogon = sizeof(KERB_INTERACTIVE_UNLOCK_LOGON)
162 + pLogonIn->LogonDomainName.Length +
163 + pLogonIn->UserName.Length +
164 + pLogonIn->Password.Length;
165
166#ifdef DEBUG
167 VBoxCredProvVerbose(3, "VBoxCredProvCredential: AllocateLogonPackage: Allocating %ld bytes (%d bytes credentials)\n",
168 cbLogon, cbLogon - sizeof(KERB_INTERACTIVE_UNLOCK_LOGON));
169#endif
170
171 KERB_INTERACTIVE_UNLOCK_LOGON *pLogon = (KERB_INTERACTIVE_UNLOCK_LOGON*)CoTaskMemAlloc(cbLogon);
172 if (!pLogon)
173 return E_OUTOFMEMORY;
174
175 /* Let our byte buffer point to the end of our allocated structure so that it can
176 * be used to store the credential data sequentially in a binary blob
177 * (without terminating \0). */
178 PBYTE pbBuffer = (PBYTE)pLogon + sizeof(KERB_INTERACTIVE_UNLOCK_LOGON);
179
180 /* The buffer of the packed destination string does not contain the actual
181 * string content but a relative offset starting at the given
182 * KERB_INTERACTIVE_UNLOCK_LOGON structure. */
183#define KERB_CRED_INIT_PACKED(StringDst, StringSrc, LogonOffset) \
184 StringDst.Length = StringSrc.Length; \
185 StringDst.MaximumLength = StringSrc.Length; \
186 StringDst.Buffer = (PWSTR)pbBuffer; \
187 CopyMemory(StringDst.Buffer, StringSrc.Buffer, StringDst.Length); \
188 StringDst.Buffer = (PWSTR)(pbBuffer - (PBYTE)LogonOffset); \
189 pbBuffer += StringDst.Length;
190
191 ZeroMemory(&pLogon->LogonId, sizeof(LUID));
192
193 KERB_INTERACTIVE_LOGON *pLogonOut = &pLogon->Logon;
194 pLogonOut->MessageType = pLogonIn->MessageType;
195
196 KERB_CRED_INIT_PACKED(pLogonOut->LogonDomainName, pLogonIn->LogonDomainName, pLogon);
197 KERB_CRED_INIT_PACKED(pLogonOut->UserName , pLogonIn->UserName, pLogon);
198 KERB_CRED_INIT_PACKED(pLogonOut->Password , pLogonIn->Password, pLogon);
199
200 *ppPackage = (PBYTE)pLogon;
201 *pcbPackage = cbLogon;
202
203#undef KERB_CRED_INIT_PACKED
204
205 return S_OK;
206}
207
208
209void VBoxCredProvCredential::Reset(void)
210{
211 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Wiping credentials ...\n");
212
213 VbglR3CredentialsDestroyUtf16(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
214 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
215 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME],
216 3 /* Passes */);
217 if (m_pEvents)
218 {
219 m_pEvents->SetFieldString(this, VBOXCREDPROV_FIELDID_USERNAME, NULL);
220 m_pEvents->SetFieldString(this, VBOXCREDPROV_FIELDID_PASSWORD, NULL);
221 m_pEvents->SetFieldString(this, VBOXCREDPROV_FIELDID_DOMAINNAME, NULL);
222 }
223}
224
225
226int VBoxCredProvCredential::RetrieveCredentials(void)
227{
228 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Checking for credentials ...\n");
229
230 int rc = VbglR3CredentialsQueryAvailability();
231 if (RT_SUCCESS(rc))
232 {
233 /*
234 * Set status to "terminating" to let the host know this module now
235 * tries to receive and use passed credentials so that credentials from
236 * the host won't be sent twice.
237 */
238 VBoxCredProvReportStatus(VBoxGuestFacilityStatus_Terminating);
239
240 rc = VbglR3CredentialsRetrieveUtf16(&m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
241 &m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
242 &m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]);
243
244 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Retrieving credentials returned rc=%Rrc\n", rc);
245 }
246
247 if (RT_SUCCESS(rc))
248 {
249 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Got credentials: User=%ls, Password=%ls, Domain=%ls\n",
250 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
251#ifdef DEBUG
252 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
253#else
254 L"XXX" /* Don't show any passwords in release mode. */,
255#endif
256 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]);
257
258 /*
259 * In case we got a "display name" (e.g. "John Doe")
260 * instead of the real user name (e.g. "jdoe") we have
261 * to translate the data first ...
262 */
263 PWSTR pwszAcount;
264 if (TranslateAccountName(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME], &pwszAcount))
265 {
266 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Translated account name %ls -> %ls\n",
267 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME], pwszAcount);
268
269 RTUtf16Free(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME]);
270 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME] = pwszAcount;
271 }
272 else
273 {
274 /*
275 * Oky, no display name, but maybe it's a
276 * principal name from which we have to extract the
277 * domain from? ([email protected] -> jdoe in
278 * domain my-domain.sub.net.com.)
279 */
280 PWSTR pwszDomain;
281 if (ExtractAccoutData(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
282 &pwszAcount, &pwszDomain))
283 {
284 /* Update user name. */
285 RTUtf16Free(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME]);
286 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME] = pwszAcount;
287
288 /* Update domain. */
289 RTUtf16Free(m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]);
290 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME] = pwszDomain;
291
292 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Extracted account data is pwszAccount=%ls, pwszDomain=%ls\n",
293 pwszAcount, pwszDomain);
294 }
295 }
296 }
297
298 return rc;
299}
300
301
302/*
303 * Initializes one credential with the field information passed in.
304 * Set the value of the VBOXCREDPROV_FIELDID_USERNAME field to pwzUsername.
305 * Optionally takes a password for the SetSerialization case.
306 */
307HRESULT VBoxCredProvCredential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpUS)
308{
309 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Initialize: cpUS=%ld\n", cpUS);
310
311 m_cpUS = cpUS;
312
313 return S_OK;
314}
315
316
317/*
318 * LogonUI calls this in order to give us a callback in case we need to notify it of anything.
319 * Store this callback pointer for later use.
320 */
321HRESULT VBoxCredProvCredential::Advise(ICredentialProviderCredentialEvents* pcpce)
322{
323 VBoxCredProvVerbose(0, "VBoxCredProvCredential: Advise\n");
324
325 if (m_pEvents != NULL)
326 m_pEvents->Release();
327 m_pEvents = pcpce;
328 m_pEvents->AddRef();
329 return S_OK;
330}
331
332
333/* LogonUI calls this to tell us to release the callback. */
334HRESULT VBoxCredProvCredential::UnAdvise(void)
335{
336 VBoxCredProvVerbose(0, "VBoxCredProvCredential: UnAdvise\n");
337
338 /*
339 * We're done with the current iteration, trigger a refresh of ourselves
340 * to reset credentials and to keep the logon UI clean (no stale entries anymore).
341 */
342 Reset();
343
344 /*
345 * Force a re-iteration of the provider (which will give zero credentials
346 * to try out because we just reset our one and only a line above.
347 */
348 if (m_pProvider)
349 m_pProvider->OnCredentialsProvided();
350
351 if (m_pEvents)
352 m_pEvents->Release();
353 m_pEvents = NULL;
354 return S_OK;
355}
356
357
358/*
359 * LogonUI calls this function when our tile is selected (zoomed).
360 * If you simply want fields to show/hide based on the selected state,
361 * there's no need to do anything here - you can set that up in the
362 * field definitions. But if you want to do something
363 * more complicated, like change the contents of a field when the tile is
364 * selected, you would do it here.
365 */
366HRESULT VBoxCredProvCredential::SetSelected(BOOL* pbAutoLogon)
367{
368 VBoxCredProvVerbose(0, "VBoxCredProvCredential: SetSelected\n");
369
370 /*
371 * Don't do auto logon here because it would retry too often with
372 * every credential field (user name, password, domain, ...) which makes
373 * winlogon wait before new login attempts can be made.
374 */
375 *pbAutoLogon = FALSE;
376 return S_OK;
377}
378
379
380/*
381 * Similarly to SetSelected, LogonUI calls this when your tile was selected
382 * and now no longer is. The most common thing to do here (which we do below)
383 * is to clear out the password field.
384 */
385HRESULT VBoxCredProvCredential::SetDeselected()
386{
387 VBoxCredProvVerbose(0, "VBoxCredProvCredential: SetDeselected\n");
388
389 VbglR3CredentialsDestroyUtf16(m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
390 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
391 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME],
392 3 /* Passes */);
393
394 if (m_pEvents)
395 m_pEvents->SetFieldString(this, VBOXCREDPROV_FIELDID_PASSWORD, L"");
396
397 return S_OK;
398}
399
400
401/*
402 * Gets info for a particular field of a tile. Called by logonUI to get information to
403 * display the tile.
404 */
405HRESULT VBoxCredProvCredential::GetFieldState(DWORD dwFieldID,
406 CREDENTIAL_PROVIDER_FIELD_STATE* pFieldState,
407 CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pFieldstateInteractive)
408{
409 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetFieldState: dwFieldID=%ld\n", dwFieldID);
410
411 HRESULT hr = S_OK;
412
413 if ( (dwFieldID < VBOXCREDPROV_NUM_FIELDS)
414 && pFieldState
415 && pFieldstateInteractive)
416 {
417 *pFieldState = s_VBoxCredProvFields[dwFieldID].state;
418 *pFieldstateInteractive = s_VBoxCredProvFields[dwFieldID].stateInteractive;
419 }
420 else
421 hr = E_INVALIDARG;
422
423 return hr;
424}
425
426
427/*
428 * Searches the account name based on a display (real) name (e.g. "John Doe" -> "jdoe").
429 * Result "ppwszAccoutName" needs to be freed with CoTaskMemFree!
430 */
431BOOL VBoxCredProvCredential::TranslateAccountName(PWSTR pwszDisplayName, PWSTR *ppwszAccoutName)
432{
433 AssertPtrReturn(pwszDisplayName, FALSE);
434 VBoxCredProvVerbose(0, "VBoxCredProvCredential: TranslateAccountName: Getting account name for \"%ls\" ...\n",
435 pwszDisplayName);
436
437 /** @todo Do we need ADS support (e.g. TranslateNameW) here? */
438 BOOL fFound = FALSE; /* Did we find the desired user? */
439 NET_API_STATUS nStatus;
440 DWORD dwLevel = 2; /* Detailed information about user accounts. */
441 DWORD dwPrefMaxLen = MAX_PREFERRED_LENGTH;
442 DWORD dwEntriesRead = 0;
443 DWORD dwTotalEntries = 0;
444 DWORD dwResumeHandle = 0;
445 LPUSER_INFO_2 pBuf = NULL;
446 LPUSER_INFO_2 pCurBuf = NULL;
447 do
448 {
449 nStatus = NetUserEnum(NULL, /* Server name, NULL for localhost. */
450 dwLevel,
451 FILTER_NORMAL_ACCOUNT,
452 (LPBYTE*)&pBuf,
453 dwPrefMaxLen,
454 &dwEntriesRead,
455 &dwTotalEntries,
456 &dwResumeHandle);
457 if ( (nStatus == NERR_Success)
458 || (nStatus == ERROR_MORE_DATA))
459 {
460 if ((pCurBuf = pBuf) != NULL)
461 {
462 for (DWORD i = 0; i < dwEntriesRead; i++)
463 {
464 /*
465 * Search for the "display name" - that might be
466 * "John Doe" or something similar the user recognizes easier
467 * and may not the same as the "account" name (e.g. "jdoe").
468 */
469 if ( pCurBuf
470 && pCurBuf->usri2_full_name
471 && StrCmpI(pwszDisplayName, pCurBuf->usri2_full_name) == 0)
472 {
473 /*
474 * Copy the real user name (e.g. "jdoe") to our
475 * output buffer.
476 */
477 LPWSTR pwszTemp;
478 HRESULT hr = SHStrDupW(pCurBuf->usri2_name, &pwszTemp);
479 if (hr == S_OK)
480 {
481 *ppwszAccoutName = pwszTemp;
482 fFound = TRUE;
483 }
484 else
485 VBoxCredProvVerbose(0, "VBoxCredProvCredential: TranslateAccountName: Error copying data, hr=%08x\n", hr);
486 break;
487 }
488 pCurBuf++;
489 }
490 }
491 if (pBuf != NULL)
492 {
493 NetApiBufferFree(pBuf);
494 pBuf = NULL;
495 }
496 }
497 } while (nStatus == ERROR_MORE_DATA && !fFound);
498
499 if (pBuf != NULL)
500 {
501 NetApiBufferFree(pBuf);
502 pBuf = NULL;
503 }
504
505 VBoxCredProvVerbose(0, "VBoxCredProvCredential: TranslateAccountName: Returned nStatus=%ld, fFound=%s\n",
506 nStatus, fFound ? "Yes" : "No");
507 return fFound;
508
509#if 0
510 DWORD dwErr = NO_ERROR;
511 ULONG cbLen = 0;
512 if ( TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, NULL, &cbLen)
513 && cbLen > 0)
514 {
515 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetAccountName: Translated ADS name has %u characters\n", cbLen));
516
517 ppwszAccoutName = (PWSTR)RTMemAlloc(cbLen * sizeof(WCHAR));
518 AssertPtrReturn(pwszName, FALSE);
519 if (TranslateNameW(pwszName, NameUnknown, NameUserPrincipal, ppwszAccoutName, &cbLen))
520 {
521 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetAccountName: Real ADS account name of '%ls' is '%ls'\n",
522 pwszName, ppwszAccoutName));
523 }
524 else
525 {
526 RTMemFree(ppwszAccoutName);
527 dwErr = GetLastError();
528 }
529 }
530 else
531 dwErr = GetLastError();
532 /* The above method for looking up in ADS failed, try another one. */
533 if (dwErr != NO_ERROR)
534 {
535 dwErr = NO_ERROR;
536
537 }
538#endif
539}
540
541
542/*
543 * Extracts the actual account name & domain from a (raw) account data string. This might
544 * be a principal or FQDN string.
545 */
546BOOL VBoxCredProvCredential::ExtractAccoutData(PWSTR pwszAccountData, PWSTR *ppwszAccoutName, PWSTR *ppwszDomain)
547{
548 AssertPtrReturn(pwszAccountData, FALSE);
549 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: Getting account name for \"%ls\" ...\n",
550 pwszAccountData);
551 HRESULT hr = E_FAIL;
552
553 /* Try to figure out whether this is a principal name (user@domain). */
554 LPWSTR pPos = NULL;
555 if ( (pPos = StrChrW(pwszAccountData, L'@')) != NULL
556 && pPos != pwszAccountData)
557 {
558 DWORD cbSize = (pPos - pwszAccountData) * sizeof(WCHAR);
559 LPWSTR pwszName = (LPWSTR)CoTaskMemAlloc(cbSize + sizeof(WCHAR)); /* Space for terminating zero. */
560 LPWSTR pwszDomain = NULL;
561 AssertPtr(pwszName);
562 hr = StringCbCopyN(pwszName, cbSize + sizeof(WCHAR), pwszAccountData, cbSize);
563 if (SUCCEEDED(hr))
564 {
565 *ppwszAccoutName = pwszName;
566 *pPos++; /* Skip @, point to domain name (if any). */
567 if ( pPos != NULL
568 && *pPos != L'\0')
569 {
570 hr = SHStrDupW(pPos, &pwszDomain);
571 if (SUCCEEDED(hr))
572 {
573 *ppwszDomain = pwszDomain;
574 }
575 else
576 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: Error copying domain data, hr=%08x\n", hr);
577 }
578 else
579 {
580 hr = E_FAIL;
581 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: No domain name found!\n");
582 }
583 }
584 else
585 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: Error copying account data, hr=%08x\n", hr);
586
587 if (hr != S_OK)
588 {
589 CoTaskMemFree(pwszName);
590 if (pwszDomain)
591 CoTaskMemFree(pwszDomain);
592 }
593 }
594 else
595 VBoxCredProvVerbose(0, "VBoxCredProvCredential: ExtractAccoutData: No valid principal account name found!\n");
596
597 return (hr == S_OK);
598}
599
600
601/* Sets ppwsz to the string value of the field at the index dwFieldID. */
602HRESULT VBoxCredProvCredential::GetStringValue(DWORD dwFieldID,
603 PWSTR *ppwszString)
604{
605 /* Check to make sure dwFieldID is a legitimate index. */
606 HRESULT hr;
607 if ( dwFieldID < VBOXCREDPROV_NUM_FIELDS
608 && ppwszString)
609 {
610 switch (dwFieldID)
611 {
612 case VBOXCREDPROV_FIELDID_SUBMIT_BUTTON:
613 /* Fill in standard value to make Winlogon happy. */
614 hr = SHStrDupW(L"Submit", ppwszString);
615 break;
616
617 default:
618
619 /*
620 * Make a copy of the string and return that, the caller is responsible for freeing it.
621 * Note that there can be empty fields (like a missing domain name); handle them
622 * by writing an empty string.
623 */
624 if ( m_pwszCredentials[dwFieldID]
625 && RTUtf16Len(m_pwszCredentials[dwFieldID]))
626 {
627 hr = SHStrDupW(m_pwszCredentials[dwFieldID], ppwszString);
628 }
629 else /* Fill in an empty value. */
630 hr = SHStrDupW(L"", ppwszString);
631 break;
632 }
633#ifdef DEBUG
634 if (SUCCEEDED(hr))
635 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetStringValue: dwFieldID=%ld, ppwszString=%ls\n",
636 dwFieldID, *ppwszString);
637#endif
638 }
639 else
640 hr = E_INVALIDARG;
641 return hr;
642}
643
644
645/*
646 * Sets pdwAdjacentTo to the index of the field the submit button should be
647 * adjacent to. We recommend that the submit button is placed next to the last
648 * field which the user is required to enter information in. Optional fields
649 * should be below the submit button.
650 */
651HRESULT VBoxCredProvCredential::GetSubmitButtonValue(DWORD dwFieldID,
652 DWORD* pdwAdjacentTo)
653{
654 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetSubmitButtonValue: dwFieldID=%ld\n", dwFieldID);
655
656 HRESULT hr = S_OK;
657
658 /* Validate parameters. */
659 if ( dwFieldID == VBOXCREDPROV_FIELDID_SUBMIT_BUTTON
660 && pdwAdjacentTo)
661 {
662 /* pdwAdjacentTo is a pointer to the fieldID you want the submit button to appear next to. */
663 *pdwAdjacentTo = VBOXCREDPROV_FIELDID_PASSWORD;
664 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetSubmitButtonValue: dwFieldID=%ld, *pdwAdjacentTo=%ld\n",
665 dwFieldID, *pdwAdjacentTo);
666 }
667 else
668 hr = E_INVALIDARG;
669
670 return hr;
671}
672
673
674/*
675 * Sets the value of a field which can accept a string as a value.
676 * This is called on each keystroke when a user types into an edit field.
677 */
678HRESULT VBoxCredProvCredential::SetStringValue(DWORD dwFieldID,
679 PCWSTR pcwzString)
680{
681 VBoxCredProvVerbose(0, "VBoxCredProvCredential: SetStringValue: dwFieldID=%ld, pcwzString=%ls\n",
682 dwFieldID, pcwzString);
683
684 /*
685 * We don't set any values into fields (e.g. the password, hidden
686 * by dots), instead keep it secret by resetting all credentials.
687 */
688 Reset();
689
690 return S_OK;
691}
692
693
694/*
695 * The following methods are for logonUI to get the values of various UI elements and then communicate
696 * to the credential about what the user did in that field. However, these methods are not implemented
697 * because our tile doesn't contain these types of UI elements.
698 */
699HRESULT VBoxCredProvCredential::GetBitmapValue(DWORD dwFieldID,
700 HBITMAP* phBitmap)
701{
702 NOREF(dwFieldID);
703 NOREF(phBitmap);
704
705 /* We don't do own bitmaps. */
706 return E_NOTIMPL;
707}
708
709
710HRESULT VBoxCredProvCredential::GetCheckboxValue(DWORD dwFieldID,
711 BOOL* pbChecked,
712 PWSTR* ppwszLabel)
713{
714 NOREF(dwFieldID);
715 NOREF(pbChecked);
716 NOREF(ppwszLabel);
717 return E_NOTIMPL;
718}
719
720
721HRESULT VBoxCredProvCredential::GetComboBoxValueCount(DWORD dwFieldID,
722 DWORD* pcItems,
723 DWORD* pdwSelectedItem)
724{
725 NOREF(dwFieldID);
726 NOREF(pcItems);
727 NOREF(pdwSelectedItem);
728 return E_NOTIMPL;
729}
730
731
732HRESULT VBoxCredProvCredential::GetComboBoxValueAt(DWORD dwFieldID,
733 DWORD dwItem,
734 PWSTR* ppwszItem)
735{
736 NOREF(dwFieldID);
737 NOREF(dwItem);
738 NOREF(ppwszItem);
739 return E_NOTIMPL;
740}
741
742
743HRESULT VBoxCredProvCredential::SetCheckboxValue(DWORD dwFieldID,
744 BOOL bChecked)
745{
746 NOREF(dwFieldID);
747 NOREF(bChecked);
748 return E_NOTIMPL;
749}
750
751
752HRESULT VBoxCredProvCredential::SetComboBoxSelectedValue(DWORD dwFieldId,
753 DWORD dwSelectedItem)
754{
755 NOREF(dwFieldId);
756 NOREF(dwSelectedItem);
757 return E_NOTIMPL;
758}
759
760
761HRESULT VBoxCredProvCredential::CommandLinkClicked(DWORD dwFieldID)
762{
763 NOREF(dwFieldID);
764 return E_NOTIMPL;
765}
766
767
768/*
769 * Collect the username and password into a serialized credential for the correct usage scenario
770 * LogonUI then passes these credentials back to the system to log on.
771 */
772HRESULT VBoxCredProvCredential::GetSerialization(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *pcpGetSerializationResponse,
773 CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpCredentialSerialization,
774 PWSTR *ppwszOptionalStatusText,
775 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
776{
777 NOREF(ppwszOptionalStatusText);
778 NOREF(pcpsiOptionalStatusIcon);
779
780 KERB_INTERACTIVE_UNLOCK_LOGON KerberosUnlockLogon;
781 ZeroMemory(&KerberosUnlockLogon, sizeof(KerberosUnlockLogon));
782
783 /* Save a pointer to the interactive logon struct. */
784 KERB_INTERACTIVE_LOGON* pKerberosLogon = &KerberosUnlockLogon.Logon;
785 AssertPtr(pKerberosLogon);
786
787 HRESULT hr;
788
789#ifdef DEBUG
790 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetSerialization: Username=%ls, Password=%ls, Domain=%ls\n",
791 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
792 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
793 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]);
794#endif
795
796 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
797 DWORD cch = ARRAYSIZE(wszComputerName);
798 if (GetComputerNameW(wszComputerName, &cch))
799 {
800 /* Is a domain name missing? Then use the name of the local computer. */
801 if ( m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]
802 && RTUtf16Len(m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME]))
803 {
804 hr = RTUTF16toToUnicode(&pKerberosLogon->LogonDomainName,
805 m_pwszCredentials[VBOXCREDPROV_FIELDID_DOMAINNAME],
806 false /* Just assign, no copy */);
807 }
808 else
809 hr = RTUTF16toToUnicode(&pKerberosLogon->LogonDomainName,
810 wszComputerName,
811 false /* Just assign, no copy */);
812
813 /* Fill in the username and password. */
814 if (SUCCEEDED(hr))
815 {
816 hr = RTUTF16toToUnicode(&pKerberosLogon->UserName,
817 m_pwszCredentials[VBOXCREDPROV_FIELDID_USERNAME],
818 false /* Just assign, no copy */);
819 if (SUCCEEDED(hr))
820 {
821 hr = RTUTF16toToUnicode(&pKerberosLogon->Password,
822 m_pwszCredentials[VBOXCREDPROV_FIELDID_PASSWORD],
823 false /* Just assign, no copy */);
824 if (SUCCEEDED(hr))
825 {
826 /* Set credential type according to current usage scenario. */
827 AssertPtr(pKerberosLogon);
828 switch (m_cpUS)
829 {
830 case CPUS_UNLOCK_WORKSTATION:
831 pKerberosLogon->MessageType = KerbWorkstationUnlockLogon;
832 break;
833
834 case CPUS_LOGON:
835 pKerberosLogon->MessageType = KerbInteractiveLogon;
836 break;
837
838 case CPUS_CREDUI:
839 pKerberosLogon->MessageType = (KERB_LOGON_SUBMIT_TYPE)0; /* No message type required here. */
840 break;
841
842 default:
843 hr = E_FAIL;
844 break;
845 }
846
847 if (SUCCEEDED(hr))
848 {
849 /* Allocate copies of, and package, the strings in a binary blob. */
850 hr = AllocateLogonPackage(KerberosUnlockLogon,
851 &pcpCredentialSerialization->rgbSerialization,
852 &pcpCredentialSerialization->cbSerialization);
853 }
854
855 if (SUCCEEDED(hr))
856 {
857 ULONG ulAuthPackage;
858
859 HANDLE hLsa;
860 NTSTATUS s = LsaConnectUntrusted(&hLsa);
861 if (SUCCEEDED(HRESULT_FROM_NT(s)))
862 {
863 LSA_STRING lsaszKerberosName;
864 size_t cchKerberosName;
865 hr = StringCchLengthA(NEGOSSP_NAME_A, USHORT_MAX, &cchKerberosName);
866 if (SUCCEEDED(hr))
867 {
868 USHORT usLength;
869 hr = SizeTToUShort(cchKerberosName, &usLength);
870 if (SUCCEEDED(hr))
871 {
872 lsaszKerberosName.Buffer = (PCHAR)NEGOSSP_NAME_A;
873 lsaszKerberosName.Length = usLength;
874 lsaszKerberosName.MaximumLength = lsaszKerberosName.Length + 1;
875
876 }
877 }
878
879 if (SUCCEEDED(hr))
880 {
881 s = LsaLookupAuthenticationPackage(hLsa, &lsaszKerberosName,
882 &ulAuthPackage);
883 if (FAILED(HRESULT_FROM_NT(s)))
884 hr = HRESULT_FROM_NT(s);
885 }
886
887 LsaDeregisterLogonProcess(hLsa);
888 }
889
890 if (SUCCEEDED(hr))
891 {
892 pcpCredentialSerialization->ulAuthenticationPackage = ulAuthPackage;
893 pcpCredentialSerialization->clsidCredentialProvider = CLSID_VBoxCredProvider;
894
895 /* We're done -- let the logon UI know. */
896 *pcpGetSerializationResponse = CPGSR_RETURN_CREDENTIAL_FINISHED;
897 }
898 }
899 }
900 }
901 }
902 }
903 else
904 hr = HRESULT_FROM_WIN32(GetLastError());
905
906 VBoxCredProvVerbose(0, "VBoxCredProvCredential: GetSerialization: Returned 0x%08x\n", hr);
907 return hr;
908}
909
910
911/*
912 * ReportResult is completely optional. Its purpose is to allow a credential to customize the string
913 * and the icon displayed in the case of a logon failure. For example, we have chosen to
914 * customize the error shown in the case of bad username/password and in the case of the account
915 * being disabled.
916 */
917HRESULT VBoxCredProvCredential::ReportResult(NTSTATUS ntsStatus,
918 NTSTATUS ntsSubstatus,
919 PWSTR *ppwszOptionalStatusText,
920 CREDENTIAL_PROVIDER_STATUS_ICON *pcpsiOptionalStatusIcon)
921{
922 /*
923 * Since NULL is a valid value for *ppwszOptionalStatusText and *pcpsiOptionalStatusIcon
924 * this function can't fail
925 */
926 return S_OK;
927}
928
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