Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1882 lines
54 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1993 - 1999.
  5. //
  6. // File: User.cpp
  7. //
  8. // Contents: implementation of CLogonUser
  9. //
  10. //----------------------------------------------------------------------------
  11. #include "priv.h"
  12. #include "resource.h"
  13. #include "UserOM.h"
  14. #include <lmaccess.h> // for NetUserSetInfo & structures
  15. #include <lmapibuf.h> // for NetApiBufferFree
  16. #include <lmerr.h> // for NERR_Success
  17. #include <ntlsa.h> // for LsaOpenPolicy, etc.
  18. #include <sddl.h> // for ConvertSidToStringSid
  19. #include <tchar.h> // for _TEOF
  20. #include "LogonIPC.h"
  21. #include "ProfileUtil.h"
  22. #include <MSGinaExports.h>
  23. #include <msshrui.h> // for IsFolderPrivateForUser, SetFolderPermissionsForSharing
  24. #include <winsta.h> // for WinStationEnumerate, etc.
  25. #include <ccstock.h>
  26. #include <passrec.h> // PRQueryStatus, dpapi.lib
  27. typedef struct
  28. {
  29. SID sid; // contains 1 subauthority
  30. DWORD dwSubAuth; // 2nd subauthority
  31. } _ALIAS_SID;
  32. #define DECLARE_ALIAS_SID(rid) {{SID_REVISION,2,SECURITY_NT_AUTHORITY,{SECURITY_BUILTIN_DOMAIN_RID}},(rid)}
  33. struct
  34. {
  35. _ALIAS_SID sid;
  36. LPCWSTR szDefaultGroupName;
  37. WCHAR szActualGroupName[GNLEN + sizeof('\0')];
  38. } g_groupname_map [] =
  39. {
  40. // in ascending order of privilege
  41. { DECLARE_ALIAS_SID(DOMAIN_ALIAS_RID_GUESTS), L"Guests", L"" },
  42. { DECLARE_ALIAS_SID(DOMAIN_ALIAS_RID_USERS), L"Users", L"" },
  43. { DECLARE_ALIAS_SID(DOMAIN_ALIAS_RID_POWER_USERS), L"Power Users", L"" },
  44. { DECLARE_ALIAS_SID(DOMAIN_ALIAS_RID_ADMINS), L"Administrators", L"" }
  45. };
  46. void _InitializeGroupNames()
  47. {
  48. int i;
  49. for (i = 0; i < ARRAYSIZE(g_groupname_map); i++)
  50. {
  51. WCHAR szDomain[DNLEN + sizeof('\0')];
  52. DWORD dwNameSize = ARRAYSIZE(g_groupname_map[i].szActualGroupName);
  53. DWORD dwDomainSize = ARRAYSIZE(szDomain);
  54. SID_NAME_USE eUse;
  55. if (L'\0' == g_groupname_map[i].szActualGroupName[0] &&
  56. !LookupAccountSidW(NULL,
  57. &g_groupname_map[i].sid,
  58. g_groupname_map[i].szActualGroupName,
  59. &dwNameSize,
  60. szDomain,
  61. &dwDomainSize,
  62. &eUse))
  63. {
  64. lstrcpynW(g_groupname_map[i].szActualGroupName,
  65. g_groupname_map[i].szDefaultGroupName,
  66. ARRAYSIZE(g_groupname_map[i].szActualGroupName));
  67. }
  68. }
  69. }
  70. //
  71. // IUnknown Interface
  72. //
  73. ULONG CLogonUser::AddRef()
  74. {
  75. _cRef++;
  76. return _cRef;
  77. }
  78. ULONG CLogonUser::Release()
  79. {
  80. ASSERT(_cRef > 0);
  81. _cRef--;
  82. if (_cRef > 0)
  83. {
  84. return _cRef;
  85. }
  86. delete this;
  87. return 0;
  88. }
  89. HRESULT CLogonUser::QueryInterface(REFIID riid, void **ppvObj)
  90. {
  91. static const QITAB qit[] =
  92. {
  93. QITABENT(CLogonUser, IDispatch),
  94. QITABENT(CLogonUser, ILogonUser),
  95. {0},
  96. };
  97. return QISearch(this, qit, riid, ppvObj);
  98. }
  99. //
  100. // IDispatch Interface
  101. //
  102. STDMETHODIMP CLogonUser::GetTypeInfoCount(UINT* pctinfo)
  103. {
  104. return CIDispatchHelper::GetTypeInfoCount(pctinfo);
  105. }
  106. STDMETHODIMP CLogonUser::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
  107. {
  108. return CIDispatchHelper::GetTypeInfo(itinfo, lcid, pptinfo);
  109. }
  110. STDMETHODIMP CLogonUser::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid)
  111. {
  112. return CIDispatchHelper::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
  113. }
  114. STDMETHODIMP CLogonUser::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
  115. {
  116. return CIDispatchHelper::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
  117. }
  118. //
  119. // ILogonUser Interface
  120. //
  121. STDMETHODIMP CLogonUser::get_setting(BSTR bstrName, VARIANT* pvarVal)
  122. {
  123. return _UserSettingAccessor(bstrName, pvarVal, FALSE);
  124. }
  125. STDMETHODIMP CLogonUser::put_setting(BSTR bstrName, VARIANT varVal)
  126. {
  127. return _UserSettingAccessor(bstrName, &varVal, TRUE);
  128. }
  129. STDMETHODIMP CLogonUser::get_isLoggedOn(VARIANT_BOOL* pbLoggedOn)
  130. {
  131. HRESULT hr = S_OK;
  132. CLogonIPC objLogon;
  133. if (NULL == pbLoggedOn)
  134. return E_POINTER;
  135. *pbLoggedOn = VARIANT_FALSE;
  136. if (objLogon.IsLogonServiceAvailable())
  137. {
  138. *pbLoggedOn = ( objLogon.IsUserLoggedOn(_szLoginName, _szDomain) ) ? VARIANT_TRUE : VARIANT_FALSE;
  139. }
  140. else
  141. {
  142. TCHAR szUsername[UNLEN + sizeof('\0')];
  143. DWORD cch = ARRAYSIZE(szUsername);
  144. if (GetUserName(szUsername, &cch) && !StrCmp(szUsername, _szLoginName))
  145. {
  146. *pbLoggedOn = VARIANT_TRUE;
  147. }
  148. else
  149. {
  150. PLOGONID pSessions;
  151. DWORD cSessions;
  152. // Iterate the sessions looking for active and disconnected sessions only.
  153. // Then match the user name and domain (case INsensitive) for a result.
  154. if (WinStationEnumerate(SERVERNAME_CURRENT,
  155. &pSessions,
  156. &cSessions))
  157. {
  158. PLOGONID pSession;
  159. DWORD i;
  160. for (i = 0, pSession = pSessions; i < cSessions; ++i, ++pSession)
  161. {
  162. if ((pSession->State == State_Active) || (pSession->State == State_Disconnected))
  163. {
  164. WINSTATIONINFORMATION winStationInformation;
  165. DWORD cb;
  166. if (WinStationQueryInformation(SERVERNAME_CURRENT,
  167. pSession->SessionId,
  168. WinStationInformation,
  169. &winStationInformation,
  170. sizeof(winStationInformation),
  171. &cb))
  172. {
  173. if ((0 == lstrcmpi(winStationInformation.UserName, _szLoginName)) &&
  174. (0 == lstrcmpi(winStationInformation.Domain, _szDomain)))
  175. {
  176. *pbLoggedOn = VARIANT_TRUE;
  177. break;
  178. }
  179. }
  180. }
  181. }
  182. WinStationFreeMemory(pSessions);
  183. }
  184. else
  185. {
  186. DWORD dwErrorCode;
  187. dwErrorCode = GetLastError();
  188. // We get RPC_S_INVALID_BINDING in safe mode, in which case
  189. // FUS is disabled, so we know the user isn't logged on.
  190. if (dwErrorCode != RPC_S_INVALID_BINDING)
  191. {
  192. hr = HRESULT_FROM_WIN32(dwErrorCode);
  193. }
  194. }
  195. }
  196. }
  197. return hr;
  198. }
  199. STDMETHODIMP CLogonUser::get_passwordRequired(VARIANT_BOOL* pbPasswordRequired)
  200. {
  201. CLogonIPC objLogon;
  202. if (objLogon.IsLogonServiceAvailable())
  203. {
  204. *pbPasswordRequired = objLogon.TestBlankPassword(_szLoginName, _szDomain) ? VARIANT_FALSE: VARIANT_TRUE;
  205. }
  206. else
  207. {
  208. if (NULL == pbPasswordRequired)
  209. return E_POINTER;
  210. if ((BOOL)-1 == _bPasswordRequired)
  211. {
  212. BOOL fResult;
  213. HANDLE hToken;
  214. // Test for a blank password by trying to
  215. // logon the user with a blank password.
  216. fResult = LogonUser(_szLoginName,
  217. NULL,
  218. L"",
  219. LOGON32_LOGON_INTERACTIVE,
  220. LOGON32_PROVIDER_DEFAULT,
  221. &hToken);
  222. if (fResult != FALSE)
  223. {
  224. TBOOL(CloseHandle(hToken));
  225. _bPasswordRequired = FALSE;
  226. }
  227. else
  228. {
  229. switch (GetLastError())
  230. {
  231. case ERROR_ACCOUNT_RESTRICTION:
  232. // This means that blank password logons are disallowed, from
  233. // which we infer that the password is blank.
  234. _bPasswordRequired = FALSE;
  235. break;
  236. case ERROR_LOGON_TYPE_NOT_GRANTED:
  237. // Interactive logon was denied. We only get this if the
  238. // password is blank, otherwise we get ERROR_LOGON_FAILURE.
  239. _bPasswordRequired = FALSE;
  240. break;
  241. case ERROR_LOGON_FAILURE: // normal case (non-blank password)
  242. case ERROR_PASSWORD_MUST_CHANGE: // expired password
  243. _bPasswordRequired = TRUE;
  244. break;
  245. default:
  246. // We'll guess TRUE
  247. _bPasswordRequired = TRUE;
  248. break;
  249. }
  250. }
  251. }
  252. *pbPasswordRequired = (FALSE != _bPasswordRequired) ? VARIANT_TRUE : VARIANT_FALSE;
  253. }
  254. return S_OK;
  255. }
  256. STDMETHODIMP CLogonUser::get_interactiveLogonAllowed(VARIANT_BOOL *pbInteractiveLogonAllowed)
  257. {
  258. HRESULT hr;
  259. CLogonIPC objLogon;
  260. if (objLogon.IsLogonServiceAvailable())
  261. {
  262. *pbInteractiveLogonAllowed = objLogon.TestInteractiveLogonAllowed(_szLoginName, _szDomain) ? VARIANT_TRUE : VARIANT_FALSE;
  263. hr = S_OK;
  264. }
  265. else
  266. {
  267. int iResult;
  268. iResult = ShellIsUserInteractiveLogonAllowed(_szLoginName);
  269. if (iResult == -1)
  270. {
  271. hr = E_ACCESSDENIED;
  272. }
  273. else
  274. {
  275. *pbInteractiveLogonAllowed = (iResult != 0) ? VARIANT_TRUE : VARIANT_FALSE;
  276. hr = S_OK;
  277. }
  278. }
  279. return hr;
  280. }
  281. HRESULT _IsGuestAccessMode(void)
  282. {
  283. HRESULT hr = E_FAIL;
  284. if (IsOS(OS_PERSONAL))
  285. {
  286. hr = S_OK;
  287. }
  288. else if (IsOS(OS_PROFESSIONAL) && !IsOS(OS_DOMAINMEMBER))
  289. {
  290. DWORD dwValue = 0;
  291. DWORD cbValue = sizeof(dwValue);
  292. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
  293. TEXT("SYSTEM\\CurrentControlSet\\Control\\LSA"),
  294. TEXT("ForceGuest"),
  295. NULL,
  296. &dwValue,
  297. &cbValue)
  298. && 1 == dwValue)
  299. {
  300. hr = S_OK;
  301. }
  302. }
  303. return hr;
  304. }
  305. HMODULE g_hmodNTShrUI = NULL;
  306. static PFNISFOLDERPRIVATEFORUSER g_pfnIsFolderPrivateForUser = NULL;
  307. static PFNSETFOLDERPERMISSIONSFORSHARING g_pfnSetFolderPermissionsForSharing = NULL;
  308. void _LoadNTShrUI(void)
  309. {
  310. if (NULL == g_hmodNTShrUI)
  311. {
  312. g_hmodNTShrUI = LoadLibraryW(L"ntshrui.dll");
  313. if (NULL != g_hmodNTShrUI)
  314. {
  315. g_pfnIsFolderPrivateForUser = (PFNISFOLDERPRIVATEFORUSER)GetProcAddress(g_hmodNTShrUI, "IsFolderPrivateForUser");
  316. g_pfnSetFolderPermissionsForSharing = (PFNSETFOLDERPERMISSIONSFORSHARING)GetProcAddress(g_hmodNTShrUI, "SetFolderPermissionsForSharing");
  317. }
  318. }
  319. }
  320. STDMETHODIMP CLogonUser::get_isProfilePrivate(VARIANT_BOOL* pbPrivate)
  321. {
  322. HRESULT hr;
  323. if (NULL == pbPrivate)
  324. return E_POINTER;
  325. *pbPrivate = VARIANT_FALSE;
  326. // Only succeed if we are on Personal, or Professional with ForceGuest=1.
  327. hr = _IsGuestAccessMode();
  328. if (SUCCEEDED(hr))
  329. {
  330. // assume failure here
  331. hr = E_FAIL;
  332. _LookupUserSid();
  333. if (NULL != _pszSID)
  334. {
  335. TCHAR szPath[MAX_PATH];
  336. // Get the profile path
  337. PathCombine(szPath, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"), _pszSID);
  338. DWORD cbData = sizeof(szPath);
  339. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
  340. szPath,
  341. TEXT("ProfileImagePath"),
  342. NULL,
  343. szPath,
  344. &cbData))
  345. {
  346. DWORD dwPrivateType;
  347. _LoadNTShrUI();
  348. if (NULL != g_pfnIsFolderPrivateForUser &&
  349. g_pfnIsFolderPrivateForUser(szPath, _pszSID, &dwPrivateType, NULL))
  350. {
  351. // Note that we return E_FAIL for FAT volumes
  352. if (0 == (dwPrivateType & IFPFU_NOT_NTFS))
  353. {
  354. if (dwPrivateType & IFPFU_PRIVATE)
  355. {
  356. *pbPrivate = VARIANT_TRUE;
  357. }
  358. hr = S_OK;
  359. }
  360. }
  361. }
  362. }
  363. }
  364. return hr;
  365. }
  366. STDMETHODIMP CLogonUser::makeProfilePrivate(VARIANT_BOOL bPrivate)
  367. {
  368. HRESULT hr;
  369. // Only succeed if we are on Personal, or Professional with ForceGuest=1.
  370. hr = _IsGuestAccessMode();
  371. if (SUCCEEDED(hr))
  372. {
  373. // assume failure here
  374. hr = E_FAIL;
  375. _LookupUserSid();
  376. if (NULL != _pszSID)
  377. {
  378. TCHAR szPath[MAX_PATH];
  379. // Get the profile path
  380. PathCombine(szPath, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"), _pszSID);
  381. DWORD cbData = sizeof(szPath);
  382. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
  383. szPath,
  384. TEXT("ProfileImagePath"),
  385. NULL,
  386. szPath,
  387. &cbData))
  388. {
  389. _LoadNTShrUI();
  390. if (NULL != g_pfnSetFolderPermissionsForSharing &&
  391. g_pfnSetFolderPermissionsForSharing(szPath, _pszSID, (VARIANT_TRUE == bPrivate) ? 0 : 1, NULL))
  392. {
  393. hr = S_OK;
  394. }
  395. }
  396. }
  397. }
  398. return hr;
  399. }
  400. STDMETHODIMP CLogonUser::logon(BSTR pbstrPassword, VARIANT_BOOL* pbRet)
  401. {
  402. HRESULT hr;
  403. CLogonIPC objLogon;
  404. TCHAR szPassword[PWLEN + sizeof('\0')];
  405. if (pbstrPassword)
  406. lstrcpyn(szPassword, pbstrPassword, ARRAYSIZE(szPassword));
  407. else
  408. szPassword[0] = 0;
  409. if (!objLogon.IsLogonServiceAvailable())
  410. {
  411. *pbRet = VARIANT_FALSE;
  412. return S_OK;
  413. }
  414. if (objLogon.LogUserOn (_szLoginName, _szDomain, szPassword))
  415. *pbRet = VARIANT_TRUE;
  416. else
  417. *pbRet = VARIANT_FALSE;
  418. if (*pbRet)
  419. {
  420. hr = S_OK;
  421. }
  422. else
  423. {
  424. hr = HRESULT_FROM_WIN32(GetLastError());
  425. }
  426. return hr;
  427. }
  428. STDMETHODIMP CLogonUser::logoff(VARIANT_BOOL* pbRet)
  429. {
  430. HRESULT hr;
  431. CLogonIPC objLogon;
  432. if (objLogon.IsLogonServiceAvailable())
  433. {
  434. *pbRet = ( objLogon.LogUserOff(_szLoginName, _szDomain) ) ? VARIANT_TRUE : VARIANT_FALSE;
  435. }
  436. else
  437. {
  438. *pbRet = ( ExitWindowsEx(EWX_LOGOFF, 0) ) ? VARIANT_TRUE : VARIANT_FALSE;
  439. }
  440. hr = S_OK;
  441. return hr;
  442. }
  443. // Borrowed from msgina
  444. BOOL IsAutologonUser(LPCTSTR szUser, LPCTSTR szDomain)
  445. {
  446. BOOL fIsUser = FALSE;
  447. HKEY hkey = NULL;
  448. TCHAR szAutologonUser[UNLEN + sizeof('\0')];
  449. TCHAR szAutologonDomain[DNLEN + sizeof('\0')];
  450. TCHAR szTempDomainBuffer[DNLEN + sizeof('\0')];
  451. DWORD cbBuffer;
  452. DWORD dwType;
  453. *szTempDomainBuffer = 0;
  454. // Domain may be a empty string. If this is the case...
  455. if (0 == *szDomain)
  456. {
  457. DWORD cchBuffer;
  458. // We really mean the local machine name
  459. // Point to our local buffer
  460. szDomain = szTempDomainBuffer;
  461. cchBuffer = ARRAYSIZE(szTempDomainBuffer);
  462. GetComputerName(szTempDomainBuffer, &cchBuffer);
  463. }
  464. // See if the domain and user name
  465. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  466. TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon"),
  467. 0,
  468. KEY_QUERY_VALUE,
  469. &hkey))
  470. {
  471. // Check the user name
  472. cbBuffer = sizeof (szAutologonUser);
  473. if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("DefaultUserName"), 0, &dwType, (LPBYTE)szAutologonUser, &cbBuffer))
  474. {
  475. // Does it match?
  476. if (0 == lstrcmpi(szAutologonUser, szUser))
  477. {
  478. // Yes. Now check domain
  479. cbBuffer = sizeof(szAutologonDomain);
  480. if (ERROR_SUCCESS == RegQueryValueEx(hkey, TEXT("DefaultDomainName"), 0, &dwType, (LPBYTE)szAutologonDomain, &cbBuffer))
  481. {
  482. // Make sure domain matches
  483. if (0 == lstrcmpi(szAutologonDomain, szDomain))
  484. {
  485. // Success - the users match
  486. fIsUser = TRUE;
  487. }
  488. }
  489. }
  490. }
  491. RegCloseKey(hkey);
  492. }
  493. return fIsUser;
  494. }
  495. // Borrowed from msgina
  496. NTSTATUS SetAutologonPassword(LPCWSTR szPassword)
  497. {
  498. NTSTATUS Status = STATUS_SUCCESS;
  499. OBJECT_ATTRIBUTES ObjectAttributes;
  500. LSA_HANDLE LsaHandle = NULL;
  501. UNICODE_STRING SecretName;
  502. UNICODE_STRING SecretValue;
  503. InitializeObjectAttributes(&ObjectAttributes, NULL, 0L, (HANDLE)NULL, NULL);
  504. Status = LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_CREATE_SECRET, &LsaHandle);
  505. if (!NT_SUCCESS(Status))
  506. return Status;
  507. RtlInitUnicodeString(&SecretName, L"DefaultPassword");
  508. RtlInitUnicodeString(&SecretValue, szPassword);
  509. Status = LsaStorePrivateData(LsaHandle, &SecretName, &SecretValue);
  510. LsaClose(LsaHandle);
  511. return Status;
  512. }
  513. STDMETHODIMP CLogonUser::changePassword(VARIANT varNewPassword, VARIANT varOldPassword, VARIANT_BOOL* pbRet)
  514. {
  515. HRESULT hr;
  516. if (VT_BSTR == varNewPassword.vt && VT_BSTR == varOldPassword.vt)
  517. {
  518. TCHAR szUsername[UNLEN + sizeof('\0')];
  519. DWORD cch = ARRAYSIZE(szUsername);
  520. NET_API_STATUS nasRet;
  521. USER_MODALS_INFO_0 *pumi0 = NULL;
  522. LPWSTR pszNewPassword = varNewPassword.bstrVal ? varNewPassword.bstrVal : L"\0";
  523. // We used to create accounts with UF_PASSWD_NOTREQD, and we still do
  524. // when password policy is enabled. If UF_PASSWD_NOTREQD is set, then
  525. // the below code will succeed even with password policy is enabled,
  526. // so do a minimal policy check here.
  527. nasRet = NetUserModalsGet(NULL, 0, (LPBYTE*)&pumi0);
  528. if (nasRet == NERR_Success && pumi0 != NULL)
  529. {
  530. if ((DWORD)lstrlen(pszNewPassword) < pumi0->usrmod0_min_passwd_len)
  531. {
  532. nasRet = NERR_PasswordTooShort;
  533. }
  534. NetApiBufferFree(pumi0);
  535. }
  536. if (nasRet == NERR_Success)
  537. {
  538. if (GetUserName(szUsername, &cch) && !StrCmp(szUsername, _szLoginName))
  539. {
  540. // This is the case of a user changing their own password.
  541. // Both passwords must be provided to effect the change.
  542. LPCWSTR pszOldPassword = varOldPassword.bstrVal ? varOldPassword.bstrVal : L"\0";
  543. nasRet = NetUserChangePassword(NULL, // Local machine
  544. _szLoginName, // name of the person to change
  545. pszOldPassword, // old password
  546. pszNewPassword); // new password
  547. }
  548. else
  549. {
  550. // This is the case of an admin changing someone else's password.
  551. // As an administrator they don't need to enter the old password.
  552. USER_INFO_1003 usri1003 = { pszNewPassword };
  553. nasRet = NetUserSetInfo(NULL, // local machine
  554. _szLoginName, // name of the person to change
  555. 1003, // structure level
  556. (LPBYTE)&usri1003, // the update info
  557. NULL); // don't care
  558. }
  559. if (nasRet == NERR_Success)
  560. {
  561. // If this is the default user for autologon, delete the cleartext
  562. // password from the registry and save the new password.
  563. if (IsAutologonUser(_szLoginName, _szDomain))
  564. {
  565. SHDeleteValue(HKEY_LOCAL_MACHINE,
  566. TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon"),
  567. TEXT("DefaultPassword"));
  568. SetAutologonPassword(pszNewPassword);
  569. }
  570. // Make an attempt to remove UF_PASSWD_NOTREQD if it's
  571. // currently set. Ignore errors since we already changed
  572. // the password above.
  573. USER_INFO_1008 *pusri1008;
  574. if (NERR_Success == NetUserGetInfo(NULL, _szLoginName, 1008, (LPBYTE*)&pusri1008))
  575. {
  576. if (pusri1008->usri1008_flags & UF_PASSWD_NOTREQD)
  577. {
  578. pusri1008->usri1008_flags &= ~UF_PASSWD_NOTREQD;
  579. NetUserSetInfo(NULL, _szLoginName, 1008, (LPBYTE)pusri1008, NULL);
  580. }
  581. NetApiBufferFree(pusri1008);
  582. }
  583. }
  584. }
  585. hr = HRESULT_FROM_WIN32(nasRet);
  586. if (SUCCEEDED(hr))
  587. {
  588. _bPasswordRequired = !(L'\0' == *pszNewPassword);
  589. }
  590. }
  591. else
  592. {
  593. hr = E_INVALIDARG;
  594. }
  595. *pbRet = ( SUCCEEDED(hr) ) ? VARIANT_TRUE : VARIANT_FALSE;
  596. return hr;
  597. }
  598. STDAPI CLogonUser_Create(REFIID riid, void** ppvObj)
  599. {
  600. return CLogonUser::Create(TEXT(""), TEXT(""), TEXT(""), riid, ppvObj);
  601. }
  602. HRESULT CLogonUser::Create(LPCTSTR pszLoginName, LPCTSTR pszFullName, LPCTSTR pszDomain, REFIID riid, LPVOID* ppv)
  603. {
  604. HRESULT hr = E_OUTOFMEMORY;
  605. CLogonUser* pUser = new CLogonUser(pszLoginName, pszFullName, pszDomain);
  606. if (pUser)
  607. {
  608. hr = pUser->QueryInterface(riid, ppv);
  609. pUser->Release();
  610. }
  611. return hr;
  612. }
  613. CLogonUser::CLogonUser(LPCTSTR pszLoginName,
  614. LPCTSTR pszFullName,
  615. LPCTSTR pszDomain)
  616. : _cRef(1), CIDispatchHelper(&IID_ILogonUser, &LIBID_SHGINALib),
  617. _strDisplayName(NULL), _strPictureSource(NULL), _strDescription(NULL),
  618. _strHint(NULL), _iPrivilegeLevel(-1), _pszSID(NULL),
  619. _bPasswordRequired((BOOL)-1)
  620. {
  621. _InitializeGroupNames();
  622. lstrcpyn(_szLoginName, pszLoginName, ARRAYSIZE(_szLoginName));
  623. lstrcpyn(_szDomain, pszDomain, ARRAYSIZE(_szDomain));
  624. if (pszFullName)
  625. _strDisplayName = SysAllocString(pszFullName);
  626. // Use the EOF marker to indicate an uninitialized string
  627. _szPicture[0] = _TEOF;
  628. DllAddRef();
  629. }
  630. CLogonUser::~CLogonUser()
  631. {
  632. SysFreeString(_strDisplayName);
  633. SysFreeString(_strPictureSource);
  634. SysFreeString(_strDescription);
  635. SysFreeString(_strHint);
  636. if (_pszSID) LocalFree(_pszSID);
  637. ASSERT(_cRef == 0);
  638. DllRelease();
  639. }
  640. typedef HRESULT (CLogonUser::*PFNPUT)(VARIANT);
  641. typedef HRESULT (CLogonUser::*PFNGET)(VARIANT *);
  642. struct SETTINGMAP
  643. {
  644. LPCWSTR szSetting;
  645. PFNGET pfnGet;
  646. PFNPUT pfnPut;
  647. };
  648. #define MAP_SETTING(x) { L#x, CLogonUser::_Get##x, CLogonUser::_Put##x }
  649. #define MAP_SETTING_GET_ONLY(x) { L#x, CLogonUser::_Get##x, NULL }
  650. #define MAP_SETTING_PUT_ONLY(x) { L#x, NULL, CLogonUser::_Put##x }
  651. // _UserSettingAccessor
  652. //
  653. // bstrName - name of the setting you widh to access
  654. // pvarVal - the value of the named setting
  655. // bPut - if true the named setting will be updated
  656. // with the value pointed to by pvarVal
  657. // if false the named setting will be retrieved
  658. // in pvarVal
  659. //
  660. HRESULT CLogonUser::_UserSettingAccessor(BSTR bstrName, VARIANT *pvarVal, BOOL bPut)
  661. {
  662. static const SETTINGMAP setting_map[] =
  663. {
  664. // in descending order of expected access frequecy
  665. MAP_SETTING(LoginName),
  666. MAP_SETTING(DisplayName),
  667. MAP_SETTING(Picture),
  668. MAP_SETTING_GET_ONLY(PictureSource),
  669. MAP_SETTING(AccountType),
  670. MAP_SETTING(Hint),
  671. MAP_SETTING_GET_ONLY(Domain),
  672. MAP_SETTING(Description),
  673. MAP_SETTING_GET_ONLY(SID),
  674. MAP_SETTING_GET_ONLY(UnreadMail)
  675. };
  676. HRESULT hr;
  677. INT i;
  678. // start off assuming bogus setting name
  679. hr = E_INVALIDARG;
  680. for ( i = 0; i < ARRAYSIZE(setting_map); i++)
  681. {
  682. if ( StrCmpW(bstrName, setting_map[i].szSetting) == 0 )
  683. {
  684. // what do we want to do with the named setting ...
  685. if ( bPut )
  686. {
  687. // ... change its value
  688. PFNPUT pfnPut = setting_map[i].pfnPut;
  689. if ( pfnPut != NULL )
  690. {
  691. hr = (this->*pfnPut)(*pvarVal);
  692. }
  693. else
  694. {
  695. // we don't support updated the value for this setting
  696. hr = E_FAIL;
  697. }
  698. }
  699. else
  700. {
  701. // ... retrieve its value
  702. PFNGET pfnGet = setting_map[i].pfnGet;
  703. if ( pfnGet != NULL )
  704. {
  705. hr = (this->*pfnGet)(pvarVal);
  706. }
  707. else
  708. {
  709. // we don't support retieving the value for this setting
  710. hr = E_FAIL;
  711. }
  712. }
  713. break;
  714. }
  715. }
  716. return hr;
  717. }
  718. HRESULT CLogonUser::_GetDisplayName(VARIANT* pvar)
  719. {
  720. if (NULL == pvar)
  721. return E_POINTER;
  722. if (NULL == _strDisplayName)
  723. {
  724. PUSER_INFO_1011 pusri1011 = NULL;
  725. NET_API_STATUS nasRet;
  726. nasRet = NetUserGetInfo(NULL, // local machine
  727. _szLoginName, // whose information do we want
  728. 1011, // structure level
  729. (LPBYTE*)&pusri1011); // pointer to the structure we'll receive
  730. if ( nasRet == NERR_Success )
  731. {
  732. _strDisplayName = SysAllocString(pusri1011->usri1011_full_name);
  733. NetApiBufferFree(pusri1011);
  734. }
  735. }
  736. pvar->vt = VT_BSTR;
  737. pvar->bstrVal = SysAllocString(_strDisplayName);
  738. return S_OK;
  739. }
  740. HRESULT CLogonUser::_PutDisplayName(VARIANT var)
  741. {
  742. HRESULT hr;
  743. if ( var.vt == VT_BSTR )
  744. {
  745. USER_INFO_1011 usri1011;
  746. NET_API_STATUS nasRet;
  747. if ( var.bstrVal )
  748. {
  749. usri1011.usri1011_full_name = var.bstrVal;
  750. }
  751. else
  752. {
  753. // OK to have emply string as display name
  754. usri1011.usri1011_full_name = L"\0";
  755. }
  756. nasRet = NetUserSetInfo(NULL, // local machine
  757. _szLoginName, // name of the person to change
  758. 1011, // structure level
  759. (LPBYTE)&usri1011, // the update info
  760. NULL); // don't care
  761. if ( nasRet == NERR_Success )
  762. {
  763. // DisplayName was successfully changed. Remember to update our
  764. // local copy
  765. SysFreeString(_strDisplayName);
  766. _strDisplayName = SysAllocString(usri1011.usri1011_full_name);
  767. // Notify everyone that a user name has changed
  768. SHChangeDWORDAsIDList dwidl;
  769. dwidl.cb = SIZEOF(dwidl) - SIZEOF(dwidl.cbZero);
  770. dwidl.dwItem1 = SHCNEE_USERINFOCHANGED;
  771. dwidl.dwItem2 = 0;
  772. dwidl.cbZero = 0;
  773. SHChangeNotify(SHCNE_EXTENDED_EVENT, SHCNF_FLUSH | SHCNF_FLUSHNOWAIT, (LPCITEMIDLIST)&dwidl, NULL);
  774. hr = S_OK;
  775. }
  776. else
  777. {
  778. // insufficient privileges?
  779. hr = HRESULT_FROM_WIN32(nasRet);
  780. }
  781. }
  782. else
  783. {
  784. hr = E_INVALIDARG;
  785. }
  786. return hr;
  787. }
  788. HRESULT CLogonUser::_GetLoginName(VARIANT* pvar)
  789. {
  790. if (NULL == pvar)
  791. return E_POINTER;
  792. pvar->vt = VT_BSTR;
  793. pvar->bstrVal = SysAllocString(_szLoginName);
  794. return S_OK;
  795. }
  796. HRESULT CLogonUser::_PutLoginName(VARIANT var)
  797. {
  798. HRESULT hr;
  799. if ( (var.vt == VT_BSTR) && (var.bstrVal) && (*var.bstrVal) )
  800. {
  801. if (_szLoginName[0] == TEXT('\0'))
  802. {
  803. // We haven't been initialized yet. Initialize to the given name.
  804. lstrcpyn(_szLoginName, var.bstrVal, ARRAYSIZE(_szLoginName));
  805. hr = S_OK;
  806. }
  807. else
  808. {
  809. USER_INFO_0 usri0;
  810. NET_API_STATUS nasRet;
  811. usri0.usri0_name = var.bstrVal;
  812. nasRet = NetUserSetInfo(NULL, // local machine
  813. _szLoginName, // name of the person to change
  814. 0, // structure level
  815. (LPBYTE)&usri0, // the update info
  816. NULL); // don't care
  817. if ( nasRet == NERR_Success )
  818. {
  819. // We should also rename the user's picture file to match
  820. // their new LoginName
  821. if (_TEOF == _szPicture[0])
  822. {
  823. // This requires _szLoginName to still have the old name,
  824. // so do it before updating _szLoginName below.
  825. _InitPicture();
  826. }
  827. if (TEXT('\0') != _szPicture[0])
  828. {
  829. TCHAR szNewPicture[ARRAYSIZE(_szPicture)];
  830. LPTSTR szFileName;
  831. LPTSTR szFileExt;
  832. szFileName = PathFindFileName(&_szPicture[7]);
  833. szFileExt = PathFindExtension(szFileName);
  834. lstrcpyn(szNewPicture, _szPicture, (int)((szFileName - _szPicture) + 1));
  835. lstrcatn(szNewPicture, usri0.usri0_name, ARRAYSIZE(szNewPicture));
  836. lstrcatn(szNewPicture, szFileExt, ARRAYSIZE(szNewPicture));
  837. if ( MoveFileEx(&_szPicture[7], &szNewPicture[7], MOVEFILE_REPLACE_EXISTING) )
  838. {
  839. lstrcpyn(_szPicture, szNewPicture, ARRAYSIZE(_szPicture));
  840. }
  841. else
  842. {
  843. // Give up and just try to delete the old picture
  844. // (otherwise it will be abandoned).
  845. DeleteFile(&_szPicture[7]);
  846. _szPicture[0] = _TEOF;
  847. }
  848. }
  849. // LoginName was successfully changed. Remember to update our
  850. // local copy
  851. lstrcpyn(_szLoginName, usri0.usri0_name, ARRAYSIZE(_szLoginName));
  852. hr = S_OK;
  853. }
  854. else
  855. {
  856. // insufficient privileges?
  857. hr = HRESULT_FROM_WIN32(nasRet);
  858. }
  859. }
  860. }
  861. else
  862. {
  863. hr = E_INVALIDARG;
  864. }
  865. return hr;
  866. }
  867. HRESULT CLogonUser::_GetDomain(VARIANT* pvar)
  868. {
  869. if (NULL == pvar)
  870. return E_POINTER;
  871. pvar->vt = VT_BSTR;
  872. pvar->bstrVal = SysAllocString(_szDomain);
  873. return S_OK;
  874. }
  875. HRESULT CLogonUser::_GetPicture(VARIANT* pvar)
  876. {
  877. if (NULL == pvar)
  878. return E_POINTER;
  879. if (_TEOF == _szPicture[0])
  880. {
  881. _InitPicture();
  882. }
  883. pvar->vt = VT_BSTR;
  884. pvar->bstrVal = SysAllocString(_szPicture);
  885. return S_OK;
  886. }
  887. HRESULT CLogonUser::_PutPicture(VARIANT var)
  888. {
  889. HRESULT hr;
  890. if ( (var.vt == VT_BSTR) && (var.bstrVal) && (*var.bstrVal) )
  891. {
  892. // Passed a string which is not NULL and not empty
  893. TCHAR szNewPicturePath[MAX_PATH];
  894. DWORD dwSize = ARRAYSIZE(szNewPicturePath);
  895. // get the path of the image we want to copy
  896. if ( PathIsURL(var.bstrVal) )
  897. {
  898. PathCreateFromUrl(var.bstrVal, szNewPicturePath, &dwSize, NULL);
  899. }
  900. else
  901. {
  902. lstrcpyn(szNewPicturePath, var.bstrVal, ARRAYSIZE(szNewPicturePath));
  903. }
  904. // REVIEW (phellar) : we build the URL string ourself so we know it's of the form,
  905. // file://<path>, the path starts on the 7th character.
  906. if ( _TEOF == _szPicture[0] || StrCmpI(szNewPicturePath, &_szPicture[7]) != 0 )
  907. {
  908. hr = _SetPicture(szNewPicturePath);
  909. }
  910. else
  911. {
  912. // Src and Dest paths are the same
  913. // nothing to do
  914. hr = S_OK;
  915. }
  916. }
  917. else
  918. {
  919. hr = E_INVALIDARG;
  920. }
  921. return hr;
  922. }
  923. HRESULT CLogonUser::_GetPictureSource(VARIANT* pvar)
  924. {
  925. if (NULL == pvar)
  926. return E_POINTER;
  927. if (NULL == _strPictureSource)
  928. {
  929. TCHAR szHintKey[MAX_PATH];
  930. DWORD dwType = REG_SZ;
  931. DWORD dwSize = 0;
  932. PathCombine(szHintKey, c_szRegRoot, _szLoginName);
  933. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
  934. szHintKey,
  935. c_szPictureSrcVal,
  936. &dwType,
  937. NULL,
  938. &dwSize)
  939. && REG_SZ == dwType && dwSize > 0)
  940. {
  941. _strPictureSource = SysAllocStringLen(NULL, dwSize/sizeof(TCHAR));
  942. if (NULL != _strPictureSource)
  943. {
  944. if (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE,
  945. szHintKey,
  946. c_szPictureSrcVal,
  947. NULL,
  948. (LPVOID)_strPictureSource,
  949. &dwSize))
  950. {
  951. SysFreeString(_strPictureSource);
  952. _strPictureSource = NULL;
  953. }
  954. }
  955. }
  956. }
  957. pvar->vt = VT_BSTR;
  958. pvar->bstrVal = SysAllocString(_strPictureSource);
  959. return S_OK;
  960. }
  961. HRESULT CLogonUser::_GetDescription(VARIANT* pvar)
  962. {
  963. if (NULL == pvar)
  964. return E_POINTER;
  965. if (NULL == _strDescription)
  966. {
  967. NET_API_STATUS nasRet;
  968. USER_INFO_1007 *pusri1007;
  969. nasRet = NetUserGetInfo(NULL, // local machine
  970. _szLoginName, // whose information do we want
  971. 1007, // structure level
  972. (LPBYTE*)&pusri1007); // pointer to the structure we'll receive
  973. if ( nasRet == NERR_Success )
  974. {
  975. _strDescription = SysAllocString(pusri1007->usri1007_comment);
  976. NetApiBufferFree(pusri1007);
  977. }
  978. }
  979. pvar->vt = VT_BSTR;
  980. pvar->bstrVal = SysAllocString(_strDescription);
  981. return S_OK;
  982. }
  983. HRESULT CLogonUser::_PutDescription(VARIANT var)
  984. {
  985. HRESULT hr;
  986. if ( var.vt == VT_BSTR )
  987. {
  988. USER_INFO_1007 usri1007;
  989. NET_API_STATUS nasRet;
  990. if ( var.bstrVal )
  991. {
  992. usri1007.usri1007_comment = var.bstrVal;
  993. }
  994. else
  995. {
  996. // OK to have emply string as a description
  997. usri1007.usri1007_comment = L"\0";
  998. }
  999. nasRet = NetUserSetInfo(NULL, // local machine
  1000. _szLoginName, // name of the person to change
  1001. 1007, // structure level
  1002. (LPBYTE)&usri1007, // the update info
  1003. NULL); // don't care
  1004. if ( nasRet == NERR_Success )
  1005. {
  1006. // Description was successfully changed. Remember to update our
  1007. // local copy
  1008. SysFreeString(_strDescription);
  1009. _strDescription = SysAllocString(usri1007.usri1007_comment);
  1010. hr = S_OK;
  1011. }
  1012. else
  1013. {
  1014. // insufficient privileges?
  1015. hr = HRESULT_FROM_WIN32(nasRet);
  1016. }
  1017. }
  1018. else
  1019. {
  1020. hr = E_INVALIDARG;
  1021. }
  1022. return hr;
  1023. }
  1024. HRESULT CLogonUser::_GetHint(VARIANT* pvar)
  1025. {
  1026. if (NULL == pvar)
  1027. return E_POINTER;
  1028. if (NULL == _strHint)
  1029. {
  1030. TCHAR szHintKey[MAX_PATH];
  1031. DWORD dwType = REG_SZ;
  1032. DWORD dwSize = 0;
  1033. PathCombine(szHintKey, c_szRegRoot, _szLoginName);
  1034. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE,
  1035. szHintKey,
  1036. NULL,
  1037. &dwType,
  1038. NULL,
  1039. &dwSize)
  1040. && REG_SZ == dwType && dwSize > 0 && dwSize < 512)
  1041. {
  1042. _strHint = SysAllocStringLen(NULL, dwSize/sizeof(TCHAR));
  1043. if (NULL != _strHint)
  1044. {
  1045. if (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE,
  1046. szHintKey,
  1047. NULL,
  1048. NULL,
  1049. (LPVOID)_strHint,
  1050. &dwSize))
  1051. {
  1052. SysFreeString(_strHint);
  1053. _strHint = NULL;
  1054. }
  1055. }
  1056. }
  1057. }
  1058. pvar->vt = VT_BSTR;
  1059. pvar->bstrVal = SysAllocString(_strHint);
  1060. return S_OK;
  1061. }
  1062. HRESULT CLogonUser::_PutHint(VARIANT var)
  1063. {
  1064. HRESULT hr;
  1065. if ( var.vt == VT_BSTR )
  1066. {
  1067. DWORD dwErr;
  1068. TCHAR *pszHint;
  1069. HKEY hkUserHint;
  1070. if (var.bstrVal)
  1071. {
  1072. pszHint = var.bstrVal;
  1073. }
  1074. else
  1075. {
  1076. pszHint = TEXT("\0");
  1077. }
  1078. dwErr = _OpenUserHintKey(KEY_SET_VALUE, &hkUserHint);
  1079. if ( dwErr == ERROR_SUCCESS )
  1080. {
  1081. DWORD cbData = lstrlen(pszHint) * sizeof(TCHAR) + sizeof(TEXT('\0'));
  1082. dwErr = RegSetValueEx(hkUserHint,
  1083. NULL,
  1084. 0,
  1085. REG_SZ,
  1086. (LPBYTE)pszHint,
  1087. cbData);
  1088. RegCloseKey(hkUserHint);
  1089. }
  1090. if ( dwErr == ERROR_SUCCESS )
  1091. {
  1092. // Hint was successfully changed. Remember to update our local copy
  1093. SysFreeString(_strHint);
  1094. _strHint = SysAllocString(pszHint);
  1095. hr = S_OK;
  1096. }
  1097. else
  1098. {
  1099. hr = HRESULT_FROM_WIN32(dwErr);
  1100. }
  1101. }
  1102. else
  1103. {
  1104. hr = E_INVALIDARG;
  1105. }
  1106. return hr;
  1107. }
  1108. HRESULT CLogonUser::_GetAccountType(VARIANT* pvar)
  1109. {
  1110. HRESULT hr;
  1111. hr = E_FAIL;
  1112. if (pvar)
  1113. {
  1114. if (-1 == _iPrivilegeLevel)
  1115. {
  1116. NET_API_STATUS nasRet;
  1117. PLOCALGROUP_INFO_0 plgi0;
  1118. DWORD dwEntriesRead;
  1119. DWORD dwEntriesTotal;
  1120. nasRet = NetUserGetLocalGroups(
  1121. NULL,
  1122. _szLoginName,
  1123. 0,
  1124. 0,
  1125. (LPBYTE *)&plgi0,
  1126. MAX_PREFERRED_LENGTH,
  1127. &dwEntriesRead,
  1128. &dwEntriesTotal);
  1129. if ( nasRet == NERR_Success )
  1130. {
  1131. // make sure we read all the groups
  1132. ASSERT(dwEntriesRead == dwEntriesTotal)
  1133. INT i, j, iMostPrivileged;
  1134. for (i = 0, iMostPrivileged = 0; i < (INT)dwEntriesRead; i++)
  1135. {
  1136. for (j = ARRAYSIZE(g_groupname_map)-1; j > 0; j--)
  1137. {
  1138. if ( lstrcmpiW(plgi0[i].lgrpi0_name, g_groupname_map[j].szActualGroupName) == 0 )
  1139. {
  1140. break;
  1141. }
  1142. }
  1143. iMostPrivileged = (iMostPrivileged > j) ? iMostPrivileged : j;
  1144. }
  1145. _iPrivilegeLevel = iMostPrivileged;
  1146. nasRet = NetApiBufferFree((LPVOID)plgi0);
  1147. }
  1148. hr = HRESULT_FROM_WIN32(nasRet);
  1149. }
  1150. if (-1 != _iPrivilegeLevel)
  1151. {
  1152. pvar->vt = VT_I4;
  1153. pvar->lVal = _iPrivilegeLevel;
  1154. hr = S_OK;
  1155. }
  1156. }
  1157. else
  1158. {
  1159. hr = E_INVALIDARG;
  1160. }
  1161. return hr;
  1162. }
  1163. HRESULT CLogonUser::_PutAccountType(VARIANT var)
  1164. {
  1165. HRESULT hr;
  1166. hr = VariantChangeType(&var, &var, 0, VT_I4);
  1167. if (SUCCEEDED(hr))
  1168. {
  1169. if (var.lVal < 0 || var.lVal >= ARRAYSIZE(g_groupname_map))
  1170. {
  1171. hr = E_INVALIDARG;
  1172. }
  1173. else if (var.lVal != _iPrivilegeLevel)
  1174. {
  1175. NET_API_STATUS nasRet;
  1176. TCHAR szDomainAndName[256];
  1177. LOCALGROUP_MEMBERS_INFO_3 lgrmi3;
  1178. // First add the user to their new group
  1179. wnsprintf(szDomainAndName,
  1180. ARRAYSIZE(szDomainAndName),
  1181. TEXT("%s\\%s"),
  1182. _szDomain,
  1183. _szLoginName);
  1184. lgrmi3.lgrmi3_domainandname = szDomainAndName;
  1185. nasRet = NetLocalGroupAddMembers(
  1186. NULL,
  1187. g_groupname_map[var.lVal].szActualGroupName,
  1188. 3,
  1189. (LPBYTE)&lgrmi3,
  1190. 1);
  1191. // If we were successful in adding to the group or
  1192. // they were already in the group ...
  1193. if ( nasRet == NERR_Success || nasRet == ERROR_MEMBER_IN_ALIAS )
  1194. {
  1195. // remember the new privilege level
  1196. _iPrivilegeLevel = var.lVal;
  1197. // remove them from all more-privileged groups
  1198. for (int i = var.lVal+1; i < ARRAYSIZE(g_groupname_map); i++)
  1199. {
  1200. // "Power Users" doesn't exist on Personal, so this will
  1201. // fail sometimes.
  1202. NetLocalGroupDelMembers(
  1203. NULL,
  1204. g_groupname_map[i].szActualGroupName,
  1205. 3,
  1206. (LPBYTE)&lgrmi3,
  1207. 1);
  1208. }
  1209. }
  1210. else
  1211. {
  1212. hr = HRESULT_FROM_WIN32(nasRet);
  1213. }
  1214. }
  1215. }
  1216. return hr;
  1217. }
  1218. HRESULT CLogonUser::_LookupUserSid()
  1219. {
  1220. HRESULT hr;
  1221. if (NULL == _pszSID)
  1222. {
  1223. BYTE rgSidBuffer[sizeof(SID) + (SID_MAX_SUB_AUTHORITIES-1)*sizeof(ULONG)];
  1224. PSID pSid = (PSID)rgSidBuffer;
  1225. DWORD cbSid = sizeof(rgSidBuffer);
  1226. TCHAR szDomainName[MAX_PATH];
  1227. DWORD cbDomainName = ARRAYSIZE(szDomainName);
  1228. SID_NAME_USE snu;
  1229. if (LookupAccountName(
  1230. (TEXT('\0') != _szDomain[0]) ? _szDomain : NULL,
  1231. _szLoginName,
  1232. pSid,
  1233. &cbSid,
  1234. szDomainName,
  1235. &cbDomainName,
  1236. &snu))
  1237. {
  1238. ConvertSidToStringSid(pSid, &_pszSID);
  1239. }
  1240. }
  1241. if (NULL == _pszSID)
  1242. {
  1243. DWORD dwErr = GetLastError();
  1244. hr = HRESULT_FROM_WIN32(dwErr);
  1245. }
  1246. else
  1247. {
  1248. hr = S_OK;
  1249. }
  1250. return hr;
  1251. }
  1252. HRESULT CLogonUser::_GetSID(VARIANT* pvar)
  1253. {
  1254. HRESULT hr;
  1255. if (pvar)
  1256. {
  1257. hr = _LookupUserSid();
  1258. if (NULL != _pszSID)
  1259. {
  1260. pvar->vt = VT_BSTR;
  1261. pvar->bstrVal = SysAllocString(_pszSID);
  1262. hr = pvar->bstrVal ? S_OK : E_OUTOFMEMORY;
  1263. }
  1264. }
  1265. else
  1266. {
  1267. hr = E_INVALIDARG;
  1268. }
  1269. return hr;
  1270. }
  1271. DWORD CLogonUser::_GetExpiryDays (HKEY hKeyCurrentUser)
  1272. {
  1273. DWORD dwDays;
  1274. DWORD dwDataType;
  1275. DWORD dwData;
  1276. DWORD dwDataSize;
  1277. HKEY hKey;
  1278. static const TCHAR s_szBaseKeyName[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail");
  1279. static const TCHAR s_szMessageExpiryValueName[] = TEXT("MessageExpiryDays");
  1280. dwDays = 3;
  1281. if (RegOpenKeyEx(hKeyCurrentUser,
  1282. s_szBaseKeyName,
  1283. 0,
  1284. KEY_QUERY_VALUE,
  1285. &hKey) == ERROR_SUCCESS)
  1286. {
  1287. dwDataSize = sizeof(dwData);
  1288. if ((RegQueryValueEx(hKey,
  1289. s_szMessageExpiryValueName,
  1290. NULL,
  1291. &dwDataType,
  1292. reinterpret_cast<LPBYTE>(&dwData),
  1293. &dwDataSize) == ERROR_SUCCESS) &&
  1294. (dwDataType == REG_DWORD) &&
  1295. (dwData <= 30))
  1296. {
  1297. dwDays = dwData;
  1298. }
  1299. TBOOL(RegCloseKey(hKey));
  1300. }
  1301. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1302. s_szBaseKeyName,
  1303. 0,
  1304. KEY_QUERY_VALUE,
  1305. &hKey))
  1306. {
  1307. dwDataSize = sizeof(dwData);
  1308. if ((RegQueryValueEx(hKey,
  1309. s_szMessageExpiryValueName,
  1310. NULL,
  1311. &dwDataType,
  1312. reinterpret_cast<LPBYTE>(&dwData),
  1313. &dwDataSize) == ERROR_SUCCESS) &&
  1314. (dwDataType == REG_DWORD) &&
  1315. (dwData <= 30))
  1316. {
  1317. dwDays = dwData;
  1318. }
  1319. TBOOL(RegCloseKey(hKey));
  1320. }
  1321. return(dwDays);
  1322. }
  1323. STDMETHODIMP CLogonUser::getMailAccountInfo(UINT uiAccountIndex, VARIANT *pvarAccountName, UINT *pcUnreadMessages)
  1324. {
  1325. HRESULT hr;
  1326. DWORD dwComputerNameSize;
  1327. TCHAR szComputerName[CNLEN + sizeof('\0')];
  1328. hr = E_FAIL;
  1329. // Only do this for local computer accounts.
  1330. dwComputerNameSize = ARRAYSIZE(szComputerName);
  1331. if ((GetComputerName(szComputerName, &dwComputerNameSize) != FALSE) &&
  1332. (lstrcmpi(szComputerName, _szDomain) == 0))
  1333. {
  1334. CUserProfile profile(_szLoginName, _szDomain);
  1335. if (static_cast<HKEY>(profile) != NULL)
  1336. {
  1337. DWORD dwCount;
  1338. TCHAR szMailAccountName[100];
  1339. hr = SHEnumerateUnreadMailAccounts(profile, uiAccountIndex, szMailAccountName, ARRAYSIZE(szMailAccountName));
  1340. if (SUCCEEDED(hr))
  1341. {
  1342. if (pvarAccountName)
  1343. {
  1344. pvarAccountName->vt = VT_BSTR;
  1345. pvarAccountName->bstrVal = SysAllocString(szMailAccountName);
  1346. hr = pvarAccountName->bstrVal ? S_OK : E_OUTOFMEMORY;
  1347. }
  1348. if (SUCCEEDED(hr) && pcUnreadMessages)
  1349. {
  1350. FILETIME ft, ftCurrent;
  1351. SYSTEMTIME st;
  1352. BOOL ftExpired = false;
  1353. DWORD dwExpiryDays = _GetExpiryDays(profile);
  1354. hr = SHGetUnreadMailCount(profile, szMailAccountName, &dwCount, &ft, NULL, 0);
  1355. IncrementFILETIME(&ft, FT_ONEDAY * dwExpiryDays);
  1356. GetLocalTime(&st);
  1357. SystemTimeToFileTime(&st, &ftCurrent);
  1358. ftExpired = ((CompareFileTime(&ft, &ftCurrent) < 0) || (dwExpiryDays == 0));
  1359. if (SUCCEEDED(hr) && !ftExpired)
  1360. {
  1361. *pcUnreadMessages = dwCount;
  1362. }
  1363. else
  1364. {
  1365. *pcUnreadMessages = 0;
  1366. }
  1367. }
  1368. }
  1369. }
  1370. }
  1371. return hr;
  1372. }
  1373. HRESULT CLogonUser::_GetUnreadMail(VARIANT* pvar)
  1374. {
  1375. HRESULT hr;
  1376. if (pvar)
  1377. {
  1378. DWORD dwComputerNameSize;
  1379. TCHAR szComputerName[CNLEN + sizeof('\0')];
  1380. hr = E_FAIL;
  1381. // Only do this for local computer accounts.
  1382. dwComputerNameSize = ARRAYSIZE(szComputerName);
  1383. if ((GetComputerName(szComputerName, &dwComputerNameSize) != FALSE) &&
  1384. (lstrcmpi(szComputerName, _szDomain) == 0))
  1385. {
  1386. CUserProfile profile(_szLoginName, _szDomain);
  1387. if (static_cast<HKEY>(profile) != NULL)
  1388. {
  1389. DWORD dwCount;
  1390. FILETIME ftFilter;
  1391. SYSTEMTIME st;
  1392. DWORD dwExpiryDays = _GetExpiryDays(profile);
  1393. GetLocalTime(&st);
  1394. SystemTimeToFileTime(&st, &ftFilter);
  1395. DecrementFILETIME(&ftFilter, FT_ONEDAY * dwExpiryDays);
  1396. hr = SHGetUnreadMailCount(profile, NULL, &dwCount, &ftFilter, NULL, 0);
  1397. if (SUCCEEDED(hr) && (dwExpiryDays != 0))
  1398. {
  1399. pvar->vt = VT_UI4;
  1400. pvar->uintVal = dwCount;
  1401. }
  1402. }
  1403. }
  1404. }
  1405. else
  1406. {
  1407. hr = E_INVALIDARG;
  1408. }
  1409. return hr;
  1410. }
  1411. HRESULT CLogonUser::_InitPicture()
  1412. {
  1413. HRESULT hr;
  1414. lstrcpyn(_szPicture, TEXT("file://"), ARRAYSIZE(_szPicture));
  1415. hr = SHGetUserPicturePath(_szLoginName, SHGUPP_FLAG_CREATE, &_szPicture[7]);
  1416. if (FAILED(hr))
  1417. {
  1418. _szPicture[0] = TEXT('\0');
  1419. }
  1420. return hr;
  1421. }
  1422. HRESULT CLogonUser::_SetPicture(LPCTSTR pszNewPicturePath)
  1423. {
  1424. // use shell32!SHSetUserPicturePath to set the user's
  1425. // picture path. If this is successful then update the
  1426. // _szPicture member variable.
  1427. HRESULT hr = SHSetUserPicturePath(_szLoginName, 0, pszNewPicturePath);
  1428. if ( S_OK == hr )
  1429. {
  1430. DWORD dwErr;
  1431. HKEY hkUserHint;
  1432. SysFreeString(_strPictureSource);
  1433. _strPictureSource = SysAllocString(pszNewPicturePath);
  1434. dwErr = _OpenUserHintKey(KEY_SET_VALUE, &hkUserHint);
  1435. if ( dwErr == ERROR_SUCCESS )
  1436. {
  1437. if (pszNewPicturePath)
  1438. {
  1439. DWORD cbData = lstrlen(pszNewPicturePath) * sizeof(TCHAR) + sizeof(TEXT('\0'));
  1440. dwErr = RegSetValueEx(hkUserHint,
  1441. c_szPictureSrcVal,
  1442. 0,
  1443. REG_SZ,
  1444. (LPBYTE)pszNewPicturePath,
  1445. cbData);
  1446. }
  1447. else
  1448. {
  1449. dwErr = RegDeleteValue(hkUserHint, c_szPictureSrcVal);
  1450. }
  1451. RegCloseKey(hkUserHint);
  1452. }
  1453. lstrcpyn(_szPicture, TEXT("file://"), ARRAYSIZE(_szPicture));
  1454. hr = SHGetUserPicturePath(_szLoginName, 0, &_szPicture[7]);
  1455. if ( FAILED(hr) )
  1456. {
  1457. lstrcatn(_szPicture, pszNewPicturePath, ARRAYSIZE(_szPicture));
  1458. hr = S_OK;
  1459. }
  1460. }
  1461. return hr;
  1462. }
  1463. DWORD CLogonUser::_OpenUserHintKey(REGSAM sam, HKEY *phkey)
  1464. {
  1465. DWORD dwErr;
  1466. TCHAR szHintKey[MAX_PATH];
  1467. // We have to store hint information under HKLM so the logon page can
  1468. // access it, Also, we want to allow non-admins to change their own
  1469. // hints, but non-admins can't write values under HKLM by default.
  1470. //
  1471. // The solution is to use subkeys rather than named values so we can
  1472. // tweak the ACLs on a per-user basis.
  1473. //
  1474. // A non-admin user needs the ability to do 2 things:
  1475. // 1. Create a hint subkey for themselves if one does not exist.
  1476. // 2. Modify the hint contained in their subkey if one already exists.
  1477. //
  1478. // At install time, we set the ACL on the Hints key to allow
  1479. // Authenticated Users KEY_CREATE_SUB_KEY access. Thus, a user is
  1480. // able to create a hint for themselves if one doesn't exist.
  1481. //
  1482. // Immediately after creating a hint subkey, whether it was created
  1483. // by the target user or an admin, we grant the target user
  1484. // KEY_SET_VALUE access to the subkey. This ensures that a user can
  1485. // modify their own hint no matter who created it for them.
  1486. //
  1487. // Note that we don't call RegCreateKeyEx or SHSetValue since we
  1488. // don't want the key to be automatically created here.
  1489. //
  1490. // Note that admins are able to create and modify hints for any user,
  1491. // but a non-admin is only able to create or modify their own hint.
  1492. // First assume the hint already exists and just try to open it.
  1493. PathCombine(szHintKey, c_szRegRoot, _szLoginName);
  1494. dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1495. szHintKey,
  1496. 0,
  1497. sam,
  1498. phkey);
  1499. if ( dwErr == ERROR_FILE_NOT_FOUND )
  1500. {
  1501. HKEY hkHints;
  1502. // The hint subkey doesn't exist yet for this user.
  1503. // Try to create one.
  1504. // Open the Hints key for KEY_CREATE_SUB_KEY
  1505. dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1506. c_szRegRoot,
  1507. 0,
  1508. KEY_CREATE_SUB_KEY,
  1509. &hkHints);
  1510. if ( dwErr == ERROR_SUCCESS )
  1511. {
  1512. // Create a subkey for this user
  1513. dwErr = RegCreateKeyEx(hkHints,
  1514. _szLoginName,
  1515. 0,
  1516. NULL,
  1517. 0,
  1518. sam,
  1519. NULL,
  1520. phkey,
  1521. NULL);
  1522. if ( dwErr == ERROR_SUCCESS )
  1523. {
  1524. // Grant KEY_SET_VALUE access to the user so they can
  1525. // change their own hint.
  1526. _LookupUserSid();
  1527. if (NULL != _pszSID)
  1528. {
  1529. TCHAR szKey[MAX_PATH];
  1530. TCHAR szSD[MAX_PATH];
  1531. PathCombine(szKey, TEXT("MACHINE"), szHintKey);
  1532. wnsprintf(szSD,
  1533. ARRAYSIZE(szSD),
  1534. TEXT("D:(A;;0x2;;;%s)"), // 0x2 = KEY_SET_VALUE
  1535. _pszSID);
  1536. SetDacl(szKey, SE_REGISTRY_KEY, szSD);
  1537. }
  1538. }
  1539. RegCloseKey(hkHints);
  1540. }
  1541. }
  1542. return dwErr;
  1543. }
  1544. STDMETHODIMP CLogonUser::get_isPasswordResetAvailable(VARIANT_BOOL* pbResetAvailable)
  1545. {
  1546. DWORD dwResult;
  1547. if (!pbResetAvailable)
  1548. return E_POINTER;
  1549. *pbResetAvailable = VARIANT_FALSE;
  1550. if (0 == PRQueryStatus(NULL, _szLoginName, &dwResult))
  1551. {
  1552. if (0 == dwResult)
  1553. {
  1554. *pbResetAvailable = VARIANT_TRUE;
  1555. }
  1556. }
  1557. return S_OK;
  1558. }