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.

642 lines
17 KiB

  1. #include "mslocusr.h"
  2. #include "msluglob.h"
  3. #include "profiles.h"
  4. #include <regentry.h>
  5. #include <ole2.h>
  6. CLUDatabase::CLUDatabase(void)
  7. : m_cRef(0),
  8. m_CurrentUser(NULL)
  9. {
  10. RefThisDLL(TRUE);
  11. }
  12. CLUDatabase::~CLUDatabase(void)
  13. {
  14. if (m_CurrentUser != NULL) {
  15. m_CurrentUser->Release();
  16. m_CurrentUser = NULL;
  17. }
  18. RefThisDLL(FALSE);
  19. }
  20. STDMETHODIMP CLUDatabase::QueryInterface(REFIID riid, LPVOID * ppvObj)
  21. {
  22. if (!IsEqualIID(riid, IID_IUnknown) &&
  23. !IsEqualIID(riid, IID_IUserDatabase)) {
  24. *ppvObj = NULL;
  25. return ResultFromScode(E_NOINTERFACE);
  26. }
  27. *ppvObj = this;
  28. AddRef();
  29. return NOERROR;
  30. }
  31. STDMETHODIMP_(ULONG) CLUDatabase::AddRef(void)
  32. {
  33. return ++m_cRef;
  34. }
  35. STDMETHODIMP_(ULONG) CLUDatabase::Release(void)
  36. {
  37. ULONG cRef;
  38. cRef = --m_cRef;
  39. if (0L == m_cRef) {
  40. delete this;
  41. }
  42. /* Handle circular refcount because of cached current user object. */
  43. else if (1L == m_cRef && m_CurrentUser != NULL) {
  44. IUser *pCurrentUser = m_CurrentUser;
  45. m_CurrentUser = NULL;
  46. pCurrentUser->Release();
  47. }
  48. return cRef;
  49. }
  50. STDMETHODIMP CLUDatabase::Install(LPCSTR pszSupervisorName,
  51. LPCSTR pszSupervisorPassword,
  52. LPCSTR pszRatingsPassword,
  53. IUserProfileInit *pInit)
  54. {
  55. /* If the system already has a supervisor password, make sure the caller's
  56. * password matches. If there isn't already a password, the caller's
  57. * (account) password is it. We use the account password because the
  58. * caller (the setup program) probably didn't pass us a ratings password
  59. * in that case -- he also checks to see if there's an old ratings
  60. * password and knows to prompt for one only if it's already there.
  61. */
  62. HRESULT hres = ::VerifySupervisorPassword(pszRatingsPassword);
  63. if (FAILED(hres)) {
  64. if (pszRatingsPassword == NULL)
  65. pszRatingsPassword = pszSupervisorPassword;
  66. ::ChangeSupervisorPassword(::szNULL, pszRatingsPassword);
  67. }
  68. else if (hres == S_FALSE)
  69. return E_ACCESSDENIED;
  70. /* User profiles and password caching have to be enabled for us to work.
  71. * We also have to be able to open or create the supervisor's PWL using
  72. * the given password. Thus we validate the password at the same time.
  73. */
  74. {
  75. RegEntry re(::szLogonKey, HKEY_LOCAL_MACHINE);
  76. if (re.GetError() != ERROR_SUCCESS)
  77. return HRESULT_FROM_WIN32(re.GetError());
  78. if (!re.GetNumber(::szUserProfiles))
  79. re.SetValue(::szUserProfiles, 1);
  80. if (re.GetError() != ERROR_SUCCESS)
  81. return HRESULT_FROM_WIN32(re.GetError());
  82. }
  83. /* Make copies of the username and password for passing to the PWL APIs.
  84. * They need to be in OEM (PWL is accessible from DOS), and must be upper
  85. * case since the Windows logon dialog uppercases all PWL passwords.
  86. */
  87. NLS_STR nlsPWLName(pszSupervisorName);
  88. NLS_STR nlsPWLPassword(pszSupervisorPassword);
  89. if (nlsPWLName.QueryError() != ERROR_SUCCESS)
  90. return HRESULT_FROM_WIN32(nlsPWLName.QueryError());
  91. if (nlsPWLPassword.QueryError() != ERROR_SUCCESS)
  92. return HRESULT_FROM_WIN32(nlsPWLPassword.QueryError());
  93. nlsPWLName.strupr();
  94. nlsPWLName.ToOEM();
  95. nlsPWLPassword.strupr();
  96. nlsPWLPassword.ToOEM();
  97. HPWL hPWL = NULL;
  98. APIERR err = ::OpenPasswordCache(&hPWL, nlsPWLName.QueryPch(),
  99. nlsPWLPassword.QueryPch(), TRUE);
  100. if (err != ERROR_SUCCESS) {
  101. if (err != IERR_IncorrectUsername)
  102. err = ::CreatePasswordCache(&hPWL, nlsPWLName.QueryPch(), nlsPWLPassword.QueryPch());
  103. if (err != ERROR_SUCCESS)
  104. return HRESULT_FROM_WIN32(err);
  105. }
  106. /* Now that the system has a supervisor password, call a worker function
  107. * to clone the supervisor account from the default profile. The worker
  108. * function assumes that the caller has validated that the current user is
  109. * a supervisor.
  110. */
  111. err = ::MakeSupervisor(hPWL, pszRatingsPassword);
  112. ::ClosePasswordCache(hPWL, TRUE);
  113. if (err != ERROR_SUCCESS)
  114. return HRESULT_FROM_WIN32(err);
  115. IUser *pSupervisor = NULL;
  116. hres = GetUser(pszSupervisorName, &pSupervisor);
  117. if (FAILED(hres)) {
  118. hres = CreateUser(pszSupervisorName, NULL, TRUE, pInit);
  119. if (pSupervisor != NULL) {
  120. pSupervisor->Release();
  121. pSupervisor = NULL;
  122. }
  123. if (SUCCEEDED(hres))
  124. hres = GetUser(pszSupervisorName, &pSupervisor); /* reinitialize with created profile */
  125. }
  126. if (pSupervisor != NULL) {
  127. if (SUCCEEDED(hres))
  128. hres = pSupervisor->Authenticate(pszSupervisorPassword);
  129. if (SUCCEEDED(hres))
  130. hres = SetCurrentUser(pSupervisor);
  131. if (SUCCEEDED(hres))
  132. pSupervisor->SetSupervisorPrivilege(TRUE, pszRatingsPassword); /* set appears-supervisor flag */
  133. pSupervisor->Release();
  134. pSupervisor = NULL;
  135. }
  136. return hres;
  137. }
  138. /* Some install stubs are "clone-user" install stubs, that get re-run if a
  139. * profile is cloned to become a new user's profile. For example, if you
  140. * clone Fred to make Barney, Outlook Express doesn't want Barney to inherit
  141. * Fred's mailbox.
  142. *
  143. * When you run the go-multiuser wizard, we assume that the first user being
  144. * created is the one who's been using the machine all along, so that one
  145. * copy should be exempt from this. So we go through all the install stub
  146. * keys for the newly created profile and, for any that are marked with a
  147. * username (even a blank one indicates that it's a clone-user install stub),
  148. * we mark it with the new username so it won't get re-run.
  149. */
  150. void FixInstallStubs(LPCSTR pszName, HKEY hkeyProfile)
  151. {
  152. HKEY hkeyList;
  153. LONG err = RegOpenKeyEx(hkeyProfile, "Software\\Microsoft\\Active Setup\\Installed Components", 0,
  154. KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, &hkeyList);
  155. if (err == ERROR_SUCCESS) {
  156. DWORD cbKeyName, iKey;
  157. TCHAR szKeyName[80];
  158. /* Enumerate components that are installed for the profile. */
  159. for (iKey = 0; ; iKey++)
  160. {
  161. LONG lEnum;
  162. cbKeyName = ARRAYSIZE(szKeyName);
  163. if ((lEnum = RegEnumKey(hkeyList, iKey, szKeyName, cbKeyName)) == ERROR_MORE_DATA)
  164. {
  165. // ERROR_MORE_DATA means the value name or data was too large
  166. // skip to the next item
  167. continue;
  168. }
  169. else if( lEnum != ERROR_SUCCESS )
  170. {
  171. // could be ERROR_NO_MORE_ENTRIES, or some kind of failure
  172. // we can't recover from any other registry problem, anyway
  173. break;
  174. }
  175. HKEY hkeyComponent;
  176. if (RegOpenKeyEx(hkeyList, szKeyName, 0,
  177. KEY_QUERY_VALUE | KEY_SET_VALUE, &hkeyComponent) == ERROR_SUCCESS) {
  178. cbKeyName = sizeof(szKeyName);
  179. err = RegQueryValueEx(hkeyComponent, "Username", NULL, NULL,
  180. (LPBYTE)szKeyName, &cbKeyName);
  181. if (err == ERROR_SUCCESS || err == ERROR_MORE_DATA) {
  182. RegSetValueEx(hkeyComponent, "Username",
  183. 0, REG_SZ,
  184. (LPBYTE)pszName,
  185. lstrlen(pszName)+1);
  186. }
  187. RegCloseKey(hkeyComponent);
  188. }
  189. }
  190. RegCloseKey(hkeyList);
  191. }
  192. }
  193. STDMETHODIMP CLUDatabase::CreateUser(LPCSTR pszName, IUser *pCloneFrom,
  194. BOOL fFixInstallStubs, IUserProfileInit *pInit)
  195. {
  196. if (::strlenf(pszName) > cchMaxUsername)
  197. return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW);
  198. RegEntry reRoot(::szProfileList, HKEY_LOCAL_MACHINE);
  199. if (reRoot.GetError() != ERROR_SUCCESS)
  200. return HRESULT_FROM_WIN32(reRoot.GetError());
  201. /* See if the user's subkey exists. If it doesn't, create it. */
  202. reRoot.MoveToSubKey(pszName);
  203. if (reRoot.GetError() != ERROR_SUCCESS) {
  204. RegEntry reUser(pszName, reRoot.GetKey());
  205. if (reUser.GetError() != ERROR_SUCCESS)
  206. return HRESULT_FROM_WIN32(reUser.GetError());
  207. reRoot.MoveToSubKey(pszName);
  208. if (reRoot.GetError() != ERROR_SUCCESS)
  209. return HRESULT_FROM_WIN32(reRoot.GetError());
  210. }
  211. NLS_STR nlsProfilePath(MAX_PATH);
  212. if (nlsProfilePath.QueryError() != ERROR_SUCCESS)
  213. return E_OUTOFMEMORY;
  214. reRoot.GetValue(::szProfileImagePath, &nlsProfilePath);
  215. /* If the profile path is already recorded for the user, see if the
  216. * profile itself exists. If it does, then CreateUser is an error.
  217. */
  218. BOOL fComputePath = FALSE;
  219. if (reRoot.GetError() == ERROR_SUCCESS) {
  220. if (!DirExists(nlsProfilePath.QueryPch())) {
  221. if (!::CreateDirectory(nlsProfilePath.QueryPch(), NULL)) {
  222. fComputePath = TRUE;
  223. }
  224. }
  225. }
  226. else {
  227. fComputePath = TRUE;
  228. }
  229. if (fComputePath) {
  230. ComputeLocalProfileName(pszName, &nlsProfilePath);
  231. reRoot.SetValue(::szProfileImagePath, nlsProfilePath.QueryPch());
  232. }
  233. AddBackslash(nlsProfilePath);
  234. nlsProfilePath.strcat(::szStdNormalProfile);
  235. if (FileExists(nlsProfilePath.QueryPch()))
  236. return HRESULT_FROM_WIN32(ERROR_USER_EXISTS);
  237. /* The user's profile directory now exists, and its path is recorded
  238. * in the registry. nlsProfilePath is now the full pathname for the
  239. * user's profile file, which does not exist yet.
  240. */
  241. NLS_STR nlsOtherProfilePath(MAX_PATH);
  242. if (nlsOtherProfilePath.QueryError() != ERROR_SUCCESS)
  243. return HRESULT_FROM_WIN32(nlsOtherProfilePath.QueryError());
  244. HRESULT hres;
  245. DWORD cbPath = nlsOtherProfilePath.QueryAllocSize();
  246. if (pCloneFrom == NULL ||
  247. FAILED(pCloneFrom->GetProfileDirectory(nlsOtherProfilePath.Party(), &cbPath)))
  248. {
  249. /* Cloning default profile. */
  250. hres = GiveUserDefaultProfile(nlsProfilePath.QueryPch());
  251. nlsOtherProfilePath.DonePartying();
  252. nlsOtherProfilePath = "";
  253. }
  254. else {
  255. /* Cloning other user's profile. */
  256. nlsOtherProfilePath.DonePartying();
  257. AddBackslash(nlsOtherProfilePath);
  258. nlsOtherProfilePath.strcat(::szStdNormalProfile);
  259. hres = CopyProfile(nlsOtherProfilePath.QueryPch(), nlsProfilePath.QueryPch());
  260. }
  261. if (FAILED(hres))
  262. return hres;
  263. /* Now the user has a profile. Load it and perform directory
  264. * reconciliation.
  265. */
  266. LONG err = ::MyRegLoadKey(HKEY_USERS, pszName, nlsProfilePath.QueryPch());
  267. if (err == ERROR_SUCCESS) {
  268. HKEY hkeyNewProfile;
  269. err = ::RegOpenKey(HKEY_USERS, pszName, &hkeyNewProfile);
  270. if (err == ERROR_SUCCESS) {
  271. /* Build just the profile directory, no "user.dat" on the end. */
  272. ISTR istrBackslash(nlsProfilePath);
  273. if (nlsProfilePath.strrchr(&istrBackslash, '\\')) {
  274. ++istrBackslash;
  275. nlsProfilePath.DelSubStr(istrBackslash);
  276. }
  277. if (pInit != NULL) {
  278. hres = pInit->PreInitProfile(hkeyNewProfile, nlsProfilePath.QueryPch());
  279. if (hres == E_NOTIMPL)
  280. hres = S_OK;
  281. }
  282. else
  283. hres = S_OK;
  284. if (SUCCEEDED(hres)) {
  285. err = ReconcileFiles(hkeyNewProfile, nlsProfilePath, nlsOtherProfilePath); /* modifies nlsProfilePath */
  286. hres = HRESULT_FROM_WIN32(err);
  287. if (fFixInstallStubs) {
  288. ::FixInstallStubs(pszName, hkeyNewProfile);
  289. }
  290. if (pInit != NULL) {
  291. hres = pInit->PostInitProfile(hkeyNewProfile, nlsProfilePath.QueryPch());
  292. if (hres == E_NOTIMPL)
  293. hres = S_OK;
  294. }
  295. }
  296. ::RegFlushKey(hkeyNewProfile);
  297. ::RegCloseKey(hkeyNewProfile);
  298. }
  299. ::RegUnLoadKey(HKEY_USERS, pszName);
  300. }
  301. return hres;
  302. }
  303. STDMETHODIMP CLUDatabase::AddUser(LPCSTR pszName, IUser *pSourceUser,
  304. IUserProfileInit *pInit, IUser **ppOut)
  305. {
  306. if (ppOut != NULL)
  307. *ppOut = NULL;
  308. if (IsCurrentUserSupervisor(this) != S_OK)
  309. return E_ACCESSDENIED;
  310. HRESULT hres = CreateUser(pszName, pSourceUser, FALSE, pInit);
  311. if (FAILED(hres))
  312. return hres;
  313. if (ppOut != NULL)
  314. hres = GetUser(pszName, ppOut);
  315. return hres;
  316. }
  317. STDMETHODIMP CLUDatabase::GetUser(LPCSTR pszName, IUser **ppOut)
  318. {
  319. *ppOut = NULL;
  320. CLUUser *pUser = new CLUUser(this);
  321. if (pUser == NULL) {
  322. return ResultFromScode(E_OUTOFMEMORY);
  323. }
  324. HRESULT err = pUser->Init(pszName);
  325. if (SUCCEEDED(err) && !pUser->Exists()) {
  326. err = HRESULT_FROM_WIN32(ERROR_NO_SUCH_USER);
  327. }
  328. if (FAILED(err) || !pUser->Exists()) {
  329. pUser->Release();
  330. return err;
  331. }
  332. *ppOut = pUser;
  333. return NOERROR;
  334. }
  335. STDMETHODIMP CLUDatabase::GetSpecialUser(DWORD nSpecialUserCode, IUser **ppOut)
  336. {
  337. switch (nSpecialUserCode) {
  338. case GSU_CURRENT:
  339. return GetCurrentUser(ppOut);
  340. break;
  341. case GSU_DEFAULT:
  342. return GetUser(szDefaultUserName, ppOut);
  343. break;
  344. default:
  345. return ResultFromScode(E_INVALIDARG);
  346. };
  347. return NOERROR;
  348. }
  349. HRESULT GetSystemCurrentUser(NLS_STR *pnlsCurrentUser)
  350. {
  351. DWORD cbBuffer = pnlsCurrentUser->QueryAllocSize();
  352. UINT err;
  353. if (!::GetUserName(pnlsCurrentUser->Party(), &cbBuffer))
  354. err = ::GetLastError();
  355. else
  356. err = NOERROR;
  357. pnlsCurrentUser->DonePartying();
  358. return HRESULT_FROM_WIN32(err);
  359. }
  360. STDMETHODIMP CLUDatabase::GetCurrentUser(IUser **ppOut)
  361. {
  362. if (m_CurrentUser == NULL) {
  363. NLS_STR nlsCurrentUser(cchMaxUsername+1);
  364. UINT err = nlsCurrentUser.QueryError();
  365. if (err)
  366. return HRESULT_FROM_WIN32(err);
  367. HRESULT hres = GetSystemCurrentUser(&nlsCurrentUser);
  368. if (FAILED(hres))
  369. return hres;
  370. hres = GetUser(nlsCurrentUser.QueryPch(), (IUser **)&m_CurrentUser);
  371. if (FAILED(hres))
  372. return hres;
  373. }
  374. *ppOut = m_CurrentUser;
  375. m_CurrentUser->AddRef();
  376. return NOERROR;
  377. }
  378. STDMETHODIMP CLUDatabase::SetCurrentUser(IUser *pUser)
  379. {
  380. CLUUser *pCLUUser = (CLUUser *)pUser;
  381. HPWL hpwlUser;
  382. if (!pCLUUser->m_fAuthenticated ||
  383. FAILED(pCLUUser->GetPasswordCache(pCLUUser->m_nlsPassword.QueryPch(), &hpwlUser)))
  384. {
  385. return HRESULT_FROM_WIN32(ERROR_NOT_AUTHENTICATED);
  386. }
  387. ::ClosePasswordCache(hpwlUser, TRUE);
  388. CLUUser *pClone;
  389. HRESULT hres = GetUser(pCLUUser->m_nlsUsername.QueryPch(), (IUser **)&pClone);
  390. if (FAILED(hres))
  391. return hres;
  392. /* Make sure the clone object is authenticated properly. */
  393. hres = pClone->Authenticate(pCLUUser->m_nlsPassword.QueryPch());
  394. if (FAILED(hres)) {
  395. return HRESULT_FROM_WIN32(ERROR_NOT_AUTHENTICATED);
  396. }
  397. if (m_CurrentUser != NULL) {
  398. m_CurrentUser->Release();
  399. }
  400. m_CurrentUser = pClone;
  401. return NOERROR;
  402. }
  403. STDMETHODIMP CLUDatabase::DeleteUser(LPCSTR pszName)
  404. {
  405. NLS_STR nlsName(MAX_PATH);
  406. if (nlsName.QueryError() != ERROR_SUCCESS)
  407. return HRESULT_FROM_WIN32(nlsName.QueryError());
  408. /* Check supervisor privilege up front, this'll handle the not-logged-on
  409. * case later if we re-enable supervisor stuff.
  410. */
  411. if (IsCurrentUserSupervisor(this) != S_OK)
  412. return E_ACCESSDENIED;
  413. IUser *pCurrentUser;
  414. HRESULT hres = GetCurrentUser(&pCurrentUser);
  415. if (SUCCEEDED(hres)) {
  416. /* Check current user's name and make sure we're not deleting him.
  417. * Note that because the current user must be an authenticated supervisor,
  418. * and you can't delete the current user, you can never delete the last
  419. * supervisor using this function.
  420. */
  421. DWORD cb = nlsName.QueryAllocSize();
  422. hres = pCurrentUser->GetName(nlsName.Party(), &cb);
  423. nlsName.DonePartying();
  424. if (SUCCEEDED(hres) && !::stricmpf(pszName, nlsName.QueryPch()))
  425. hres = HRESULT_FROM_WIN32(ERROR_BUSY);
  426. if (FAILED(hres))
  427. return hres;
  428. }
  429. /* Check system's idea of current user as well. */
  430. hres = GetSystemCurrentUser(&nlsName);
  431. if (SUCCEEDED(hres)) {
  432. if (!::stricmpf(pszName, nlsName.QueryPch()))
  433. return HRESULT_FROM_WIN32(ERROR_BUSY);
  434. }
  435. return DeleteProfile(pszName);
  436. }
  437. STDMETHODIMP CLUDatabase::RenameUser(LPCSTR pszOldName, LPCSTR pszNewName)
  438. {
  439. return ResultFromScode(E_NOTIMPL);
  440. }
  441. STDMETHODIMP CLUDatabase::EnumUsers(IEnumUnknown **ppOut)
  442. {
  443. *ppOut = NULL;
  444. CLUEnum *pEnum = new CLUEnum(this);
  445. if (pEnum == NULL) {
  446. return ResultFromScode(E_OUTOFMEMORY);
  447. }
  448. HRESULT err = pEnum->Init();
  449. if (FAILED(err)) {
  450. pEnum->Release();
  451. return err;
  452. }
  453. *ppOut = pEnum;
  454. return NOERROR;
  455. }
  456. STDMETHODIMP CLUDatabase::Authenticate(HWND hwndOwner, DWORD dwFlags,
  457. LPCSTR pszName, LPCSTR pszPassword,
  458. IUser **ppOut)
  459. {
  460. if (dwFlags & LUA_DIALOG) {
  461. if (!UseUserProfiles() || FAILED(VerifySupervisorPassword(szNULL))) {
  462. return InstallWizard(hwndOwner);
  463. }
  464. return ::DoUserDialog(hwndOwner, dwFlags, ppOut);
  465. }
  466. /* Null out return pointer for error cases. */
  467. if (ppOut != NULL)
  468. *ppOut = NULL;
  469. IUser *pUser;
  470. BOOL fReleaseMe = TRUE;
  471. HRESULT hres = GetUser(pszName, &pUser);
  472. if (SUCCEEDED(hres)) {
  473. hres = pUser->Authenticate(pszPassword);
  474. if (SUCCEEDED(hres)) {
  475. if ((dwFlags & LUA_SUPERVISORONLY) && (pUser->IsSupervisor() != S_OK)) {
  476. hres = E_ACCESSDENIED;
  477. }
  478. else if (ppOut != NULL) {
  479. *ppOut = pUser;
  480. fReleaseMe = FALSE;
  481. }
  482. }
  483. if (fReleaseMe)
  484. pUser->Release();
  485. }
  486. return hres;
  487. }
  488. STDMETHODIMP CLUDatabase::InstallComponent(REFCLSID clsidComponent,
  489. LPCSTR pszName, DWORD dwFlags)
  490. {
  491. return ResultFromScode(E_NOTIMPL);
  492. }
  493. STDMETHODIMP CLUDatabase::RemoveComponent(REFCLSID clsidComponent, LPCSTR pszName)
  494. {
  495. return ResultFromScode(E_NOTIMPL);
  496. }
  497. #ifdef MSLOCUSR_USE_SUPERVISOR_PASSWORD
  498. HRESULT IsCurrentUserSupervisor(IUserDatabase *pDB)
  499. {
  500. IUser *pCurrentUser = NULL;
  501. HRESULT hres = pDB->GetCurrentUser(&pCurrentUser);
  502. if (SUCCEEDED(hres)) {
  503. hres = pCurrentUser->IsSupervisor();
  504. }
  505. if (pCurrentUser != NULL) {
  506. pCurrentUser->Release();
  507. }
  508. return hres;
  509. }
  510. #else
  511. HRESULT IsCurrentUserSupervisor(IUserDatabase *pDB) { return S_OK; }
  512. #endif