VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxCredProv/VBoxCredProvProvider.cpp@ 103914

Last change on this file since 103914 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.8 KB
Line 
1/* $Id: VBoxCredProvProvider.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxCredProvProvider - The actual credential provider class.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <new> /* For bad_alloc. */
33
34#include <iprt/win/windows.h>
35#include <iprt/win/credentialprovider.h>
36
37#include <iprt/errcore.h>
38#include <VBox/VBoxGuestLib.h>
39
40#include "VBoxCredentialProvider.h"
41#include "VBoxCredProvProvider.h"
42#include "VBoxCredProvCredential.h"
43
44
45
46VBoxCredProvProvider::VBoxCredProvProvider(void)
47 : m_cRefs(1)
48 , m_pCred(NULL)
49 , m_pPoller(NULL)
50 , m_pEvents(NULL)
51 , m_fHandleRemoteSessions(false)
52{
53 VBoxCredentialProviderAcquire();
54
55 VBoxCredProvReportStatus(VBoxGuestFacilityStatus_Init);
56}
57
58
59VBoxCredProvProvider::~VBoxCredProvProvider(void)
60{
61 VBoxCredProvVerbose(0, "VBoxCredProv: Destroying\n");
62
63 if (m_pCred)
64 {
65 m_pCred->Release();
66 m_pCred = NULL;
67 }
68
69 if (m_pPoller)
70 {
71 m_pPoller->Shutdown();
72 delete m_pPoller;
73 m_pPoller = NULL;
74 }
75
76 VBoxCredProvReportStatus(VBoxGuestFacilityStatus_Terminated);
77
78 VBoxCredentialProviderRelease();
79}
80
81
82/* IUnknown overrides. */
83ULONG
84VBoxCredProvProvider::AddRef(void)
85{
86 LONG cRefs = InterlockedIncrement(&m_cRefs);
87 VBoxCredProvVerbose(0, "VBoxCredProv: AddRef: Returning refcount=%ld\n",
88 cRefs);
89 return cRefs;
90}
91
92
93ULONG
94VBoxCredProvProvider::Release(void)
95{
96 LONG cRefs = InterlockedDecrement(&m_cRefs);
97 VBoxCredProvVerbose(0, "VBoxCredProv: Release: Returning refcount=%ld\n",
98 cRefs);
99 if (!cRefs)
100 {
101 VBoxCredProvVerbose(0, "VBoxCredProv: Calling destructor\n");
102 delete this;
103 }
104 return cRefs;
105}
106
107
108HRESULT
109VBoxCredProvProvider::QueryInterface(REFIID interfaceID, void **ppvInterface)
110{
111 HRESULT hr = S_OK;
112 if (ppvInterface)
113 {
114 if ( IID_IUnknown == interfaceID
115 || IID_ICredentialProvider == interfaceID)
116 {
117 *ppvInterface = static_cast<IUnknown*>(this);
118 reinterpret_cast<IUnknown*>(*ppvInterface)->AddRef();
119 }
120 else
121 {
122 *ppvInterface = NULL;
123 hr = E_NOINTERFACE;
124 }
125 }
126 else
127 hr = E_INVALIDARG;
128
129 return hr;
130}
131
132
133/**
134 * Loads the global configuration from registry.
135 *
136 * @return DWORD Windows error code.
137 */
138DWORD
139VBoxCredProvProvider::LoadConfiguration(void)
140{
141 HKEY hKey;
142 /** @todo Add some registry wrapper function(s) as soon as we got more values to retrieve. */
143 DWORD dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Oracle\\VirtualBox Guest Additions\\AutoLogon",
144 0L, KEY_QUERY_VALUE, &hKey);
145 if (dwRet == ERROR_SUCCESS)
146 {
147 DWORD dwValue;
148 DWORD dwType = REG_DWORD;
149 DWORD dwSize = sizeof(DWORD);
150
151 dwRet = RegQueryValueEx(hKey, L"HandleRemoteSessions", NULL, &dwType, (LPBYTE)&dwValue, &dwSize);
152 if ( dwRet == ERROR_SUCCESS
153 && dwType == REG_DWORD
154 && dwSize == sizeof(DWORD))
155 {
156 m_fHandleRemoteSessions = RT_BOOL(dwValue);
157 }
158
159 dwRet = RegQueryValueEx(hKey, L"LoggingEnabled", NULL, &dwType, (LPBYTE)&dwValue, &dwSize);
160 if ( dwRet == ERROR_SUCCESS
161 && dwType == REG_DWORD
162 && dwSize == sizeof(DWORD))
163 {
164 g_dwVerbosity = 1; /* Default logging level. */
165 }
166
167 if (g_dwVerbosity) /* Do we want logging at all? */
168 {
169 dwRet = RegQueryValueEx(hKey, L"LoggingLevel", NULL, &dwType, (LPBYTE)&dwValue, &dwSize);
170 if ( dwRet == ERROR_SUCCESS
171 && dwType == REG_DWORD
172 && dwSize == sizeof(DWORD))
173 {
174 g_dwVerbosity = dwValue;
175 }
176 }
177
178 RegCloseKey(hKey);
179 }
180 /* Do not report back an error here yet. */
181 return ERROR_SUCCESS;
182}
183
184
185/**
186 * Determines whether we should handle the current session or not.
187 *
188 * @return bool true if we should handle this session, false if not.
189 */
190bool
191VBoxCredProvProvider::HandleCurrentSession(void)
192{
193 /* Load global configuration from registry. */
194 int rc = LoadConfiguration();
195 if (RT_FAILURE(rc))
196 VBoxCredProvVerbose(0, "VBoxCredProv: Error loading global configuration, rc=%Rrc\n",
197 rc);
198
199 bool fHandle = false;
200 if (VbglR3AutoLogonIsRemoteSession())
201 {
202 if (m_fHandleRemoteSessions) /* Force remote session handling. */
203 fHandle = true;
204 }
205 else /* No remote session. */
206 fHandle = true;
207
208 VBoxCredProvVerbose(3, "VBoxCredProv: Handling current session=%RTbool\n", fHandle);
209 return fHandle;
210}
211
212
213/**
214 * Tells this provider the current usage scenario.
215 *
216 * @return HRESULT
217 * @param enmUsageScenario Current usage scenario this provider will be
218 * used in.
219 * @param dwFlags Optional flags for the usage scenario.
220 */
221HRESULT
222VBoxCredProvProvider::SetUsageScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO enmUsageScenario, DWORD dwFlags)
223{
224 HRESULT hr = S_OK;
225
226 VBoxCredProvVerbose(0, "VBoxCredProv::SetUsageScenario: enmUsageScenario=%d, dwFlags=%ld\n",
227 enmUsageScenario, dwFlags);
228
229 m_enmUsageScenario = enmUsageScenario;
230
231 switch (m_enmUsageScenario)
232 {
233 case CPUS_LOGON:
234 case CPUS_UNLOCK_WORKSTATION:
235 {
236 VBoxCredProvReportStatus(VBoxGuestFacilityStatus_Active);
237
238 DWORD dwErr = LoadConfiguration();
239 if (dwErr != ERROR_SUCCESS)
240 VBoxCredProvVerbose(0, "VBoxCredProv: Error while loading configuration, error=%ld\n", dwErr);
241 /* Do not stop running on a misconfigured system. */
242
243 /*
244 * If we're told to not handle the current session just bail out and let the
245 * user know.
246 */
247 if (!HandleCurrentSession())
248 break;
249
250 hr = S_OK;
251 if (!m_pPoller)
252 {
253#ifdef RT_EXCEPTIONS_ENABLED
254 try { m_pPoller = new VBoxCredProvPoller(); }
255 catch (std::bad_alloc &) { hr = E_OUTOFMEMORY; }
256#else
257 m_pPoller = new VBoxCredProvPoller();
258 AssertStmt(m_pPoller, hr = E_OUTOFMEMORY);
259#endif
260 if (SUCCEEDED(hr))
261 {
262 int rc = m_pPoller->Initialize(this);
263 if (RT_FAILURE(rc))
264 VBoxCredProvVerbose(0, "VBoxCredProv::SetUsageScenario: Error initializing poller thread, rc=%Rrc\n", rc);
265/** @todo r=bird: Why is the initialize failure ignored here? */
266 }
267 }
268
269 if ( SUCCEEDED(hr)
270 && !m_pCred)
271 {
272#ifdef RT_EXCEPTIONS_ENABLED
273 try { m_pCred = new VBoxCredProvCredential(); }
274 catch (std::bad_alloc &) { hr = E_OUTOFMEMORY; }
275#else
276 m_pCred = new VBoxCredProvCredential();
277 AssertStmt(m_pCred, hr = E_OUTOFMEMORY);
278#endif
279 if (SUCCEEDED(hr))
280 hr = m_pCred->Initialize(m_enmUsageScenario);
281 }
282 else
283 {
284 /* All set up already! Nothing to do here right now. */
285 }
286
287 /* If we failed, do some cleanup. */
288/** @todo r=bird: Why aren't we cleaning up m_pPoller too? Very confusing given
289 * that m_pCred wasn't necessarily even created above. Always explain the WHY
290 * when doing something that isn't logical like here! */
291 if (FAILED(hr))
292 {
293 if (m_pCred != NULL)
294 {
295 m_pCred->Release();
296 m_pCred = NULL;
297 }
298 }
299 break;
300 }
301
302 case CPUS_CHANGE_PASSWORD: /* Asks us to provide a way to change the password. */
303 case CPUS_CREDUI: /* Displays an own UI. We don't need that. */
304 case CPUS_PLAP: /* See Pre-Logon-Access Provider. Not needed (yet). */
305 hr = E_NOTIMPL;
306 break;
307
308 default:
309
310 hr = E_INVALIDARG;
311 break;
312 }
313
314 VBoxCredProvVerbose(0, "VBoxCredProv::SetUsageScenario returned hr=0x%08x\n", hr);
315 return hr;
316}
317
318
319/**
320 * Tells this provider how the serialization will be handled. Currently not used.
321 *
322 * @return STDMETHODIMP
323 * @param pcpCredentialSerialization Credentials serialization.
324 */
325STDMETHODIMP
326VBoxCredProvProvider::SetSerialization(const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpCredentialSerialization)
327{
328 NOREF(pcpCredentialSerialization);
329 return E_NOTIMPL;
330}
331
332
333/**
334 * Initializes the communication with LogonUI through callbacks events which we can later
335 * use to start re-enumeration of credentials.
336 *
337 * @return HRESULT
338 * @param pcpEvents Pointer to event interface.
339 * @param upAdviseContext The current advise context.
340 */
341HRESULT
342VBoxCredProvProvider::Advise(ICredentialProviderEvents *pcpEvents, UINT_PTR upAdviseContext)
343{
344 VBoxCredProvVerbose(0, "VBoxCredProv::Advise, pcpEvents=0x%p, upAdviseContext=%u\n",
345 pcpEvents, upAdviseContext);
346 if (m_pEvents)
347 {
348 m_pEvents->Release();
349 m_pEvents = NULL;
350 }
351
352 m_pEvents = pcpEvents;
353 if (m_pEvents)
354 m_pEvents->AddRef();
355
356 /*
357 * Save advice context for later use when binding to
358 * certain ICredentialProviderEvents events.
359 */
360 m_upAdviseContext = upAdviseContext;
361 return S_OK;
362}
363
364
365/**
366 * Uninitializes the callback events so that they're no longer valid.
367 *
368 * @return HRESULT
369 */
370HRESULT
371VBoxCredProvProvider::UnAdvise(void)
372{
373 VBoxCredProvVerbose(0, "VBoxCredProv::UnAdvise: pEvents=0x%p\n",
374 m_pEvents);
375 if (m_pEvents)
376 {
377 m_pEvents->Release();
378 m_pEvents = NULL;
379 }
380
381 return S_OK;
382}
383
384
385/**
386 * Retrieves the total count of fields we're handling (needed for field enumeration
387 * through LogonUI).
388 *
389 * @return HRESULT
390 * @param pdwCount Receives total count of fields.
391 */
392HRESULT
393VBoxCredProvProvider::GetFieldDescriptorCount(DWORD *pdwCount)
394{
395 if (pdwCount)
396 {
397 *pdwCount = VBOXCREDPROV_NUM_FIELDS;
398 VBoxCredProvVerbose(0, "VBoxCredProv::GetFieldDescriptorCount: %ld\n", *pdwCount);
399 }
400 return S_OK;
401}
402
403
404/**
405 * Retrieves a descriptor of a specified field.
406 *
407 * @return HRESULT
408 * @param dwIndex ID of field to retrieve descriptor for.
409 * @param ppFieldDescriptor Pointer which receives the allocated field
410 * descriptor.
411 */
412HRESULT
413VBoxCredProvProvider::GetFieldDescriptorAt(DWORD dwIndex, CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR **ppFieldDescriptor)
414{
415 HRESULT hr = S_OK;
416 if ( dwIndex < VBOXCREDPROV_NUM_FIELDS
417 && ppFieldDescriptor)
418 {
419 PCREDENTIAL_PROVIDER_FIELD_DESCRIPTOR pcpFieldDesc =
420 (PCREDENTIAL_PROVIDER_FIELD_DESCRIPTOR)CoTaskMemAlloc(sizeof(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR));
421
422 if (pcpFieldDesc)
423 {
424 const VBOXCREDPROV_FIELD &field = s_VBoxCredProvDefaultFields[dwIndex];
425
426 RT_BZERO(pcpFieldDesc, sizeof(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR));
427
428 pcpFieldDesc->dwFieldID = field.desc.dwFieldID;
429 pcpFieldDesc->cpft = field.desc.cpft;
430
431 PCRTUTF16 pcwszField = NULL;
432
433 if (dwIndex != VBOXCREDPROV_FIELDID_PASSWORD) /* Don't ever get any password. Never ever, ever. */
434 {
435 if (m_pCred) /* If we have retrieved credentials, get the actual (current) value. */
436 pcwszField = m_pCred->getField(dwIndex);
437 else /* Otherwise get the default value. */
438 pcwszField = field.desc.pszLabel;
439 }
440
441 hr = SHStrDupW(pcwszField ? pcwszField : L"", &pcpFieldDesc->pszLabel);
442
443 VBoxCredProvVerbose(0, "VBoxCredProv::GetFieldDescriptorAt: dwIndex=%ld, pszLabel=%ls, hr=0x%08x\n",
444 dwIndex,
445#ifdef DEBUG /* Don't show any (sensitive data) in release mode. */
446 pcwszField ? pcwszField : L"",
447#else
448 L"XXX",
449#endif
450 hr);
451
452 pcpFieldDesc->guidFieldType = field.desc.guidFieldType;
453 }
454 else
455 hr = E_OUTOFMEMORY;
456
457 if (SUCCEEDED(hr))
458 {
459 *ppFieldDescriptor = pcpFieldDesc;
460 }
461 else if (pcpFieldDesc)
462 {
463 if (pcpFieldDesc->pszLabel)
464 {
465 CoTaskMemFree(pcpFieldDesc->pszLabel);
466 pcpFieldDesc->pszLabel = NULL;
467 }
468
469 CoTaskMemFree(pcpFieldDesc);
470 }
471 }
472 else
473 hr = E_INVALIDARG;
474
475 VBoxCredProvVerbose(0, "VBoxCredProv::GetFieldDescriptorAt: dwIndex=%ld, ppDesc=0x%p, hr=0x%08x\n",
476 dwIndex, ppFieldDescriptor, hr);
477 return hr;
478}
479
480
481/**
482 * Retrieves the total number of credentials this provider can offer at the current time and
483 * if a logon attempt should be made.
484 *
485 * @return HRESULT
486 * @param pdwCount Receives number of credentials to serve.
487 * @param pdwDefault Receives the credentials index to try
488 * logging on if there is more than one
489 * credential provided. 0 is default.
490 * @param pfAutoLogonWithDefault Receives a flag indicating whether a
491 * logon attempt using the default
492 * credential should be made or not.
493 */
494HRESULT
495VBoxCredProvProvider::GetCredentialCount(DWORD *pdwCount, DWORD *pdwDefault, BOOL *pfAutoLogonWithDefault)
496{
497 AssertPtr(pdwCount);
498 AssertPtr(pdwDefault);
499 AssertPtr(pfAutoLogonWithDefault);
500
501 bool fHasCredentials = false;
502
503 /* Do we have credentials? */
504 if (m_pCred)
505 {
506 int rc = m_pCred->RetrieveCredentials();
507 fHasCredentials = rc == VINF_SUCCESS;
508 }
509
510 if (fHasCredentials)
511 {
512 *pdwCount = 1; /* This provider always has the same number of credentials (1). */
513 *pdwDefault = 0; /* The credential we provide is *always* at index 0! */
514 *pfAutoLogonWithDefault = TRUE; /* We always at least try to auto-login (if password is correct). */
515 }
516 else
517 {
518 *pdwCount = 0;
519 *pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT;
520 *pfAutoLogonWithDefault = FALSE;
521 }
522
523 VBoxCredProvVerbose(0, "VBoxCredProv::GetCredentialCount: *pdwCount=%ld, *pdwDefault=%ld, *pfAutoLogonWithDefault=%s\n",
524 *pdwCount, *pdwDefault, *pfAutoLogonWithDefault ? "true" : "false");
525 return S_OK;
526}
527
528
529/**
530 * Called by Winlogon to retrieve the interface of our current ICredentialProviderCredential interface.
531 *
532 * @return HRESULT
533 * @param dwIndex Index of credential (in case there is more than one credential at a time) to
534 * retrieve the interface for.
535 * @param ppCredProvCredential Pointer that receives the credential interface.
536 */
537HRESULT
538VBoxCredProvProvider::GetCredentialAt(DWORD dwIndex, ICredentialProviderCredential **ppCredProvCredential)
539{
540 VBoxCredProvVerbose(0, "VBoxCredProv::GetCredentialAt: Index=%ld, ppCredProvCredential=0x%p\n",
541 dwIndex, ppCredProvCredential);
542 if (!m_pCred)
543 {
544 VBoxCredProvVerbose(0, "VBoxCredProv::GetCredentialAt: No credentials available\n");
545 return E_INVALIDARG;
546 }
547
548 HRESULT hr;
549 if ( dwIndex == 0
550 && ppCredProvCredential)
551 {
552 hr = m_pCred->QueryInterface(IID_ICredentialProviderCredential,
553 reinterpret_cast<void**>(ppCredProvCredential));
554 }
555 else
556 {
557 VBoxCredProvVerbose(0, "VBoxCredProv::GetCredentialAt: More than one credential not supported!\n");
558 hr = E_INVALIDARG;
559 }
560 return hr;
561}
562
563
564/**
565 * Triggers a credential re-enumeration -- will be called by our poller thread. This then invokes
566 * GetCredentialCount() and GetCredentialAt() called by Winlogon.
567 */
568void
569VBoxCredProvProvider::OnCredentialsProvided(void)
570{
571 VBoxCredProvVerbose(0, "VBoxCredProv::OnCredentialsProvided\n");
572
573 if (m_pEvents)
574 m_pEvents->CredentialsChanged(m_upAdviseContext);
575}
576
577
578/**
579 * Creates our provider. This happens *before* CTRL-ALT-DEL was pressed!
580 */
581HRESULT
582VBoxCredProvProviderCreate(REFIID interfaceID, void **ppvInterface)
583{
584 VBoxCredProvProvider *pProvider;
585#ifdef RT_EXCEPTIONS_ENABLED
586 try { pProvider = new VBoxCredProvProvider(); }
587 catch (std::bad_alloc &) { AssertFailedReturn(E_OUTOFMEMORY); }
588#else
589 pProvider = new VBoxCredProvProvider();
590 AssertReturn(pProvider, E_OUTOFMEMORY);
591#endif
592
593 HRESULT hr = pProvider->QueryInterface(interfaceID, ppvInterface);
594 pProvider->Release();
595
596 return hr;
597}
598
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