VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/VBoxCredProv/VBoxCredential.cpp@ 29813

Last change on this file since 29813 was 29200, checked in by vboxsync, 15 years ago

more manual Oracle rebranding in headers

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.5 KB
Line 
1//
2// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
3// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
4// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
5// PARTICULAR PURPOSE.
6//
7// Copyright (c) 2006 Microsoft Corporation. All rights reserved.
8//
9// Modifications (c) 2009-2010 Oracle Corporation
10//
11
12#ifndef WIN32_NO_STATUS
13#include <ntstatus.h>
14#define WIN32_NO_STATUS
15#endif
16
17#include <iprt/string.h>
18
19#include "VBoxCredential.h"
20#include "guid.h"
21
22
23VBoxCredential::VBoxCredential() : m_cRef(1),
24 m_pCredProvCredentialEvents(NULL)
25{
26 Log(("VBoxCredential::VBoxCredential\n"));
27
28 DllAddRef();
29
30 ZeroMemory(m_rgCredProvFieldDescriptors, sizeof(m_rgCredProvFieldDescriptors));
31 ZeroMemory(m_rgFieldStatePairs, sizeof(m_rgFieldStatePairs));
32 ZeroMemory(m_rgFieldStrings, sizeof(m_rgFieldStrings));
33}
34
35
36VBoxCredential::~VBoxCredential()
37{
38 Log(("VBoxCredential::~VBoxCredential\n"));
39
40 Reset();
41
42 for (int i = 0; i < ARRAYSIZE(m_rgFieldStrings); i++)
43 {
44 CoTaskMemFree(m_rgFieldStrings[i]);
45 CoTaskMemFree(m_rgCredProvFieldDescriptors[i].pszLabel);
46 }
47
48 DllRelease();
49}
50
51
52void VBoxCredential::Reset(void)
53{
54 if (m_rgFieldStrings[SFI_PASSWORD])
55 {
56 // CoTaskMemFree (below) deals with NULL, but StringCchLength does not.
57 size_t lenPassword;
58 HRESULT hr = StringCchLengthW(m_rgFieldStrings[SFI_PASSWORD], 128, &(lenPassword));
59 if (SUCCEEDED(hr))
60 {
61 SecureZeroMemory(m_rgFieldStrings[SFI_PASSWORD], lenPassword * sizeof(*m_rgFieldStrings[SFI_PASSWORD]));
62 }
63 else
64 {
65 // TODO: Determine how to handle count error here.
66 }
67 }
68
69 /** @todo securely clear other fields (user name, domain, ...) as well. */
70}
71
72
73int VBoxCredential::Update(const char *pszUser,
74 const char *pszPw,
75 const char *pszDomain)
76{
77 /* Convert credentials to unicode. */
78 PWSTR *ppwszStored;
79
80 ppwszStored = &m_rgFieldStrings[SFI_USERNAME];
81 CoTaskMemFree(*ppwszStored);
82 SHStrDupA(pszUser, ppwszStored);
83
84 ppwszStored = &m_rgFieldStrings[SFI_PASSWORD];
85 CoTaskMemFree(*ppwszStored);
86 SHStrDupA(pszPw, ppwszStored);
87
88 ppwszStored = &m_rgFieldStrings[SFI_DOMAINNAME];
89 CoTaskMemFree(*ppwszStored);
90 SHStrDupA(pszDomain, ppwszStored);
91
92 Log(("VBoxCredential::Update: user=%ls, pw=%ls, domain=%ls\n",
93 m_rgFieldStrings[SFI_USERNAME] ? m_rgFieldStrings[SFI_USERNAME] : L"<empty>",
94 m_rgFieldStrings[SFI_PASSWORD] ? m_rgFieldStrings[SFI_PASSWORD] : L"<empty>",
95 m_rgFieldStrings[SFI_DOMAINNAME] ? m_rgFieldStrings[SFI_DOMAINNAME] : L"<empty>"));
96 return S_OK;
97}
98
99
100// Initializes one credential with the field information passed in.
101// Set the value of the SFI_USERNAME field to pwzUsername.
102// Optionally takes a password for the SetSerialization case.
103HRESULT VBoxCredential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
104 const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* rgcpfd,
105 const FIELD_STATE_PAIR* rgfsp)
106{
107 Log(("VBoxCredential::Initialize: cpus=%ld, rgcpfd=%p, rgfsp=%p\n", cpus, rgcpfd, rgfsp));
108
109 HRESULT hr = S_OK;
110
111 m_cpUS = cpus;
112
113 /* Copy the field descriptors for each field. This is useful if you want to vary the
114 * field descriptors based on what Usage scenario the credential was created for. */
115 for (DWORD i = 0; SUCCEEDED(hr) && i < ARRAYSIZE(m_rgCredProvFieldDescriptors); i++)
116 {
117 m_rgFieldStatePairs[i] = rgfsp[i];
118 hr = FieldDescriptorCopy(rgcpfd[i], &m_rgCredProvFieldDescriptors[i]);
119 }
120
121 /* Fill in the default value to make winlogon happy. */
122 hr = SHStrDupW(L"Submit", &m_rgFieldStrings[SFI_SUBMIT_BUTTON]);
123
124 return S_OK;
125}
126
127
128/* LogonUI calls this in order to give us a callback in case we need to notify it of anything.
129 * Store this callback pointer for later use. */
130HRESULT VBoxCredential::Advise(ICredentialProviderCredentialEvents* pcpce)
131{
132 Log(("VBoxCredential::Advise\n"));
133
134 if (m_pCredProvCredentialEvents != NULL)
135 m_pCredProvCredentialEvents->Release();
136 m_pCredProvCredentialEvents = pcpce;
137 m_pCredProvCredentialEvents->AddRef();
138 return S_OK;
139}
140
141
142/* LogonUI calls this to tell us to release the callback. */
143HRESULT VBoxCredential::UnAdvise()
144{
145 Log(("VBoxCredential::UnAdvise\n"));
146
147 Reset();
148
149 if (m_pCredProvCredentialEvents)
150 m_pCredProvCredentialEvents->Release();
151 m_pCredProvCredentialEvents = NULL;
152 return S_OK;
153}
154
155
156// LogonUI calls this function when our tile is selected (zoomed).
157// If you simply want fields to show/hide based on the selected state,
158// there's no need to do anything here - you can set that up in the
159// field definitions. But if you want to do something
160// more complicated, like change the contents of a field when the tile is
161// selected, you would do it here.
162HRESULT VBoxCredential::SetSelected(BOOL* pbAutoLogon)
163{
164 Log(("VBoxCredential::SetSelected\n"));
165
166 /* Don't do auto logon here because it would retry too often with
167 * every credential field (user name, password, domain, ...) which makes
168 * winlogon wait before new login attempts can be made.
169 */
170 *pbAutoLogon = FALSE;
171 return S_OK;
172}
173
174
175// Similarly to SetSelected, LogonUI calls this when your tile was selected
176// and now no longer is. The most common thing to do here (which we do below)
177// is to clear out the password field.
178HRESULT VBoxCredential::SetDeselected()
179{
180 Log(("VBoxCredential::SetDeselected\n"));
181
182 HRESULT hr = S_OK;
183 if (m_rgFieldStrings[SFI_PASSWORD])
184 {
185 // CoTaskMemFree (below) deals with NULL, but StringCchLength does not.
186 size_t lenPassword;
187 hr = StringCchLengthW(m_rgFieldStrings[SFI_PASSWORD], 128, &(lenPassword));
188 if (SUCCEEDED(hr))
189 {
190 SecureZeroMemory(m_rgFieldStrings[SFI_PASSWORD], lenPassword * sizeof(*m_rgFieldStrings[SFI_PASSWORD]));
191
192 CoTaskMemFree(m_rgFieldStrings[SFI_PASSWORD]);
193 hr = SHStrDupW(L"", &m_rgFieldStrings[SFI_PASSWORD]);
194 }
195
196 if (SUCCEEDED(hr) && m_pCredProvCredentialEvents)
197 {
198 m_pCredProvCredentialEvents->SetFieldString(this, SFI_PASSWORD, m_rgFieldStrings[SFI_PASSWORD]);
199 }
200 }
201
202 return hr;
203}
204
205
206// Gets info for a particular field of a tile. Called by logonUI to get information to
207// display the tile.
208HRESULT VBoxCredential::GetFieldState(DWORD dwFieldID,
209 CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
210 CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis)
211{
212 Log(("VBoxCredential::GetFieldState: dwFieldID=%ld, pcpfs=%p, pcpfis=%p\n", dwFieldID, pcpfs, pcpfis));
213
214 HRESULT hr;
215
216 if ( (dwFieldID < ARRAYSIZE(m_rgFieldStatePairs))
217 && pcpfs
218 && pcpfis)
219 {
220 *pcpfs = m_rgFieldStatePairs[dwFieldID].cpfs;
221 *pcpfis = m_rgFieldStatePairs[dwFieldID].cpfis;
222
223 hr = S_OK;
224 }
225 else
226 {
227 hr = E_INVALIDARG;
228 }
229 return hr;
230}
231
232
233// Sets ppwsz to the string value of the field at the index dwFieldID.
234HRESULT VBoxCredential::GetStringValue(DWORD dwFieldID,
235 PWSTR* ppwsz)
236{
237 Log(("VBoxCredential::GetStringValue: dwFieldID=%ld, pwz=%p\n", dwFieldID, ppwsz));
238
239 HRESULT hr;
240
241 // Check to make sure dwFieldID is a legitimate index.
242 if (dwFieldID < ARRAYSIZE(m_rgCredProvFieldDescriptors) && ppwsz)
243 {
244 // Make a copy of the string and return that. The caller
245 // is responsible for freeing it.
246 hr = SHStrDupW(m_rgFieldStrings[dwFieldID], ppwsz);
247 if (SUCCEEDED(hr))
248 Log(("VBoxCredential::GetStringValue: dwFieldID=%ld, pwz=%ls\n", dwFieldID, *ppwsz));
249 }
250 else
251 {
252 hr = E_INVALIDARG;
253 }
254
255 return hr;
256}
257
258
259// Sets pdwAdjacentTo to the index of the field the submit button should be
260// adjacent to. We recommend that the submit button is placed next to the last
261// field which the user is required to enter information in. Optional fields
262// should be below the submit button.
263HRESULT VBoxCredential::GetSubmitButtonValue(DWORD dwFieldID,
264 DWORD* pdwAdjacentTo)
265{
266 Log(("VBoxCredential::GetSubmitButtonValue: dwFieldID=%ld\n", dwFieldID));
267
268 HRESULT hr;
269
270 // Validate parameters.
271 if ((SFI_SUBMIT_BUTTON == dwFieldID) && pdwAdjacentTo)
272 {
273 // pdwAdjacentTo is a pointer to the fieldID you want the submit button to appear next to.
274 *pdwAdjacentTo = SFI_PASSWORD;
275 Log(("VBoxCredential::GetSubmitButtonValue: dwFieldID=%ld, *pdwAdjacentTo=%ld\n", dwFieldID, *pdwAdjacentTo));
276 hr = S_OK;
277 }
278 else
279 {
280 hr = E_INVALIDARG;
281 }
282 return hr;
283}
284
285
286// Sets the value of a field which can accept a string as a value.
287// This is called on each keystroke when a user types into an edit field.
288HRESULT VBoxCredential::SetStringValue(DWORD dwFieldID,
289 PCWSTR pwz)
290{
291 Log(("VBoxCredential::SetStringValue: dwFieldID=%ld, pwz=%ls\n", dwFieldID, pwz));
292
293 HRESULT hr;
294
295 // Validate parameters.
296 if ( dwFieldID < ARRAYSIZE(m_rgCredProvFieldDescriptors)
297 && (CPFT_EDIT_TEXT == m_rgCredProvFieldDescriptors[dwFieldID].cpft ||
298 CPFT_PASSWORD_TEXT == m_rgCredProvFieldDescriptors[dwFieldID].cpft))
299 {
300 PWSTR* ppwszStored = &m_rgFieldStrings[dwFieldID];
301 CoTaskMemFree(*ppwszStored);
302 hr = SHStrDupW(pwz, ppwszStored);
303 }
304 else
305 {
306 hr = E_INVALIDARG;
307 }
308 return hr;
309}
310
311
312// The following methods are for logonUI to get the values of various UI elements and then communicate
313// to the credential about what the user did in that field. However, these methods are not implemented
314// because our tile doesn't contain these types of UI elements
315
316/* Gets the image to show in the user tile */
317HRESULT VBoxCredential::GetBitmapValue(DWORD dwFieldID,
318 HBITMAP* phbmp)
319{
320 UNREFERENCED_PARAMETER(dwFieldID);
321 UNREFERENCED_PARAMETER(phbmp);
322
323 /* We don't do own bitmaps */
324 return E_NOTIMPL;
325}
326
327
328HRESULT VBoxCredential::GetCheckboxValue(DWORD dwFieldID,
329 BOOL* pbChecked,
330 PWSTR* ppwszLabel)
331{
332 UNREFERENCED_PARAMETER(dwFieldID);
333 UNREFERENCED_PARAMETER(pbChecked);
334 UNREFERENCED_PARAMETER(ppwszLabel);
335 return E_NOTIMPL;
336}
337
338
339HRESULT VBoxCredential::GetComboBoxValueCount(DWORD dwFieldID,
340 DWORD* pcItems,
341 DWORD* pdwSelectedItem)
342{
343 UNREFERENCED_PARAMETER(dwFieldID);
344 UNREFERENCED_PARAMETER(pcItems);
345 UNREFERENCED_PARAMETER(pdwSelectedItem);
346 return E_NOTIMPL;
347}
348
349
350HRESULT VBoxCredential::GetComboBoxValueAt(DWORD dwFieldID,
351 DWORD dwItem,
352 PWSTR* ppwszItem)
353{
354 UNREFERENCED_PARAMETER(dwFieldID);
355 UNREFERENCED_PARAMETER(dwItem);
356 UNREFERENCED_PARAMETER(ppwszItem);
357 return E_NOTIMPL;
358}
359
360
361HRESULT VBoxCredential::SetCheckboxValue(DWORD dwFieldID,
362 BOOL bChecked)
363{
364 UNREFERENCED_PARAMETER(dwFieldID);
365 UNREFERENCED_PARAMETER(bChecked);
366 return E_NOTIMPL;
367}
368
369
370HRESULT VBoxCredential::SetComboBoxSelectedValue(DWORD dwFieldId,
371 DWORD dwSelectedItem)
372{
373 UNREFERENCED_PARAMETER(dwFieldId);
374 UNREFERENCED_PARAMETER(dwSelectedItem);
375 return E_NOTIMPL;
376}
377
378
379HRESULT VBoxCredential::CommandLinkClicked(DWORD dwFieldID)
380{
381 UNREFERENCED_PARAMETER(dwFieldID);
382 return E_NOTIMPL;
383}
384
385
386// Collect the username and password into a serialized credential for the correct usage scenario
387// (logon/unlock is what's demonstrated in this sample). LogonUI then passes these credentials
388// back to the system to log on.
389HRESULT VBoxCredential::GetSerialization(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
390 CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
391 PWSTR* ppwszOptionalStatusText,
392 CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon)
393{
394 Log(("VBoxCredential::GetSerialization: pcpgsr=%p, pcpcs=%p, ppwszOptionalStatusText=%p, pcpsiOptionalStatusIcon=%p\n",
395 pcpgsr, pcpcs, ppwszOptionalStatusText, pcpsiOptionalStatusIcon));
396
397 UNREFERENCED_PARAMETER(ppwszOptionalStatusText);
398 UNREFERENCED_PARAMETER(pcpsiOptionalStatusIcon);
399
400 KERB_INTERACTIVE_LOGON kil;
401 ZeroMemory(&kil, sizeof(kil));
402
403 HRESULT hr;
404
405 WCHAR wsz[MAX_COMPUTERNAME_LENGTH+1];
406 DWORD cch = ARRAYSIZE(wsz);
407 if (GetComputerNameW(wsz, &cch))
408 {
409 /* Is a domain name missing? Then use the name of the local computer. */
410 if (NULL == m_rgFieldStrings [SFI_DOMAINNAME])
411 hr = UnicodeStringInitWithString(wsz, &kil.LogonDomainName);
412 else
413 hr = UnicodeStringInitWithString(
414 m_rgFieldStrings [SFI_DOMAINNAME], &kil.LogonDomainName);
415
416 /* Fill in the username and password. */
417 if (SUCCEEDED(hr))
418 {
419 hr = UnicodeStringInitWithString(m_rgFieldStrings[SFI_USERNAME], &kil.UserName);
420
421 if (SUCCEEDED(hr))
422 {
423 hr = UnicodeStringInitWithString(m_rgFieldStrings[SFI_PASSWORD], &kil.Password);
424
425 if (SUCCEEDED(hr))
426 {
427 // Allocate copies of, and package, the strings in a binary blob
428 kil.MessageType = KerbInteractiveLogon;
429 hr = KerbInteractiveLogonPack(kil, &pcpcs->rgbSerialization, &pcpcs->cbSerialization);
430
431 if (SUCCEEDED(hr))
432 {
433 ULONG ulAuthPackage;
434 hr = RetrieveNegotiateAuthPackage(&ulAuthPackage);
435 if (SUCCEEDED(hr))
436 {
437 pcpcs->ulAuthenticationPackage = ulAuthPackage;
438 pcpcs->clsidCredentialProvider = CLSID_VBoxCredProvider;
439
440 // At this point the credential has created the serialized credential used for logon
441 // By setting this to CPGSR_RETURN_CREDENTIAL_FINISHED we are letting logonUI know
442 // that we have all the information we need and it should attempt to submit the
443 // serialized credential.
444 *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
445 }
446 }
447 }
448 }
449 }
450 }
451 else
452 {
453 DWORD dwErr = GetLastError();
454 hr = HRESULT_FROM_WIN32(dwErr);
455 }
456
457 Log(("VBoxCredential::GetSerialization: Returned 0x%08x\n", hr));
458 return hr;
459}
460
461
462struct REPORT_RESULT_STATUS_INFO
463{
464 NTSTATUS ntsStatus;
465 NTSTATUS ntsSubstatus;
466 PWSTR pwzMessage;
467 CREDENTIAL_PROVIDER_STATUS_ICON cpsi;
468};
469
470static const REPORT_RESULT_STATUS_INFO s_rgLogonStatusInfo[] =
471{
472 { STATUS_LOGON_FAILURE, STATUS_SUCCESS, L"Incorrect password or username.", CPSI_ERROR, },
473 { STATUS_ACCOUNT_RESTRICTION, STATUS_ACCOUNT_DISABLED, L"The account is disabled.", CPSI_WARNING },
474};
475
476
477// ReportResult is completely optional. Its purpose is to allow a credential to customize the string
478// and the icon displayed in the case of a logon failure. For example, we have chosen to
479// customize the error shown in the case of bad username/password and in the case of the account
480// being disabled.
481HRESULT VBoxCredential::ReportResult(NTSTATUS ntsStatus,
482 NTSTATUS ntsSubstatus,
483 PWSTR* ppwszOptionalStatusText,
484 CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon)
485{
486 *ppwszOptionalStatusText = NULL;
487 *pcpsiOptionalStatusIcon = CPSI_NONE;
488
489 DWORD dwStatusInfo = (DWORD)-1;
490
491 // Look for a match on status and substatus.
492 for (DWORD i = 0; i < ARRAYSIZE(s_rgLogonStatusInfo); i++)
493 {
494 if (s_rgLogonStatusInfo[i].ntsStatus == ntsStatus && s_rgLogonStatusInfo[i].ntsSubstatus == ntsSubstatus)
495 {
496 dwStatusInfo = i;
497 break;
498 }
499 }
500
501 if ((DWORD)-1 != dwStatusInfo)
502 {
503 if (SUCCEEDED(SHStrDupW(s_rgLogonStatusInfo[dwStatusInfo].pwzMessage, ppwszOptionalStatusText)))
504 {
505 *pcpsiOptionalStatusIcon = s_rgLogonStatusInfo[dwStatusInfo].cpsi;
506 }
507 }
508
509 /* Try to lookup a text message for error code */
510 LPVOID lpMessageBuffer = NULL;
511 HMODULE hMod = LoadLibrary(L"NTDLL.DLL");
512 if (hMod)
513 {
514 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER
515 | FORMAT_MESSAGE_FROM_SYSTEM
516 | FORMAT_MESSAGE_FROM_HMODULE,
517 hMod,
518 ntsStatus,
519 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
520 (LPTSTR) &lpMessageBuffer,
521 0,
522 NULL);
523 }
524
525 Log(("VBoxCredential::ReportResult: ntsStatus=%ld, ntsSubstatus=%ld, ppwszOptionalStatusText=%p, pcpsiOptionalStatusIcon=%p, dwStatusInfo=%ld, Message=%ls\n",
526 ntsStatus, ntsSubstatus, ppwszOptionalStatusText, pcpsiOptionalStatusIcon, dwStatusInfo, lpMessageBuffer ? lpMessageBuffer : L"none"));
527
528 if (lpMessageBuffer)
529 LocalFree(lpMessageBuffer);
530 FreeLibrary(hMod);
531
532 // Since NULL is a valid value for *ppwszOptionalStatusText and *pcpsiOptionalStatusIcon
533 // this function can't fail.
534 return S_OK;
535}
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