/pGina/src/CredentialProvider/Credential.cpp

https://github.com/pyq881120/pgina · C++ · 760 lines · 568 code · 116 blank · 76 comment · 143 complexity · 769ed0f01dd73c83be948fbe503ecd9c MD5 · raw file

  1. /*
  2. Copyright (c) 2013, pGina Team
  3. All rights reserved.
  4. Redistribution and use in source and binary forms, with or without
  5. modification, are permitted provided that the following conditions are met:
  6. * Redistributions of source code must retain the above copyright
  7. notice, this list of conditions and the following disclaimer.
  8. * Redistributions in binary form must reproduce the above copyright
  9. notice, this list of conditions and the following disclaimer in the
  10. documentation and/or other materials provided with the distribution.
  11. * Neither the name of the pGina Team nor the names of its contributors
  12. may be used to endorse or promote products derived from this software without
  13. specific prior written permission.
  14. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  15. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  16. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  17. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
  18. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  19. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  20. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  21. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  23. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include <Windows.h>
  26. #include "Credential.h"
  27. #include "Dll.h"
  28. #pragma warning(push)
  29. #pragma warning(disable : 4995)
  30. #include <shlwapi.h>
  31. #pragma warning(pop)
  32. #include <Macros.h>
  33. #include "ClassFactory.h"
  34. #include "TileUiTypes.h"
  35. #include "TileUiLogon.h"
  36. #include "TileUiUnlock.h"
  37. #include "TileUiChangePassword.h"
  38. #include "SerializationHelpers.h"
  39. #include "ServiceStateHelper.h"
  40. #include "ProviderGuid.h"
  41. #include "resource.h"
  42. #include <wincred.h>
  43. namespace pGina
  44. {
  45. namespace CredProv
  46. {
  47. IFACEMETHODIMP Credential::QueryInterface(__in REFIID riid, __deref_out void **ppv)
  48. {
  49. static const QITAB qitBaseOnly[] =
  50. {
  51. QITABENT(Credential, ICredentialProviderCredential),
  52. {0},
  53. };
  54. static const QITAB qitFull[] =
  55. {
  56. QITABENT(Credential, ICredentialProviderCredential),
  57. QITABENT(Credential, IConnectableCredentialProviderCredential),
  58. {0},
  59. };
  60. if(m_usageScenario == CPUS_CREDUI)
  61. {
  62. return QISearch(this, qitBaseOnly, riid, ppv);
  63. }
  64. else
  65. {
  66. return QISearch(this, qitFull, riid, ppv);
  67. }
  68. }
  69. IFACEMETHODIMP_(ULONG) Credential::AddRef()
  70. {
  71. return InterlockedIncrement(&m_referenceCount);
  72. }
  73. IFACEMETHODIMP_(ULONG) Credential::Release()
  74. {
  75. LONG count = InterlockedDecrement(&m_referenceCount);
  76. if (!count)
  77. delete this;
  78. return count;
  79. }
  80. IFACEMETHODIMP Credential::Advise(__in ICredentialProviderCredentialEvents* pcpce)
  81. {
  82. // Release any ref for current ptr (if any)
  83. UnAdvise();
  84. m_logonUiCallback = pcpce;
  85. if(m_logonUiCallback)
  86. {
  87. m_logonUiCallback->AddRef();
  88. }
  89. return S_OK;
  90. }
  91. IFACEMETHODIMP Credential::UnAdvise()
  92. {
  93. if(m_logonUiCallback)
  94. {
  95. m_logonUiCallback->Release();
  96. m_logonUiCallback = NULL;
  97. }
  98. return S_OK;
  99. }
  100. IFACEMETHODIMP Credential::SetSelected(__out BOOL* pbAutoLogon)
  101. {
  102. // We don't do anything special here, but twould be the place to react to our tile being selected
  103. *pbAutoLogon = FALSE;
  104. return S_OK;
  105. }
  106. IFACEMETHODIMP Credential::SetDeselected()
  107. {
  108. // No longer selected, if we have any password fields set, lets zero/clear/free them
  109. ClearZeroAndFreeAnyPasswordFields(true);
  110. return S_OK;
  111. }
  112. IFACEMETHODIMP Credential::GetFieldState(__in DWORD dwFieldID, __out CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs, __out CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis)
  113. {
  114. if(!m_fields || dwFieldID >= m_fields->fieldCount || !pcpfs || !pcpfis)
  115. return E_INVALIDARG;
  116. *pcpfs = m_fields->fields[dwFieldID].fieldStatePair.fieldState;
  117. *pcpfis = m_fields->fields[dwFieldID].fieldStatePair.fieldInteractiveState;
  118. return S_OK;
  119. }
  120. IFACEMETHODIMP Credential::GetStringValue(__in DWORD dwFieldID, __deref_out PWSTR* ppwsz)
  121. {
  122. if(!m_fields || dwFieldID >= m_fields->fieldCount || !ppwsz)
  123. return E_INVALIDARG;
  124. if(IsFieldDynamic(dwFieldID))
  125. {
  126. std::wstring text = GetTextForField(dwFieldID);
  127. if( ! text.empty() )
  128. return SHStrDupW( text.c_str(), ppwsz );
  129. }
  130. // We copy our value with SHStrDupW which uses CoTask alloc, caller owns result
  131. if(m_fields->fields[dwFieldID].wstr)
  132. return SHStrDupW(m_fields->fields[dwFieldID].wstr, ppwsz);
  133. *ppwsz = NULL;
  134. return S_OK;
  135. }
  136. IFACEMETHODIMP Credential::GetBitmapValue(__in DWORD dwFieldID, __out HBITMAP* phbmp)
  137. {
  138. if(!m_fields || dwFieldID >= m_fields->fieldCount || !phbmp)
  139. return E_INVALIDARG;
  140. if(m_fields->fields[dwFieldID].fieldDescriptor.cpft != CPFT_TILE_IMAGE)
  141. return E_INVALIDARG;
  142. HBITMAP bitmap = NULL;
  143. std::wstring tileImage = pGina::Registry::GetString(L"TileImage", L"");
  144. if(tileImage.empty() || tileImage.length() == 1)
  145. {
  146. // Use builtin
  147. bitmap = LoadBitmap(GetMyInstance(), MAKEINTRESOURCE(IDB_LOGO_MONOCHROME_200));
  148. }
  149. else
  150. {
  151. pDEBUG(L"Credential::GetBitmapValue: Loading image from: %s", tileImage.c_str());
  152. bitmap = (HBITMAP) LoadImageW((HINSTANCE) NULL, tileImage.c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  153. }
  154. if(!bitmap)
  155. return HRESULT_FROM_WIN32(GetLastError());
  156. *phbmp = bitmap;
  157. return S_OK;
  158. }
  159. IFACEMETHODIMP Credential::GetCheckboxValue(__in DWORD dwFieldID, __out BOOL* pbChecked, __deref_out PWSTR* ppwszLabel)
  160. {
  161. return E_NOTIMPL;
  162. }
  163. IFACEMETHODIMP Credential::GetComboBoxValueCount(__in DWORD dwFieldID, __out DWORD* pcItems, __out_range(<,*pcItems) DWORD* pdwSelectedItem)
  164. {
  165. return E_NOTIMPL;
  166. }
  167. IFACEMETHODIMP Credential::GetComboBoxValueAt(__in DWORD dwFieldID, __in DWORD dwItem, __deref_out PWSTR* ppwszItem)
  168. {
  169. return E_NOTIMPL;
  170. }
  171. IFACEMETHODIMP Credential::GetSubmitButtonValue(__in DWORD dwFieldID, __out DWORD* pdwAdjacentTo)
  172. {
  173. if(!m_fields || dwFieldID >= m_fields->fieldCount || !pdwAdjacentTo)
  174. return E_INVALIDARG;
  175. if(m_fields->fields[dwFieldID].fieldDescriptor.cpft != CPFT_SUBMIT_BUTTON)
  176. return E_INVALIDARG;
  177. *pdwAdjacentTo = m_fields->submitAdjacentTo;
  178. return S_OK;
  179. }
  180. IFACEMETHODIMP Credential::SetStringValue(__in DWORD dwFieldID, __in PCWSTR pwz)
  181. {
  182. if(!m_fields || dwFieldID >= m_fields->fieldCount)
  183. return E_INVALIDARG;
  184. if(m_fields->fields[dwFieldID].fieldDescriptor.cpft != CPFT_EDIT_TEXT &&
  185. m_fields->fields[dwFieldID].fieldDescriptor.cpft != CPFT_PASSWORD_TEXT &&
  186. m_fields->fields[dwFieldID].fieldDescriptor.cpft != CPFT_SMALL_TEXT &&
  187. m_fields->fields[dwFieldID].fieldDescriptor.cpft != CPFT_LARGE_TEXT)
  188. return E_INVALIDARG;
  189. if(m_fields->fields[dwFieldID].wstr)
  190. {
  191. CoTaskMemFree(m_fields->fields[dwFieldID].wstr);
  192. m_fields->fields[dwFieldID].wstr = NULL;
  193. }
  194. if(pwz)
  195. {
  196. return SHStrDupW(pwz, &m_fields->fields[dwFieldID].wstr);
  197. }
  198. return S_OK;
  199. }
  200. IFACEMETHODIMP Credential::SetCheckboxValue(__in DWORD dwFieldID, __in BOOL bChecked)
  201. {
  202. return E_NOTIMPL;
  203. }
  204. IFACEMETHODIMP Credential::SetComboBoxSelectedValue(__in DWORD dwFieldID, __in DWORD dwSelectedItem)
  205. {
  206. return E_NOTIMPL;
  207. }
  208. IFACEMETHODIMP Credential::CommandLinkClicked(__in DWORD dwFieldID)
  209. {
  210. return E_NOTIMPL;
  211. }
  212. IFACEMETHODIMP Credential::GetSerialization(__out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr, __out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
  213. __deref_out_opt PWSTR* ppwszOptionalStatusText, __out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon)
  214. {
  215. pDEBUG(L"Credential::GetSerialization, enter");
  216. // If we are operating in a CPUS_LOGON, CPUS_CHANGE_PASSWORD or CPUS_UNLOCK_WORKSTATION scenario, then
  217. // Credential::Connect will have executed prior to this method, which calls
  218. // ProcessLoginAttempt, so m_loginResult should have the result from the plugins.
  219. // Otherwise, we need to execute plugins for the appropriate scenario.
  220. if(m_usageScenario == CPUS_CREDUI)
  221. {
  222. ProcessLoginAttempt(NULL);
  223. }
  224. if( m_logonCancelled )
  225. {
  226. // User clicked cancel during logon
  227. pDEBUG(L"Credential::GetSerialization - Logon was cancelled, returning S_FALSE");
  228. SHStrDupW(L"Logon cancelled", ppwszOptionalStatusText);
  229. *pcpgsr = CPGSR_NO_CREDENTIAL_FINISHED;
  230. *pcpsiOptionalStatusIcon = CPSI_ERROR;
  231. return S_FALSE;
  232. }
  233. if(!m_loginResult.Result())
  234. {
  235. pERROR(L"Credential::GetSerialization: Failed attempt");
  236. if(m_loginResult.Message().length() > 0)
  237. {
  238. SHStrDupW(m_loginResult.Message().c_str(), ppwszOptionalStatusText);
  239. }
  240. else
  241. {
  242. SHStrDupW(L"Plugins did not provide a specific error message", ppwszOptionalStatusText);
  243. }
  244. *pcpgsr = CPGSR_NO_CREDENTIAL_FINISHED;
  245. *pcpsiOptionalStatusIcon = CPSI_ERROR;
  246. return S_FALSE;
  247. }
  248. // If this is the change password scenario, we don't want to continue any
  249. // further. Just notify the user that the change was successful, and return
  250. // false, because we don't want Windows to actually process this change. It was already
  251. // processed by the plugins, so there's nothing more to do.
  252. if( m_loginResult.Result() && CPUS_CHANGE_PASSWORD == m_usageScenario ) {
  253. if(m_loginResult.Message().length() > 0)
  254. {
  255. SHStrDupW(m_loginResult.Message().c_str(), ppwszOptionalStatusText);
  256. }
  257. else
  258. {
  259. SHStrDupW(L"pGina: Your password was successfully changed", ppwszOptionalStatusText);
  260. }
  261. *pcpgsr = CPGSR_NO_CREDENTIAL_FINISHED;
  262. *pcpsiOptionalStatusIcon = CPSI_SUCCESS;
  263. return S_FALSE;
  264. }
  265. // At this point we have a successful logon, and we're not in the
  266. // change password scenario. The successful login info is validated and available
  267. // in m_loginResult. So now we pack it up and provide it back to
  268. // LogonUI/Winlogon as a serialized/packed structure.
  269. pGina::Memory::ObjectCleanupPool cleanup;
  270. PWSTR username = m_loginResult.Username().length() > 0 ? _wcsdup(m_loginResult.Username().c_str()) : NULL;
  271. PWSTR password = m_loginResult.Password().length() > 0 ? _wcsdup(m_loginResult.Password().c_str()) : NULL;
  272. PWSTR domain = m_loginResult.Domain().length() > 0 ? _wcsdup(m_loginResult.Domain().c_str()) : NULL;
  273. cleanup.AddFree(username);
  274. cleanup.AddFree(password);
  275. cleanup.AddFree(domain);
  276. PWSTR protectedPassword = NULL;
  277. HRESULT result = Microsoft::Sample::ProtectIfNecessaryAndCopyPassword(password, m_usageScenario, &protectedPassword);
  278. if(!SUCCEEDED(result))
  279. return result;
  280. cleanup.Add(new pGina::Memory::CoTaskMemFreeCleanup(protectedPassword));
  281. // CredUI we use CredPackAuthenticationBuffer
  282. if(m_usageScenario == CPUS_CREDUI)
  283. {
  284. // Need username/domain as a single string
  285. PWSTR domainUsername = NULL;
  286. result = Microsoft::Sample::DomainUsernameStringAlloc(domain, username, &domainUsername);
  287. if(SUCCEEDED(result))
  288. {
  289. DWORD size = 0;
  290. BYTE* rawbits = NULL;
  291. if(!CredPackAuthenticationBufferW((CREDUIWIN_PACK_32_WOW & m_usageFlags) ? CRED_PACK_WOW_BUFFER : 0, domainUsername, protectedPassword, rawbits, &size))
  292. {
  293. if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  294. {
  295. rawbits = (BYTE *)HeapAlloc(GetProcessHeap(), 0, size);
  296. if(!CredPackAuthenticationBufferW((CREDUIWIN_PACK_32_WOW & m_usageFlags) ? CRED_PACK_WOW_BUFFER : 0, domainUsername, protectedPassword, rawbits, &size))
  297. {
  298. HeapFree(GetProcessHeap(), 0, rawbits);
  299. HeapFree(GetProcessHeap(), 0, domainUsername);
  300. return HRESULT_FROM_WIN32(GetLastError());
  301. }
  302. pcpcs->rgbSerialization = rawbits;
  303. pcpcs->cbSerialization = size;
  304. }
  305. else
  306. {
  307. HeapFree(GetProcessHeap(), 0, domainUsername);
  308. return E_FAIL;
  309. }
  310. }
  311. }
  312. }
  313. else if( CPUS_LOGON == m_usageScenario || CPUS_UNLOCK_WORKSTATION == m_usageScenario )
  314. {
  315. // Init kiul
  316. KERB_INTERACTIVE_UNLOCK_LOGON kiul;
  317. result = Microsoft::Sample::KerbInteractiveUnlockLogonInit(domain, username, password, m_usageScenario, &kiul);
  318. if(!SUCCEEDED(result))
  319. return result;
  320. // Pack for the negotiate package and include our CLSID
  321. result = Microsoft::Sample::KerbInteractiveUnlockLogonPack(kiul, &pcpcs->rgbSerialization, &pcpcs->cbSerialization);
  322. if(!SUCCEEDED(result))
  323. return result;
  324. }
  325. ULONG authPackage = 0;
  326. result = Microsoft::Sample::RetrieveNegotiateAuthPackage(&authPackage);
  327. if(!SUCCEEDED(result))
  328. return result;
  329. pcpcs->ulAuthenticationPackage = authPackage;
  330. pcpcs->clsidCredentialProvider = CLSID_CpGinaProvider;
  331. *pcpgsr = CPGSR_RETURN_CREDENTIAL_FINISHED;
  332. return S_OK;
  333. }
  334. IFACEMETHODIMP Credential::ReportResult(__in NTSTATUS ntsStatus, __in NTSTATUS ntsSubstatus,
  335. __deref_out_opt PWSTR* ppwszOptionalStatusText,
  336. __out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon)
  337. {
  338. pDEBUG(L"Credential::ReportResult(0x%08x, 0x%08x) called", ntsStatus, ntsSubstatus);
  339. /**ppwszOptionalStatusText = NULL;
  340. *pcpsiOptionalStatusIcon = CPSI_NONE;*/
  341. return E_NOTIMPL;
  342. }
  343. Credential::Credential() :
  344. m_referenceCount(1),
  345. m_usageScenario(CPUS_INVALID),
  346. m_logonUiCallback(NULL),
  347. m_fields(NULL),
  348. m_usageFlags(0),
  349. m_logonCancelled(false)
  350. {
  351. AddDllReference();
  352. if( pGina::Registry::GetBool(L"ShowServiceStatusInLogonUi", true) )
  353. pGina::Service::StateHelper::AddTarget(this);
  354. }
  355. Credential::~Credential()
  356. {
  357. pGina::Service::StateHelper::RemoveTarget(this);
  358. ClearZeroAndFreeAnyTextFields(false); // Free memory used to back text fields, no ui update
  359. ReleaseDllReference();
  360. }
  361. void Credential::Initialize(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, UI_FIELDS const& fields, DWORD usageFlags, const wchar_t *username, const wchar_t *password)
  362. {
  363. m_usageScenario = cpus;
  364. m_usageFlags = usageFlags;
  365. // Allocate and copy our UI_FIELDS struct, we need our own copy to set/track the state of
  366. // our fields over time
  367. m_fields = (UI_FIELDS *) (malloc(sizeof(UI_FIELDS) + (sizeof(UI_FIELD) * fields.fieldCount)));
  368. m_fields->fieldCount = fields.fieldCount;
  369. m_fields->submitAdjacentTo = fields.submitAdjacentTo;
  370. m_fields->usernameFieldIdx = fields.usernameFieldIdx;
  371. m_fields->passwordFieldIdx = fields.passwordFieldIdx;
  372. m_fields->statusFieldIdx = fields.statusFieldIdx;
  373. for(DWORD x = 0; x < fields.fieldCount; x++)
  374. {
  375. m_fields->fields[x].fieldDescriptor = fields.fields[x].fieldDescriptor;
  376. m_fields->fields[x].fieldStatePair = fields.fields[x].fieldStatePair;
  377. m_fields->fields[x].fieldDataSource = fields.fields[x].fieldDataSource;
  378. m_fields->fields[x].wstr = NULL;
  379. if(fields.fields[x].wstr && !IsFieldDynamic(x))
  380. {
  381. SHStrDup(fields.fields[x].wstr, &m_fields->fields[x].wstr);
  382. }
  383. if(IsFieldDynamic(x))
  384. {
  385. std::wstring text = GetTextForField(x);
  386. if( ! text.empty() )
  387. {
  388. SHStrDup( text.c_str(), &m_fields->fields[x].wstr );
  389. }
  390. }
  391. }
  392. // Fill the username field (if necessary)
  393. if(username != NULL)
  394. {
  395. SHStrDupW(username, &(m_fields->fields[m_fields->usernameFieldIdx].wstr));
  396. // If the username field has focus, hand focus over to the password field
  397. if(m_fields->fields[m_fields->usernameFieldIdx].fieldStatePair.fieldInteractiveState == CPFIS_FOCUSED) {
  398. m_fields->fields[m_fields->usernameFieldIdx].fieldStatePair.fieldInteractiveState = CPFIS_NONE;
  399. m_fields->fields[m_fields->passwordFieldIdx].fieldStatePair.fieldInteractiveState = CPFIS_FOCUSED;
  400. }
  401. }
  402. else if(m_usageScenario == CPUS_UNLOCK_WORKSTATION)
  403. {
  404. DWORD mySession = pGina::Helpers::GetCurrentSessionId();
  405. std::wstring sessionUname, domain; // Username and domain to be determined
  406. std::wstring usernameFieldValue; // The value for the username field
  407. std::wstring machineName = pGina::Helpers::GetMachineName();
  408. // Get user information from service (if available)
  409. pDEBUG(L"Retrieving user information from service.");
  410. pGina::Transactions::LoginInfo::UserInformation userInfo =
  411. pGina::Transactions::LoginInfo::GetUserInformation(mySession);
  412. pDEBUG(L"Received: original uname: '%s' uname: '%s' domain: '%s'",
  413. userInfo.OriginalUsername().c_str(), userInfo.Username().c_str(), userInfo.Domain().c_str());
  414. // Grab the domain if available
  415. if( ! userInfo.Domain().empty() )
  416. domain = userInfo.Domain();
  417. // Are we configured to use the original username?
  418. if( pGina::Registry::GetBool(L"UseOriginalUsernameInUnlockScenario", false) )
  419. sessionUname = userInfo.OriginalUsername();
  420. else
  421. sessionUname = userInfo.Username();
  422. // If we didn't get a username/domain from the service, try to get it from WTS
  423. if( sessionUname.empty() )
  424. sessionUname = pGina::Helpers::GetSessionUsername(mySession);
  425. if( domain.empty() )
  426. domain = pGina::Helpers::GetSessionDomainName(mySession);
  427. if(!domain.empty() && _wcsicmp(domain.c_str(), machineName.c_str()) != 0)
  428. {
  429. usernameFieldValue += domain;
  430. usernameFieldValue += L"\\";
  431. }
  432. usernameFieldValue += sessionUname;
  433. SHStrDupW(usernameFieldValue.c_str(), &(m_fields->fields[m_fields->usernameFieldIdx].wstr));
  434. } else if( CPUS_CHANGE_PASSWORD == m_usageScenario ) {
  435. DWORD mySession = pGina::Helpers::GetCurrentSessionId();
  436. std::wstring sessionUname = pGina::Helpers::GetSessionUsername(mySession);
  437. SHStrDupW(sessionUname.c_str(), &(m_fields->fields[m_fields->usernameFieldIdx].wstr));
  438. }
  439. if(password != NULL)
  440. {
  441. SHStrDupW(password, &(m_fields->fields[m_fields->passwordFieldIdx].wstr));
  442. }
  443. // Hide MOTD field if not enabled
  444. if( ! pGina::Registry::GetBool(L"EnableMotd", true) )
  445. if( m_usageScenario == CPUS_LOGON )
  446. m_fields->fields[CredProv::LUIFI_MOTD].fieldStatePair.fieldState = CPFS_HIDDEN;
  447. // Hide service status if configured to do so
  448. if( ! pGina::Registry::GetBool(L"ShowServiceStatusInLogonUi", true) )
  449. {
  450. if( m_usageScenario == CPUS_UNLOCK_WORKSTATION )
  451. m_fields->fields[CredProv::LOIFI_STATUS].fieldStatePair.fieldState = CPFS_HIDDEN;
  452. else if( m_usageScenario == CPUS_LOGON )
  453. m_fields->fields[CredProv::LUIFI_STATUS].fieldStatePair.fieldState = CPFS_HIDDEN;
  454. }
  455. }
  456. void Credential::ClearZeroAndFreeAnyPasswordFields(bool updateUi)
  457. {
  458. ClearZeroAndFreeFields(CPFT_PASSWORD_TEXT, updateUi);
  459. }
  460. void Credential::ClearZeroAndFreeAnyTextFields(bool updateUi)
  461. {
  462. ClearZeroAndFreeFields(CPFT_PASSWORD_TEXT, updateUi);
  463. ClearZeroAndFreeFields(CPFT_EDIT_TEXT, updateUi);
  464. }
  465. void Credential::ClearZeroAndFreeFields(CREDENTIAL_PROVIDER_FIELD_TYPE type, bool updateUi)
  466. {
  467. if(!m_fields) return;
  468. for(DWORD x = 0; x < m_fields->fieldCount; x++)
  469. {
  470. if(m_fields->fields[x].fieldDescriptor.cpft == type)
  471. {
  472. if(m_fields->fields[x].wstr)
  473. {
  474. size_t len = wcslen(m_fields->fields[x].wstr);
  475. SecureZeroMemory(m_fields->fields[x].wstr, len * sizeof(wchar_t));
  476. CoTaskMemFree(m_fields->fields[x].wstr);
  477. m_fields->fields[x].wstr = NULL;
  478. // If we've been advised, we can tell the UI so the UI correctly reflects that this
  479. // field is not set any longer (set it to empty string)
  480. if(m_logonUiCallback && updateUi)
  481. {
  482. m_logonUiCallback->SetFieldString(this, m_fields->fields[x].fieldDescriptor.dwFieldID, L"");
  483. }
  484. }
  485. }
  486. }
  487. }
  488. PWSTR Credential::FindUsernameValue()
  489. {
  490. if(!m_fields) return NULL;
  491. return m_fields->fields[m_fields->usernameFieldIdx].wstr;
  492. }
  493. PWSTR Credential::FindPasswordValue()
  494. {
  495. if(!m_fields) return NULL;
  496. return m_fields->fields[m_fields->passwordFieldIdx].wstr;
  497. }
  498. DWORD Credential::FindStatusId()
  499. {
  500. if(!m_fields) return 0;
  501. return m_fields->statusFieldIdx;
  502. }
  503. bool Credential::IsFieldDynamic(DWORD dwFieldID)
  504. {
  505. // Retrieve data for dynamic fields
  506. return (m_fields->fields[dwFieldID].fieldDataSource == SOURCE_DYNAMIC ||
  507. (m_fields->fields[dwFieldID].fieldDataSource == SOURCE_CALLBACK && m_fields->fields[dwFieldID].labelCallback != NULL) ||
  508. m_fields->fields[dwFieldID].fieldDataSource == SOURCE_STATUS);
  509. }
  510. std::wstring Credential::GetTextForField(DWORD dwFieldID)
  511. {
  512. // Retrieve data for dynamic fields
  513. if( m_fields->fields[dwFieldID].fieldDataSource == SOURCE_DYNAMIC )
  514. {
  515. return pGina::Transactions::TileUi::GetDynamicLabel( m_fields->fields[dwFieldID].fieldDescriptor.pszLabel );
  516. }
  517. else if(m_fields->fields[dwFieldID].fieldDataSource == SOURCE_CALLBACK && m_fields->fields[dwFieldID].labelCallback != NULL)
  518. {
  519. return m_fields->fields[dwFieldID].labelCallback(m_fields->fields[dwFieldID].fieldDescriptor.pszLabel, m_fields->fields[dwFieldID].fieldDescriptor.dwFieldID);
  520. }
  521. else if(m_fields->fields[dwFieldID].fieldDataSource == SOURCE_STATUS)
  522. {
  523. return pGina::Service::StateHelper::GetStateText();
  524. }
  525. return L"";
  526. }
  527. void Credential::ServiceStateChanged(bool newState)
  528. {
  529. if(m_logonUiCallback)
  530. {
  531. std::wstring text = pGina::Service::StateHelper::GetStateText();
  532. m_logonUiCallback->SetFieldString(this, FindStatusId(), text.c_str());
  533. }
  534. }
  535. // Called just after the "submit" button is clicked and just before GetSerialization
  536. IFACEMETHODIMP Credential::Connect( IQueryContinueWithStatus *pqcws )
  537. {
  538. pDEBUG(L"Credential::Connect()");
  539. if( CPUS_CREDUI == m_usageScenario || CPUS_LOGON == m_usageScenario ) {
  540. ProcessLoginAttempt(pqcws);
  541. } else if( CPUS_CHANGE_PASSWORD == m_usageScenario ) {
  542. ProcessChangePasswordAttempt();
  543. }
  544. return S_OK;
  545. }
  546. IFACEMETHODIMP Credential::Disconnect()
  547. {
  548. return E_NOTIMPL;
  549. }
  550. void Credential::ProcessLoginAttempt(IQueryContinueWithStatus *pqcws)
  551. {
  552. // Reset m_loginResult
  553. m_loginResult.Clear();
  554. m_logonCancelled = false;
  555. // Workout what our username, and password are. Plugins are responsible for
  556. // parsing out domain\machine name if needed
  557. PWSTR username = FindUsernameValue();
  558. PWSTR password = FindPasswordValue();
  559. PWSTR domain = NULL;
  560. pGina::Protocol::LoginRequestMessage::LoginReason reason = pGina::Protocol::LoginRequestMessage::Login;
  561. switch(m_usageScenario)
  562. {
  563. case CPUS_LOGON:
  564. break;
  565. case CPUS_UNLOCK_WORKSTATION:
  566. reason = pGina::Protocol::LoginRequestMessage::Unlock;
  567. break;
  568. case CPUS_CREDUI:
  569. reason = pGina::Protocol::LoginRequestMessage::CredUI;
  570. break;
  571. }
  572. pDEBUG(L"ProcessLoginAttempt: Processing login for %s", username);
  573. // Set the status message
  574. if( pqcws )
  575. {
  576. std::wstring message = pGina::Registry::GetString(L"LogonProgressMessage", L"Logging on...");
  577. // Replace occurences of %u with the username
  578. std::wstring unameCopy = username;
  579. std::wstring::size_type unameSize = unameCopy.size();
  580. for( std::wstring::size_type pos = 0;
  581. (pos = message.find(L"%u", pos)) != std::wstring::npos;
  582. pos += unameSize )
  583. {
  584. message.replace(pos, unameSize, unameCopy);
  585. }
  586. pqcws->SetStatusMessage(message.c_str());
  587. }
  588. // Execute plugins
  589. m_loginResult = pGina::Transactions::User::ProcessLoginForUser(username, NULL, password, reason);
  590. if( pqcws )
  591. {
  592. if( m_loginResult.Result() )
  593. {
  594. pDEBUG(L"Plugins registered logon success");
  595. pqcws->SetStatusMessage(L"Logon successful");
  596. }
  597. else
  598. {
  599. pDEBUG(L"Plugins registered logon failure");
  600. pqcws->SetStatusMessage(L"Logon failed");
  601. }
  602. // Did the user click the "Cancel" button?
  603. if( pqcws->QueryContinue() != S_OK )
  604. {
  605. pDEBUG(L"User clicked cancel button during plugin processing");
  606. m_logonCancelled = true;
  607. }
  608. }
  609. }
  610. void Credential::ProcessChangePasswordAttempt()
  611. {
  612. pDEBUG(L"ProcessChangePasswordAttempt()");
  613. m_loginResult.Clear();
  614. m_logonCancelled = false;
  615. // Get strings from fields
  616. PWSTR username = FindUsernameValue();
  617. PWSTR oldPassword = FindPasswordValue(); // This is the old password
  618. // Find the new password and confirm new password fields
  619. PWSTR newPassword = NULL;
  620. PWSTR newPasswordConfirm = NULL;
  621. if(m_fields) {
  622. newPassword = m_fields->fields[CredProv::CPUIFI_NEW_PASSWORD].wstr;
  623. newPasswordConfirm = m_fields->fields[CredProv::CPUIFI_CONFIRM_NEW_PASSWORD].wstr;
  624. }
  625. // Check that the new password and confirmation are exactly the same, if not
  626. // return a failure.
  627. if( wcscmp(newPassword, newPasswordConfirm ) != 0 ) {
  628. m_loginResult.Result(false);
  629. m_loginResult.Message(L"New passwords do not match");
  630. return;
  631. }
  632. m_loginResult =
  633. pGina::Transactions::User::ProcessChangePasswordForUser( username, L"", oldPassword, newPassword );
  634. if( m_loginResult.Message().empty() ) {
  635. if( m_loginResult.Result() )
  636. m_loginResult.Message(L"Password was successfully changed");
  637. else
  638. m_loginResult.Message(L"Failed to change password, no message from plugins.");
  639. }
  640. }
  641. }
  642. }