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.

2324 lines
60 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. account.cxx
  5. Abstract:
  6. This module contains service account related routines:
  7. ScInitServiceAccount
  8. ScEndServiceAccount
  9. ScCanonAccountName
  10. ScValidateAndSaveAccount
  11. ScValidateAndChangeAccount
  12. ScRemoveAccount
  13. ScLookupAccount
  14. ScSetPassword
  15. ScDeletePassword
  16. ScOpenPolicy
  17. ScFormSecretName
  18. ScLookupServiceAccount
  19. ScLogonService
  20. ScLoadUserProfile
  21. ScUPNToAccountName
  22. Author:
  23. Rita Wong (ritaw) 19-Apr-1992
  24. Environment:
  25. Calls NT native APIs.
  26. Revision History:
  27. 24-Jan-1993 Danl
  28. Added call to WNetLogonNotify when logging on a service (ScLogonService).
  29. 29-Apr-1993 Danl
  30. ScGetAccountDomainInfo() is now only called at init time. Otherwise,
  31. we risked race conditions because it updates a global location.
  32. (ScLocalDomain).
  33. 17-Jan-1995 AnirudhS
  34. Added call to LsaOpenSecret when the secret already exists in
  35. ScCreatePassword.
  36. 29-Nov-1995 AnirudhS
  37. Added call to LoadUserProfile when logging on a service.
  38. 14-May-1996 AnirudhS
  39. Changed to simpler Lsa PrivateData APIs instead of Lsa Secret APIs
  40. for storing secrets and removed the use of OldPassword (as done in
  41. the _CAIRO_ version of this file on 05-Apr-1995).
  42. 22-Oct-1997 JSchwart (after AnirudhS in _CAIRO_ 10-Apr-1995)
  43. Split out ScLookupServiceAccount from ScLogonService.
  44. 04-Mar-1999 jschwart
  45. Added support for UPNs
  46. --*/
  47. #include "precomp.hxx"
  48. #include <stdlib.h> // srand, rand
  49. extern "C" {
  50. #include <ntlsa.h> // LsaOpenPolicy, LsaCreateSecret
  51. }
  52. #include <winerror.h>
  53. #include <userenv.h> // LoadUserProfile
  54. #include <userenvp.h> // PI_HIDEPROFILE flag
  55. #include <tstr.h> // WCSSIZE
  56. #include <ntdsapi.h> // DsCrackNames
  57. #include <sclib.h> // _wcsicmp
  58. #include <scseclib.h> // LocalSid
  59. #include "scconfig.h" // ScWriteStartName
  60. #include "account.h" // Exported function prototypes
  61. //-------------------------------------------------------------------//
  62. // //
  63. // Constants and Macros //
  64. // //
  65. //-------------------------------------------------------------------//
  66. #define SC_SECRET_PREFIX L"_SC_"
  67. #define SC_UPN_SYMBOL L'@'
  68. //-------------------------------------------------------------------//
  69. // //
  70. // Static global variables //
  71. // //
  72. //-------------------------------------------------------------------//
  73. //
  74. // Mutex to serialize access to secret objects
  75. //
  76. HANDLE ScSecretObjectsMutex = (HANDLE) NULL;
  77. //
  78. // LSA Authentication Package expects the local computername to
  79. // be specified for the account domain if the system is WinNT,
  80. // and the primary domain to be specified if the system is LanmanNT.
  81. // Set ScLocalDomain to point to either ScComputerName or
  82. // ScAccountDomain depending on the product type.
  83. //
  84. PUNICODE_STRING ScLocalDomain;
  85. UNICODE_STRING ScComputerName;
  86. UNICODE_STRING ScAccountDomain;
  87. //-------------------------------------------------------------------//
  88. // //
  89. // Local function prototypes //
  90. // //
  91. //-------------------------------------------------------------------//
  92. DWORD
  93. ScLookupAccount(
  94. IN LPWSTR AccountName,
  95. OUT LPWSTR *DomainName,
  96. OUT LPWSTR *UserName
  97. );
  98. DWORD
  99. ScSetPassword(
  100. IN LPWSTR ServiceName,
  101. IN LPWSTR Password
  102. );
  103. DWORD
  104. ScDeletePassword(
  105. IN LPWSTR ServiceName
  106. );
  107. DWORD
  108. ScOpenPolicy(
  109. IN ACCESS_MASK DesiredAccess,
  110. OUT LSA_HANDLE *PolicyHandle
  111. );
  112. DWORD
  113. ScFormSecretName(
  114. IN LPWSTR ServiceName,
  115. OUT LPWSTR *LsaSecretName
  116. );
  117. VOID
  118. ScLoadUserProfile(
  119. IN HANDLE LogonToken,
  120. IN LPWSTR DomainName,
  121. IN LPWSTR UserName,
  122. OUT PHANDLE pProfileHandle OPTIONAL
  123. );
  124. DWORD
  125. ScUPNToAccountName(
  126. IN LPWSTR lpUPN,
  127. OUT LPWSTR *ppAccountName
  128. );
  129. //-------------------------------------------------------------------//
  130. // //
  131. // Functions //
  132. // //
  133. //-------------------------------------------------------------------//
  134. BOOL
  135. ScGetComputerNameAndMutex(
  136. VOID
  137. )
  138. /*++
  139. Routine Description:
  140. This function allocates the memory for the ScComputerName global
  141. pointer and retrieves the current computer name into it. This
  142. functionality used to be in the ScInitAccount routine but has to
  143. be put into its own routine because the main initialization code
  144. needs to call this before ScInitDatabase() since the computername
  145. is needed for deleting service entries that have the persistent
  146. delete flag set.
  147. This function also creates ScSecretObjectsMutex because it is used
  148. to remove accounts early in the init process.
  149. If successful, the pointer to the computername must be freed when
  150. done. This is freed by the ScEndServiceAccount routine called
  151. by SvcctrlMain(). The handle to ScSecretObjectsMutex is closed
  152. by ScSecretObjectsMutex.
  153. Arguments:
  154. None
  155. Return Value:
  156. TRUE - The operation was completely successful.
  157. FALSE - An error occurred.
  158. --*/
  159. {
  160. DWORD ComputerNameSize = MAX_COMPUTERNAME_LENGTH + 1;
  161. ScComputerName.Buffer = NULL;
  162. //
  163. // Allocate the exact size needed to hold the computername
  164. //
  165. if ((ScComputerName.Buffer = (LPWSTR)LocalAlloc(
  166. LMEM_ZEROINIT,
  167. (UINT) ComputerNameSize * sizeof(WCHAR)
  168. )) == NULL) {
  169. SC_LOG1(ERROR, "ScInitServiceAccount: LocalAlloc failed %lu\n", GetLastError());
  170. return FALSE;
  171. }
  172. ScComputerName.MaximumLength = (USHORT) ComputerNameSize * sizeof(WCHAR);
  173. if (! GetComputerNameW(
  174. ScComputerName.Buffer,
  175. &ComputerNameSize
  176. )) {
  177. SC_LOG2(ERROR, "GetComputerNameW returned %lu, required size=%lu\n",
  178. GetLastError(), ComputerNameSize);
  179. LocalFree(ScComputerName.Buffer);
  180. ScComputerName.Buffer = NULL;
  181. return FALSE;
  182. }
  183. ScComputerName.Length = (USHORT) (wcslen(ScComputerName.Buffer) * sizeof(WCHAR));
  184. SC_LOG(ACCOUNT, "ScInitServiceAccount: ScComputerName is "
  185. FORMAT_LPWSTR "\n", ScComputerName.Buffer);
  186. //
  187. // Create a mutex to serialize accesses to all secret objects. A secret
  188. // object can be created, deleted, or set by installation programs, set
  189. // by the service controller during periodic password changes, and queried
  190. // or set by a start service operation.
  191. //
  192. ScSecretObjectsMutex = CreateMutex(NULL, FALSE, NULL);
  193. if (ScSecretObjectsMutex == NULL) {
  194. SC_LOG1(ERROR, "ScInitServiceAccount: CreateMutex failed "
  195. FORMAT_DWORD "\n", GetLastError());
  196. return FALSE;
  197. }
  198. return TRUE;
  199. }
  200. BOOL
  201. ScInitServiceAccount(
  202. VOID
  203. )
  204. /*++
  205. Routine Description:
  206. This function initializes accounts for services by
  207. 2) Register service controller as an LSA logon process and
  208. lookup the MS V 1.0 authentication package.
  209. Arguments:
  210. None
  211. Return Value:
  212. TRUE - The operation was completely successful.
  213. FALSE - An error occurred.
  214. --*/
  215. {
  216. DWORD status;
  217. //
  218. // Initialize the account domain buffer so that we know if it has
  219. // been filled in.
  220. //
  221. ScAccountDomain.Buffer = NULL;
  222. status = ScGetAccountDomainInfo();
  223. if (status != NO_ERROR)
  224. {
  225. SC_LOG1(ERROR, "ScInitServiceAccount: ScGetAccountDomainInfo failed "
  226. FORMAT_DWORD "\n", status);
  227. return FALSE;
  228. }
  229. return TRUE;
  230. }
  231. VOID
  232. ScEndServiceAccount(
  233. VOID
  234. )
  235. /*++
  236. Routine Description:
  237. This function frees the memory for the ScComputerName global pointer,
  238. and closes the ScSecretObjectsMutex.
  239. Arguments:
  240. None.
  241. Return Value:
  242. None.
  243. --*/
  244. {
  245. //
  246. // Free computer name buffer allocated by ScGetComputerName
  247. //
  248. LocalFree(ScComputerName.Buffer);
  249. ScComputerName.Buffer = NULL;
  250. if (ScSecretObjectsMutex != (HANDLE) NULL)
  251. {
  252. CloseHandle(ScSecretObjectsMutex);
  253. }
  254. LocalFree(ScAccountDomain.Buffer);
  255. }
  256. DWORD
  257. ScValidateAndSaveAccount(
  258. IN LPWSTR ServiceName,
  259. IN HKEY ServiceNameKey,
  260. IN LPWSTR CanonAccountName,
  261. IN LPWSTR Password OPTIONAL
  262. )
  263. /*++
  264. Routine Description:
  265. This function verifies that the account is valid, and then saves
  266. the account information away. The account name is saved in the
  267. registry under the service node in the ObjectName value. The
  268. password is saved in an LSA secret object created which can be
  269. looked up based on the name string formed with the service name.
  270. This function can only be called for the installation of a Win32
  271. service (CreateService).
  272. NOTE: The registry ServiceNameKey is NOT flushed by this function.
  273. Arguments:
  274. ServiceName - Supplies the name of the service to save away account
  275. info for. This makes up part of the secret object name to tuck
  276. away the password.
  277. ServiceNameKey - Supplies an opened registry key handle for the service.
  278. CanonAccountName - Supplies a canonicalized account name string in the
  279. format of DomainName\Username, LocalSystem, or UPN
  280. Password - Supplies the password of the account, if any. This is
  281. ignored if LocalSystem is specified for the account name.
  282. Return Value:
  283. NO_ERROR - The operation was successful.
  284. ERROR_INVALID_SERVICE_ACCOUNT - The account name is invalid.
  285. ERROR_NOT_ENOUGH_MEMORY - Failed to allocate work buffer.
  286. Registry error codes caused by failure to read old account name
  287. string.
  288. --*/
  289. {
  290. DWORD status;
  291. LPWSTR DomainName;
  292. LPWSTR UserName;
  293. LPWSTR lpNameToParse = CanonAccountName;
  294. //
  295. // Empty account name is invalid.
  296. //
  297. if ((CanonAccountName == NULL) || (*CanonAccountName == 0)) {
  298. return ERROR_INVALID_SERVICE_ACCOUNT;
  299. }
  300. if (_wcsicmp(CanonAccountName, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
  301. //
  302. // CanonAccountName is LocalSystem. Write to the registry and
  303. // we are done.
  304. //
  305. return ScWriteStartName(
  306. ServiceNameKey,
  307. SC_LOCAL_SYSTEM_USER_NAME
  308. );
  309. }
  310. //
  311. // Account name is DomainName\UserName or a UPN
  312. //
  313. if (wcschr(CanonAccountName, SCDOMAIN_USERNAME_SEPARATOR) == NULL
  314. &&
  315. wcschr(CanonAccountName, SC_UPN_SYMBOL) != NULL)
  316. {
  317. //
  318. // It's a UPN -- we need to crack it
  319. //
  320. status = ScUPNToAccountName(CanonAccountName, &lpNameToParse);
  321. if (status != NO_ERROR) {
  322. return status;
  323. }
  324. }
  325. //
  326. // Look up the account to see if it exists.
  327. //
  328. if ((status = ScLookupAccount(
  329. lpNameToParse,
  330. &DomainName,
  331. &UserName
  332. )) != NO_ERROR) {
  333. if (lpNameToParse != CanonAccountName) {
  334. LocalFree(lpNameToParse);
  335. }
  336. return status;
  337. }
  338. //
  339. // Write the new account name to the registry.
  340. // Note -- for UPNs, write the UPN to the registry, not
  341. // the cracked UPN
  342. //
  343. if ((status = ScWriteStartName(
  344. ServiceNameKey,
  345. CanonAccountName
  346. )) != NO_ERROR) {
  347. if (lpNameToParse != CanonAccountName) {
  348. LocalFree(lpNameToParse);
  349. }
  350. LocalFree(DomainName);
  351. return status;
  352. }
  353. //
  354. // Create the password for the new account.
  355. //
  356. status = ScSetPassword(
  357. ServiceName,
  358. Password
  359. );
  360. if (lpNameToParse != CanonAccountName) {
  361. LocalFree(lpNameToParse);
  362. }
  363. LocalFree(DomainName);
  364. return status;
  365. //
  366. // Don't have to worry about removing the account name written to
  367. // the registry if ScSetPassword returned an error because the
  368. // entire service key will be deleted by the caller of this routine.
  369. //
  370. }
  371. DWORD
  372. ScValidateAndChangeAccount(
  373. IN LPSERVICE_RECORD ServiceRecord,
  374. IN HKEY ServiceNameKey,
  375. IN LPWSTR OldAccountName,
  376. IN LPWSTR CanonAccountName,
  377. IN LPWSTR Password OPTIONAL
  378. )
  379. /*++
  380. Routine Description:
  381. This function validates that the account is valid, and then replaces
  382. the old account information. The account name is saved in the
  383. registry under the service node in the ObjectName value. The
  384. password is saved in an LSA secret object created which can be
  385. looked up based on the name string formed with the service name.
  386. This function can only be called for the reconfiguration of a Win32
  387. service (ChangeServiceConfig).
  388. NOTE: The registry ServiceNameKey is NOT flushed by this function.
  389. Arguments:
  390. ServiceRecord - Supplies the record of the service to change account
  391. info. This makes up part of the secret object name to tuck
  392. away the password.
  393. ServiceNameKey - Supplies an opened registry key handle for the service.
  394. OldAccountName - Supplies the string to the old account name.
  395. CanonAccountName - Supplies a canonicalized account name string in the
  396. format of DomainName\Username, LocalSystem, or a UPN.
  397. Password - Supplies the password of the account, if any. This is
  398. ignored if LocalSystem is specified for the account name.
  399. Return Value:
  400. NO_ERROR - The operation was successful.
  401. ERROR_INVALID_SERVICE_ACCOUNT - The account name is invalid.
  402. ERROR_ALREADY_EXISTS - Attempt to create an LSA secret object that
  403. already exists.
  404. Registry error codes caused by failure to read old account name
  405. string.
  406. --*/
  407. {
  408. DWORD status;
  409. LPWSTR DomainName;
  410. LPWSTR UserName;
  411. BOOL fIsUPN = FALSE;
  412. LPWSTR lpNameToParse = CanonAccountName;
  413. if ((CanonAccountName == OldAccountName) ||
  414. (_wcsicmp(CanonAccountName, OldAccountName) == 0)) {
  415. //
  416. // Newly specified account name is identical to existing
  417. // account name.
  418. //
  419. if (Password == NULL) {
  420. //
  421. // Not changing account name or password.
  422. //
  423. return NO_ERROR;
  424. }
  425. if (_wcsicmp(CanonAccountName, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
  426. //
  427. // Account name is LocalSystem and password is specified.
  428. // Just ignore.
  429. //
  430. return NO_ERROR;
  431. }
  432. else {
  433. //
  434. // Account name is DomainName\UserName or a UPN.
  435. // Set the specified password.
  436. //
  437. status = ScSetPassword(
  438. ServiceRecord->ServiceName,
  439. Password);
  440. return status;
  441. }
  442. }
  443. //
  444. // Newly specified account name is different from existing
  445. // account name.
  446. //
  447. if (Password == NULL) {
  448. //
  449. // Cannot specify new account name without specifying
  450. // the password also.
  451. //
  452. return ERROR_INVALID_SERVICE_ACCOUNT;
  453. }
  454. if (_wcsicmp(CanonAccountName, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
  455. //
  456. // Change from DomainName\UserName or UPN to LocalSystem
  457. //
  458. //
  459. // Write the new account name to the registry.
  460. //
  461. if ((status = ScWriteStartName(
  462. ServiceNameKey,
  463. SC_LOCAL_SYSTEM_USER_NAME
  464. )) != NO_ERROR) {
  465. return status;
  466. }
  467. //
  468. // Account name is LocalSystem and password is specified.
  469. // Ignore the password specified, and delete the password
  470. // for the old account.
  471. //
  472. status = ScDeletePassword(ServiceRecord->ServiceName);
  473. if (status != NO_ERROR) {
  474. //
  475. // Restore the old account name to the registry.
  476. //
  477. ScWriteStartName(ServiceNameKey,
  478. OldAccountName);
  479. }
  480. else {
  481. LPWSTR CurrentDependencies;
  482. //
  483. // Get rid of the implicit dependency on NetLogon since this
  484. // service no longer runs in an account. Since the dependency
  485. // on NetLogon is soft (i.e., not stored in the registry),
  486. // simply read in the dependencies and update the service record
  487. //
  488. status = ScReadDependencies(ServiceNameKey,
  489. &CurrentDependencies,
  490. ServiceRecord->ServiceName);
  491. if (status == NO_ERROR) {
  492. //
  493. // Dynamically update the dependencies
  494. //
  495. status = ScUpdateServiceRecordConfig(
  496. ServiceRecord,
  497. SERVICE_NO_CHANGE,
  498. SERVICE_NO_CHANGE,
  499. SERVICE_NO_CHANGE,
  500. NULL,
  501. (LPBYTE) CurrentDependencies);
  502. if (status != NO_ERROR) {
  503. SC_LOG1(ERROR,
  504. "ScValidateAndChangeAccount: ScUpdateServiceRecordConfig "
  505. "FAILED %d\n",
  506. status);
  507. }
  508. LocalFree(CurrentDependencies);
  509. }
  510. else {
  511. SC_LOG1(ERROR,
  512. "ScValidateAndChangeAccount: ScReadDependencies "
  513. "FAILED %d\n",
  514. status);
  515. }
  516. }
  517. return status;
  518. }
  519. if (_wcsicmp(OldAccountName, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
  520. //
  521. // Change from LocalSystem to DomainName\UserName or UPN.
  522. //
  523. if (wcschr(CanonAccountName, SCDOMAIN_USERNAME_SEPARATOR) == NULL
  524. &&
  525. wcschr(CanonAccountName, SC_UPN_SYMBOL) != NULL)
  526. {
  527. fIsUPN = TRUE;
  528. status = ScUPNToAccountName(CanonAccountName, &lpNameToParse);
  529. if (status != NO_ERROR) {
  530. return status;
  531. }
  532. }
  533. if ((status = ScLookupAccount(
  534. lpNameToParse,
  535. &DomainName,
  536. &UserName
  537. )) != NO_ERROR) {
  538. if (fIsUPN) {
  539. LocalFree(lpNameToParse);
  540. }
  541. return status;
  542. }
  543. //
  544. // Write the new account name to the registry.
  545. //
  546. if ((status = ScWriteStartName(
  547. ServiceNameKey,
  548. CanonAccountName
  549. )) != NO_ERROR) {
  550. if (fIsUPN) {
  551. LocalFree(lpNameToParse);
  552. }
  553. LocalFree(DomainName);
  554. return status;
  555. }
  556. //
  557. // Create the password for the new account.
  558. //
  559. status = ScSetPassword(
  560. ServiceRecord->ServiceName,
  561. Password);
  562. if (status != NO_ERROR) {
  563. //
  564. // Restore the old account name to the registry.
  565. //
  566. (void) ScWriteStartName(
  567. ServiceNameKey,
  568. SC_LOCAL_SYSTEM_USER_NAME);
  569. }
  570. if (fIsUPN) {
  571. LocalFree(lpNameToParse);
  572. }
  573. LocalFree(DomainName);
  574. return status;
  575. }
  576. //
  577. // Must be changing an account of DomainName\UserName or UPN to
  578. // DomainName\UserName or UPN
  579. //
  580. if (wcschr(CanonAccountName, SCDOMAIN_USERNAME_SEPARATOR) == NULL
  581. &&
  582. wcschr(CanonAccountName, SC_UPN_SYMBOL) != NULL)
  583. {
  584. fIsUPN = TRUE;
  585. status = ScUPNToAccountName(CanonAccountName, &lpNameToParse);
  586. if (status != NO_ERROR) {
  587. return status;
  588. }
  589. }
  590. if ((status = ScLookupAccount(
  591. lpNameToParse,
  592. &DomainName,
  593. &UserName
  594. )) != NO_ERROR) {
  595. if (fIsUPN) {
  596. LocalFree(lpNameToParse);
  597. }
  598. return status;
  599. }
  600. //
  601. // Write the new account name to the registry.
  602. //
  603. if ((status = ScWriteStartName(
  604. ServiceNameKey,
  605. CanonAccountName
  606. )) != NO_ERROR) {
  607. if (fIsUPN) {
  608. LocalFree(lpNameToParse);
  609. }
  610. LocalFree(DomainName);
  611. return status;
  612. }
  613. //
  614. // Set the password for the new account.
  615. //
  616. status = ScSetPassword(ServiceRecord->ServiceName,
  617. Password);
  618. if (status != NO_ERROR) {
  619. //
  620. // Restore the old account name to the registry.
  621. //
  622. ScWriteStartName(ServiceNameKey,
  623. OldAccountName);
  624. }
  625. else if (*DomainName == L'.' && !fIsUPN) {
  626. LPWSTR CurrentDependencies;
  627. //
  628. // Get rid of the implicit dependency on NetLogon since this
  629. // service now runs in a local account (domain is ".\")
  630. //
  631. status = ScReadDependencies(ServiceNameKey,
  632. &CurrentDependencies,
  633. ServiceRecord->ServiceName);
  634. if (status == NO_ERROR) {
  635. //
  636. // Dynamically update the dependencies
  637. //
  638. status = ScUpdateServiceRecordConfig(
  639. ServiceRecord,
  640. SERVICE_NO_CHANGE,
  641. SERVICE_NO_CHANGE,
  642. SERVICE_NO_CHANGE,
  643. NULL,
  644. (LPBYTE) CurrentDependencies);
  645. if (status != NO_ERROR) {
  646. SC_LOG1(ERROR,
  647. "ScValidateAndChangeAccount: ScUpdateServiceRecordConfig "
  648. "FAILED %d\n",
  649. status);
  650. }
  651. LocalFree(CurrentDependencies);
  652. }
  653. else {
  654. SC_LOG1(ERROR,
  655. "ScValidateAndChangeAccount: ScReadDependencies "
  656. "FAILED %d\n",
  657. status);
  658. }
  659. }
  660. if (fIsUPN) {
  661. LocalFree(lpNameToParse);
  662. }
  663. LocalFree(DomainName);
  664. return status;
  665. }
  666. VOID
  667. ScRemoveAccount(
  668. IN LPWSTR ServiceName
  669. )
  670. {
  671. (void) ScDeletePassword(ServiceName);
  672. }
  673. DWORD
  674. ScCanonAccountName(
  675. IN LPWSTR AccountName,
  676. OUT LPWSTR *CanonAccountName
  677. )
  678. /*++
  679. Routine Description:
  680. This function canonicalizes the account name and allocates the
  681. returned buffer for returning the canonicalized string.
  682. AccountName *CanonAccountName
  683. ----------- -----------------
  684. .\UserName .\UserName
  685. ComputerName\UserName .\UserName
  686. LocalSystem LocalSystem
  687. .\LocalSystem LocalSystem
  688. ComputerName\LocalSystem LocalSystem
  689. DomainName\UserName DomainName\UserName
  690. DomainName\LocalSystem Error!
  691. UPN (foo@bar) UPN (foo@bar)
  692. Caller must free the CanonAccountName pointer with LocalFree when done.
  693. Arguments:
  694. AccountName - Supplies a pointer to the account name.
  695. CanonAccountName - Receives a pointer to the buffer (allocated by this
  696. routine) which contains the canonicalized account name. Must
  697. free this pointer with LocalFree.
  698. Return Value:
  699. NO_ERROR - Successful canonicalization.
  700. ERROR_NOT_ENOUGH_MEMORY - Out of memory trying to allocate CanonAccountName
  701. buffer.
  702. ERROR_INVALID_SERVICE_ACCOUNT - Invalid account name.
  703. --*/
  704. {
  705. LPWSTR BufPtr = wcschr(AccountName, SCDOMAIN_USERNAME_SEPARATOR);
  706. //
  707. // Allocate buffer for receiving the canonicalized account name.
  708. //
  709. if ((*CanonAccountName = (LPWSTR)LocalAlloc(
  710. 0,
  711. WCSSIZE(AccountName) +
  712. ScComputerName.MaximumLength
  713. )) == NULL) {
  714. SC_LOG1(ERROR, "ScCanonAccountName: LocalAlloc failed %lu\n",
  715. GetLastError());
  716. return ERROR_NOT_ENOUGH_MEMORY;
  717. }
  718. if (BufPtr == NULL) {
  719. //
  720. // Backslash is not found.
  721. //
  722. if (_wcsicmp(AccountName, SC_LOCAL_SYSTEM_USER_NAME) == 0
  723. ||
  724. wcschr(AccountName, SC_UPN_SYMBOL) != NULL)
  725. {
  726. //
  727. // Account name is LocalSystem or a UPN
  728. //
  729. wcscpy(*CanonAccountName, AccountName);
  730. return NO_ERROR;
  731. }
  732. else {
  733. //
  734. // The AccountName is neither LocalSystem nor a UPN -- invalid.
  735. //
  736. SC_LOG1(ERROR,
  737. "Account name %ws is not LocalSystem and has no \\ or @\n",
  738. AccountName);
  739. LocalFree(*CanonAccountName);
  740. *CanonAccountName = NULL;
  741. return ERROR_INVALID_SERVICE_ACCOUNT;
  742. }
  743. }
  744. //
  745. // BufPtr points to the first occurrence of backslash in
  746. // AccountName.
  747. //
  748. //
  749. // If first portion of the AccountName matches ".\" or "ComputerName\"
  750. //
  751. if ((wcsncmp(AccountName, L".\\", 2) == 0) ||
  752. ((_wcsnicmp(AccountName, ScComputerName.Buffer,
  753. ScComputerName.Length / sizeof(WCHAR)) == 0) &&
  754. ((LPWSTR) ((DWORD_PTR) AccountName + ScComputerName.Length) == BufPtr))) {
  755. if (_wcsicmp(BufPtr + 1, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
  756. //
  757. // .\LocalSystem -> LocalSystem OR
  758. // Computer\LocalSystem -> LocalSystem
  759. //
  760. wcscpy(*CanonAccountName, SC_LOCAL_SYSTEM_USER_NAME);
  761. return NO_ERROR;
  762. }
  763. //
  764. // .\XXX -> .\XXX
  765. // ComputerName\XXX -> .\XXX
  766. //
  767. wcscpy(*CanonAccountName, SC_LOCAL_DOMAIN_NAME);
  768. wcscat(*CanonAccountName, BufPtr);
  769. return NO_ERROR;
  770. }
  771. //
  772. // First portion of the AccountName specifies a domain name other than
  773. // the local one. This domain name will be validated later in
  774. // ScValidateAndSaveAccount.
  775. //
  776. if (_wcsicmp(BufPtr + 1, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
  777. //
  778. // XXX\LocalSystem is invalid.
  779. //
  780. LocalFree(*CanonAccountName);
  781. *CanonAccountName = NULL;
  782. SC_LOG0(ERROR, "Account name is LocalSystem but is not local\n");
  783. return ERROR_INVALID_SERVICE_ACCOUNT;
  784. }
  785. wcscpy(*CanonAccountName, AccountName);
  786. return NO_ERROR;
  787. }
  788. BOOL
  789. GetDefaultDomainName(
  790. LPWSTR DomainName
  791. )
  792. {
  793. OBJECT_ATTRIBUTES ObjectAttributes;
  794. NTSTATUS NtStatus;
  795. LSA_HANDLE LsaPolicyHandle = NULL;
  796. PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo = NULL;
  797. //
  798. // Open a handle to the local machine's LSA policy object.
  799. //
  800. InitializeObjectAttributes( &ObjectAttributes, // object attributes
  801. NULL, // name
  802. 0L, // attributes
  803. NULL, // root directory
  804. NULL ); // security descriptor
  805. NtStatus = LsaOpenPolicy( NULL, // system name
  806. &ObjectAttributes, // object attributes
  807. POLICY_EXECUTE, // access mask
  808. &LsaPolicyHandle ); // policy handle
  809. if( !NT_SUCCESS( NtStatus ) )
  810. {
  811. return(FALSE);
  812. }
  813. //
  814. // Query the domain information from the policy object.
  815. //
  816. NtStatus = LsaQueryInformationPolicy( LsaPolicyHandle,
  817. PolicyAccountDomainInformation,
  818. (PVOID *) &DomainInfo );
  819. if (!NT_SUCCESS(NtStatus))
  820. {
  821. LsaClose(LsaPolicyHandle);
  822. return(FALSE);
  823. }
  824. (void) LsaClose(LsaPolicyHandle);
  825. //
  826. // Copy the domain name into our cache, and
  827. //
  828. CopyMemory( DomainName,
  829. DomainInfo->DomainName.Buffer,
  830. DomainInfo->DomainName.Length );
  831. //
  832. // Null terminate it appropriately
  833. //
  834. DomainName[DomainInfo->DomainName.Length / sizeof(WCHAR)] = L'\0';
  835. //
  836. // Clean up
  837. //
  838. LsaFreeMemory( (PVOID)DomainInfo );
  839. return TRUE;
  840. }
  841. DWORD
  842. ScLookupAccount(
  843. IN LPWSTR AccountName,
  844. OUT LPWSTR *DomainName,
  845. OUT LPWSTR *UserName
  846. )
  847. /*++
  848. Routine Description:
  849. This function calls LsaLookupNames to see if the specified username
  850. exists in the specified domain name. If this function returns
  851. NO_ERROR, DomainName and UserName pointers will be set to the
  852. domain name and username strings in the buffer allocated by this
  853. function.
  854. The caller must free the returned buffer by calling LocalFree
  855. on the pointer returned in DomainName.
  856. Arguments:
  857. AccountName - Supplies the account name in the format of
  858. DomainName\UserName to look up.
  859. DomainName - Receives a pointer to the allocated buffer which
  860. contains the NULL-terminated domain name string, followed
  861. by the NULL-terminated user name string.
  862. UserName - Receives a pointer to the username in the returned
  863. buffer allocated by this routine.
  864. Return Value:
  865. NO_ERROR - UserName is found in the DomainName.
  866. ERROR_NOT_ENOUGH_MEMORY - Failed to allocate work buffer.
  867. ERROR_INVALID_SERVICE_ACCOUNT - any other error that is encountered
  868. in this function.
  869. --*/
  870. {
  871. DWORD status;
  872. NTSTATUS ntstatus;
  873. LSA_HANDLE PolicyHandle;
  874. UNICODE_STRING AccountNameString;
  875. PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains;
  876. PLSA_TRANSLATED_SID Sids;
  877. LPWSTR BackSlashPtr;
  878. LPWSTR LocalAccount = NULL;
  879. WCHAR Domain[MAX_COMPUTERNAME_LENGTH+1];
  880. //
  881. // Allocate buffer for separating AccountName into DomainName and
  882. // UserName.
  883. //
  884. if ((*DomainName = (LPWSTR) LocalAlloc(
  885. 0,
  886. WCSSIZE(AccountName)
  887. )) == NULL) {
  888. SC_LOG1(ERROR, "ScLookupAccount: LocalAlloc failed %lu\n",
  889. GetLastError());
  890. return ERROR_NOT_ENOUGH_MEMORY;
  891. }
  892. //
  893. // Find the backslash character in the specified account name
  894. //
  895. wcscpy(*DomainName, AccountName);
  896. BackSlashPtr = wcschr(*DomainName, SCDOMAIN_USERNAME_SEPARATOR);
  897. if (BackSlashPtr == NULL) {
  898. SC_LOG0(ERROR, "ScLookupAccount: No backslash in account name!\n");
  899. ScLogEvent(NEVENT_BAD_ACCOUNT_NAME);
  900. SC_ASSERT(FALSE);
  901. status = ERROR_GEN_FAILURE;
  902. goto CleanExit;
  903. }
  904. *UserName = BackSlashPtr + 1; // Skip the backslash
  905. if (_wcsnicmp(*DomainName, SC_LOCAL_DOMAIN_NAME, wcslen(SC_LOCAL_DOMAIN_NAME)) == 0) {
  906. //
  907. // DomainName is "." (local domain), so convert "." to the
  908. // local domain name, which on WinNT systems is the computername,
  909. // and on Adv Server systems it's the account domain name.
  910. //
  911. //
  912. // Allocate buffer to hold Domain\UserName string.
  913. //
  914. //
  915. // this code does not use the global var LocalDomain because it
  916. // contains invalid data during gui mode setup. if a service
  917. // that needs an account/password is created during gui mode
  918. // setup, the create would fail with the LocalDomain value. calling
  919. // the GetDefaultDomainName funtion guarantees that we have
  920. // the correct value in all cases.
  921. //
  922. if (!GetDefaultDomainName( Domain )) {
  923. SC_LOG0( ERROR, "ScLookupAccount: GetDefaultDomainName failed\n");
  924. status = ERROR_GEN_FAILURE;
  925. goto CleanExit;
  926. }
  927. if ((LocalAccount = (LPWSTR) LocalAlloc(
  928. LMEM_ZEROINIT,
  929. WCSSIZE(Domain) + WCSSIZE(*UserName)
  930. )) == NULL)
  931. {
  932. SC_LOG1(ERROR, "ScLookupAccount: LocalAlloc failed %lu\n", GetLastError());
  933. status = ERROR_NOT_ENOUGH_MEMORY;
  934. goto CleanExit;
  935. }
  936. wcscpy( LocalAccount, Domain );
  937. wcscat( LocalAccount, BackSlashPtr );
  938. RtlInitUnicodeString( &AccountNameString, LocalAccount );
  939. }
  940. else {
  941. //
  942. // Lookup the domain-qualified name.
  943. //
  944. RtlInitUnicodeString(&AccountNameString, *DomainName);
  945. }
  946. //
  947. // Open a handle to the local security policy.
  948. //
  949. if (ScOpenPolicy(
  950. POLICY_LOOKUP_NAMES |
  951. POLICY_VIEW_LOCAL_INFORMATION,
  952. &PolicyHandle
  953. ) != NO_ERROR) {
  954. SC_LOG0(ERROR, "ScLookupAccount: ScOpenPolicy failed\n");
  955. status = ERROR_INVALID_SERVICE_ACCOUNT;
  956. goto CleanExit;
  957. }
  958. ntstatus = LsaLookupNames(
  959. PolicyHandle,
  960. 1,
  961. &AccountNameString,
  962. &ReferencedDomains,
  963. &Sids
  964. );
  965. if (! NT_SUCCESS(ntstatus)) {
  966. SC_LOG1(ERROR,
  967. "ScLookupAccount: LsaLookupNames returned " FORMAT_NTSTATUS "\n",
  968. ntstatus);
  969. (void) LsaClose(PolicyHandle);
  970. status = ERROR_INVALID_SERVICE_ACCOUNT;
  971. goto CleanExit;
  972. }
  973. //
  974. // Don't need PolicyHandle anymore
  975. //
  976. (void) LsaClose(PolicyHandle);
  977. //
  978. // Free the returned SIDs since we don't look at them.
  979. //
  980. if (Sids != NULL) {
  981. LsaFreeMemory((PVOID) Sids);
  982. }
  983. if (ReferencedDomains == NULL) {
  984. SC_LOG1(ERROR, "ScLookupAccount: Did not find " FORMAT_LPWSTR
  985. " in any domain\n", AccountNameString.Buffer);
  986. status = ERROR_INVALID_SERVICE_ACCOUNT;
  987. goto CleanExit;
  988. }
  989. else {
  990. LsaFreeMemory((PVOID) ReferencedDomains);
  991. }
  992. status = NO_ERROR;
  993. //
  994. // Convert DomainName\UserName into DomainName0UserName.
  995. //
  996. *BackSlashPtr = 0;
  997. CleanExit:
  998. LocalFree(LocalAccount);
  999. if (status != NO_ERROR) {
  1000. LocalFree(*DomainName);
  1001. *DomainName = NULL;
  1002. }
  1003. return status;
  1004. }
  1005. DWORD
  1006. ScSetPassword(
  1007. IN LPWSTR ServiceName,
  1008. IN LPWSTR Password
  1009. )
  1010. /*++
  1011. Routine Description:
  1012. This function sets the secret object for the service with the specified
  1013. password. If the secret object doesn't already exist, it is created.
  1014. Arguments:
  1015. ServiceName - Supplies the service name which is part of the secret
  1016. object name to be created.
  1017. Password - Supplies the user specified password for an account.
  1018. Return Value:
  1019. NO_ERROR - Secret object for the password is created and set with new value.
  1020. ERROR_INVALID_SERVICE_ACCOUNT - for any error encountered in this
  1021. function. The true error is written to the event log.
  1022. --*/
  1023. {
  1024. DWORD status;
  1025. NTSTATUS ntstatus;
  1026. LSA_HANDLE PolicyHandle;
  1027. LPWSTR LsaSecretName;
  1028. UNICODE_STRING SecretNameString;
  1029. UNICODE_STRING NewPasswordString;
  1030. //
  1031. // Open a handle to the local security policy.
  1032. //
  1033. if (ScOpenPolicy(
  1034. POLICY_CREATE_SECRET,
  1035. &PolicyHandle
  1036. ) != NO_ERROR) {
  1037. SC_LOG0(ERROR, "ScSetPassword: ScOpenPolicy failed\n");
  1038. return ERROR_INVALID_SERVICE_ACCOUNT;
  1039. }
  1040. //
  1041. // Create the secret object. But first, let's form a secret
  1042. // name that is very difficult to guess.
  1043. //
  1044. if ((status = ScFormSecretName(
  1045. ServiceName,
  1046. &LsaSecretName
  1047. )) != NO_ERROR) {
  1048. LsaClose(PolicyHandle);
  1049. return status;
  1050. }
  1051. //
  1052. // Serialize secret object operations
  1053. //
  1054. if (WaitForSingleObject(ScSecretObjectsMutex, INFINITE) == MAXULONG) {
  1055. status = GetLastError();
  1056. SC_LOG1(ERROR, "ScSetPassword: WaitForSingleObject failed "
  1057. FORMAT_DWORD "\n", status);
  1058. LocalFree(LsaSecretName);
  1059. (void) LsaClose(PolicyHandle);
  1060. return status;
  1061. }
  1062. RtlInitUnicodeString(&SecretNameString, LsaSecretName);
  1063. RtlInitUnicodeString(&NewPasswordString, Password);
  1064. ntstatus = LsaStorePrivateData(
  1065. PolicyHandle,
  1066. &SecretNameString,
  1067. &NewPasswordString
  1068. );
  1069. if (NT_SUCCESS(ntstatus)) {
  1070. SC_LOG1(ACCOUNT, "ScSetPassword " FORMAT_LPWSTR " success\n",
  1071. ServiceName);
  1072. status = NO_ERROR;
  1073. }
  1074. else {
  1075. SC_LOG2(ERROR,
  1076. "ScSetPassword: LsaStorePrivateData returned " FORMAT_NTSTATUS
  1077. " for " FORMAT_LPWSTR "\n", ntstatus, LsaSecretName);
  1078. //
  1079. // The ntstatus code was not mapped to a windows error because it wasn't
  1080. // clear if all the mappings made sense, and the feeling was that
  1081. // information would be lost during the mapping.
  1082. //
  1083. ScLogEvent(
  1084. NEVENT_CALL_TO_FUNCTION_FAILED,
  1085. SC_LSA_STOREPRIVATEDATA,
  1086. ntstatus);
  1087. status = ERROR_INVALID_SERVICE_ACCOUNT;
  1088. }
  1089. LocalFree(LsaSecretName);
  1090. (void) LsaClose(PolicyHandle);
  1091. (void) ReleaseMutex(ScSecretObjectsMutex);
  1092. return status;
  1093. }
  1094. DWORD
  1095. ScDeletePassword(
  1096. IN LPWSTR ServiceName
  1097. )
  1098. /*++
  1099. Routine Description:
  1100. This function deletes the LSA secret object whose name is derived
  1101. from the specified ServiceName.
  1102. Arguments:
  1103. ServiceName - Supplies the service name which is part of the secret
  1104. object name to be deleted.
  1105. Return Value:
  1106. NO_ERROR - Secret object for password is deleted.
  1107. ERROR_INVALID_SERVICE_ACCOUNT - for any error encountered in this
  1108. function. The true error is written to the event log.
  1109. --*/
  1110. {
  1111. DWORD status;
  1112. NTSTATUS ntstatus;
  1113. LSA_HANDLE PolicyHandle;
  1114. UNICODE_STRING SecretNameString;
  1115. LPWSTR LsaSecretName;
  1116. //
  1117. // Open a handle to the local security policy.
  1118. //
  1119. if (ScOpenPolicy(
  1120. POLICY_VIEW_LOCAL_INFORMATION,
  1121. &PolicyHandle
  1122. ) != NO_ERROR) {
  1123. SC_LOG0(ERROR, "ScDeletePassword: ScOpenPolicy failed\n");
  1124. return ERROR_INVALID_SERVICE_ACCOUNT;
  1125. }
  1126. //
  1127. // Get the secret object name from the specified service name.
  1128. //
  1129. if ((status = ScFormSecretName(
  1130. ServiceName,
  1131. &LsaSecretName
  1132. )) != NO_ERROR) {
  1133. (void) LsaClose(PolicyHandle);
  1134. return status;
  1135. }
  1136. //
  1137. // Serialize secret object operations
  1138. //
  1139. if (WaitForSingleObject(ScSecretObjectsMutex, INFINITE) == MAXULONG) {
  1140. status = GetLastError();
  1141. SC_LOG1(ERROR, "ScDeletePassword: WaitForSingleObject failed "
  1142. FORMAT_DWORD "\n", status);
  1143. LocalFree(LsaSecretName);
  1144. LsaClose(PolicyHandle);
  1145. return status;
  1146. }
  1147. RtlInitUnicodeString(&SecretNameString, LsaSecretName);
  1148. ntstatus = LsaStorePrivateData(
  1149. PolicyHandle,
  1150. &SecretNameString,
  1151. NULL
  1152. );
  1153. //
  1154. // Treat STATUS_OBJECT_NAME_NOT_FOUND as success since the
  1155. // password's already deleted (effectively) in that case.
  1156. //
  1157. if (NT_SUCCESS(ntstatus) || (ntstatus == STATUS_OBJECT_NAME_NOT_FOUND))
  1158. {
  1159. SC_LOG1(ACCOUNT, "ScDeletePassword " FORMAT_LPWSTR " success\n",
  1160. ServiceName);
  1161. status = NO_ERROR;
  1162. }
  1163. else
  1164. {
  1165. SC_LOG2(ERROR,
  1166. "ScDeletePassword: LsaStorePrivateData returned " FORMAT_NTSTATUS
  1167. " for " FORMAT_LPWSTR "\n", ntstatus, LsaSecretName);
  1168. //
  1169. // The ntstatus code was not mapped to a windows error because it wasn't
  1170. // clear if all the mappings made sense, and the feeling was that
  1171. // information would be lost during the mapping.
  1172. //
  1173. ScLogEvent(
  1174. NEVENT_CALL_TO_FUNCTION_FAILED,
  1175. SC_LSA_STOREPRIVATEDATA,
  1176. ntstatus);
  1177. status = ERROR_INVALID_SERVICE_ACCOUNT;
  1178. }
  1179. LocalFree(LsaSecretName);
  1180. LsaClose(PolicyHandle);
  1181. ReleaseMutex(ScSecretObjectsMutex);
  1182. return status;
  1183. }
  1184. DWORD
  1185. ScOpenPolicy(
  1186. IN ACCESS_MASK DesiredAccess,
  1187. OUT LSA_HANDLE *PolicyHandle
  1188. )
  1189. /*++
  1190. Routine Description:
  1191. This function gets a handle to the local security policy by calling
  1192. LsaOpenPolicy.
  1193. Arguments:
  1194. DesiredAccess - Supplies the desired access to the local security
  1195. policy.
  1196. PolicyHandle - Receives a handle to the opened policy.
  1197. Return Value:
  1198. NO_ERROR - Policy handle is returned.
  1199. ERROR_INVALID_SERVICE_ACCOUNT - for any error encountered in this
  1200. function.
  1201. --*/
  1202. {
  1203. NTSTATUS ntstatus;
  1204. OBJECT_ATTRIBUTES ObjAttributes;
  1205. //
  1206. // Open a handle to the local security policy. Initialize the
  1207. // objects attributes structure first.
  1208. //
  1209. InitializeObjectAttributes(
  1210. &ObjAttributes,
  1211. NULL,
  1212. 0L,
  1213. NULL,
  1214. NULL
  1215. );
  1216. ntstatus = LsaOpenPolicy(
  1217. NULL,
  1218. &ObjAttributes,
  1219. DesiredAccess,
  1220. PolicyHandle
  1221. );
  1222. if (! NT_SUCCESS(ntstatus)) {
  1223. SC_LOG1(ERROR,
  1224. "ScOpenPolicy: LsaOpenPolicy returned " FORMAT_NTSTATUS "\n",
  1225. ntstatus);
  1226. //
  1227. // The ntstatus code was not mapped to a windows error because it wasn't
  1228. // clear if all the mappings made sense, and the feeling was that
  1229. // information would be lost during the mapping.
  1230. //
  1231. ScLogEvent(
  1232. NEVENT_CALL_TO_FUNCTION_FAILED,
  1233. SC_LSA_OPENPOLICY,
  1234. ntstatus
  1235. );
  1236. return ERROR_INVALID_SERVICE_ACCOUNT;
  1237. }
  1238. return NO_ERROR;
  1239. }
  1240. DWORD
  1241. ScFormSecretName(
  1242. IN LPWSTR ServiceName,
  1243. OUT LPWSTR *LsaSecretName
  1244. )
  1245. /*++
  1246. Routine Description:
  1247. This function creates a secret name from the service name.
  1248. It also allocates the buffer to return the created secret name which
  1249. must be freed by the caller using LocalFree when done with it.
  1250. Arguments:
  1251. ServiceName - Supplies the service name which is part of the secret
  1252. object name we are creating.
  1253. LsaSecretName - Receives a pointer to the buffer which contains the
  1254. secret object name.
  1255. Return Value:
  1256. NO_ERROR - Successfully returned secret name.
  1257. ERROR_NOT_ENOUGH_MEMORY - Failed to allocate buffer to hold the secret
  1258. name.
  1259. --*/
  1260. {
  1261. if ((*LsaSecretName = (LPWSTR)LocalAlloc(
  1262. 0,
  1263. (wcslen(SC_SECRET_PREFIX) +
  1264. wcslen(ServiceName) +
  1265. 1) * sizeof(WCHAR)
  1266. )) == NULL) {
  1267. SC_LOG1(ERROR, "ScFormSecretName: LocalAlloc failed %lu\n",
  1268. GetLastError());
  1269. return ERROR_NOT_ENOUGH_MEMORY;
  1270. }
  1271. wcscpy(*LsaSecretName, SC_SECRET_PREFIX);
  1272. wcscat(*LsaSecretName, ServiceName);
  1273. return NO_ERROR;
  1274. }
  1275. DWORD
  1276. ScLookupServiceAccount(
  1277. IN LPWSTR ServiceName,
  1278. OUT LPWSTR *AccountName
  1279. )
  1280. /*++
  1281. Routine Description:
  1282. This function looks up the service account from the registry.
  1283. Arguments:
  1284. ServiceName - Supplies the service name to logon.
  1285. AccountName - Receives a pointer to a string containing the name of
  1286. the account that the service is configured to logon under. The
  1287. pointer returned is NULL if the service account is LocalSystem.
  1288. Otherwise the string is in the form .\UserName or
  1289. DomainName\UserName where DomainName != the local computername.
  1290. It must be freed with LocalAlloc when done.
  1291. Return Value:
  1292. NO_ERROR - Secret object for password is changed to new value.
  1293. ERROR_INVALID_SERVICE_ACCOUNT - The account name obtained from the
  1294. registry is invalid.
  1295. Other errors from registry APIs.
  1296. --*/
  1297. {
  1298. DWORD status;
  1299. HKEY ServiceNameKey;
  1300. LPWSTR DomainName = NULL;
  1301. LPWSTR UserName;
  1302. LPWSTR Separator;
  1303. LPWSTR lpNameToParse;
  1304. *AccountName = NULL;
  1305. //
  1306. // Open the service name key.
  1307. //
  1308. status = ScOpenServiceConfigKey(
  1309. ServiceName,
  1310. KEY_READ,
  1311. FALSE, // Create if missing
  1312. &ServiceNameKey
  1313. );
  1314. if (status != NO_ERROR) {
  1315. return status;
  1316. }
  1317. //
  1318. // Read the account name from the registry.
  1319. //
  1320. status = ScReadStartName(
  1321. ServiceNameKey,
  1322. &lpNameToParse
  1323. );
  1324. ScRegCloseKey(ServiceNameKey);
  1325. if (status != NO_ERROR) {
  1326. return status;
  1327. }
  1328. if (lpNameToParse == NULL) {
  1329. return ERROR_INVALID_SERVICE_ACCOUNT;
  1330. }
  1331. //
  1332. // Check if the account name is LocalSystem.
  1333. //
  1334. if (_wcsicmp(lpNameToParse, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
  1335. LocalFree(lpNameToParse);
  1336. return NO_ERROR;
  1337. }
  1338. //
  1339. // If it isn't LocalSystem, it must be in the form
  1340. // Domain\User or .\User.
  1341. //
  1342. Separator = wcsrchr(lpNameToParse, SCDOMAIN_USERNAME_SEPARATOR);
  1343. if (Separator == NULL) {
  1344. if (wcsrchr(lpNameToParse, SC_UPN_SYMBOL) != NULL) {
  1345. //
  1346. // It's a UPN -- crack it
  1347. //
  1348. status = ScUPNToAccountName(lpNameToParse, &DomainName);
  1349. LocalFree(lpNameToParse);
  1350. if (status != NO_ERROR
  1351. ||
  1352. (Separator = wcschr(DomainName, SCDOMAIN_USERNAME_SEPARATOR)) == NULL)
  1353. {
  1354. SC_LOG1(ERROR,
  1355. "ScLookupServiceAccount: ScUPNToAccountName failed %d\n",
  1356. status);
  1357. if (status == NO_ERROR) {
  1358. SC_LOG1(ACCOUNT,
  1359. "Cracked account name was %ws\n",
  1360. DomainName);
  1361. }
  1362. LocalFree(DomainName);
  1363. return ERROR_INVALID_SERVICE_ACCOUNT;
  1364. }
  1365. }
  1366. else {
  1367. SC_LOG1(ERROR,
  1368. "ScLookupServiceAccount: No \\ or @ in account name %ws\n",
  1369. lpNameToParse);
  1370. LocalFree(lpNameToParse);
  1371. return ERROR_INVALID_SERVICE_ACCOUNT;
  1372. }
  1373. }
  1374. else {
  1375. DomainName = lpNameToParse;
  1376. }
  1377. *Separator = 0;
  1378. UserName = Separator + 1;
  1379. //
  1380. // Translate ComputerName into . (to facilitate subsequent comparison
  1381. // of account names)
  1382. //
  1383. if (_wcsicmp(DomainName, ScComputerName.Buffer) == 0)
  1384. {
  1385. WCHAR *Dest, *Src;
  1386. // Assumption: "." is no longer than any computer name
  1387. SC_ASSERT(wcslen(SC_LOCAL_DOMAIN_NAME) == 1 &&
  1388. wcslen(DomainName) >= 1);
  1389. wcscpy(DomainName, SC_LOCAL_DOMAIN_NAME);
  1390. Separator = DomainName + wcslen(SC_LOCAL_DOMAIN_NAME);
  1391. // Shift username left
  1392. Src = UserName;
  1393. UserName = Separator + 1;
  1394. Dest = UserName;
  1395. while (*Dest++ = *Src++)
  1396. ;
  1397. }
  1398. //
  1399. // Check if the user name is LocalSystem
  1400. //
  1401. if (_wcsicmp(UserName, SC_LOCAL_SYSTEM_USER_NAME) == 0) {
  1402. //
  1403. // This is only acceptable if DomainName is "."
  1404. //
  1405. if (_wcsicmp(DomainName, SC_LOCAL_DOMAIN_NAME) == 0) {
  1406. status = NO_ERROR;
  1407. }
  1408. else {
  1409. status = ERROR_INVALID_SERVICE_ACCOUNT;
  1410. }
  1411. LocalFree(DomainName);
  1412. return status;
  1413. }
  1414. //
  1415. // Restore the "\"
  1416. //
  1417. *Separator = SCDOMAIN_USERNAME_SEPARATOR;
  1418. *AccountName = DomainName;
  1419. return NO_ERROR;
  1420. }
  1421. DWORD
  1422. ScLogonService(
  1423. IN LPWSTR ServiceName,
  1424. IN LPWSTR AccountName,
  1425. OUT LPHANDLE ServiceToken,
  1426. OUT LPHANDLE pProfileHandle OPTIONAL,
  1427. OUT PSID *ServiceSid
  1428. )
  1429. /*++
  1430. Routine Description:
  1431. This function looks up the service account from the registry and
  1432. the password from the secret object to logon the service. If
  1433. successful, the handle to the logon token is returned.
  1434. Arguments:
  1435. ServiceName - Supplies the service name to logon.
  1436. AccountName - Supplies the account name to logon the service under.
  1437. (Supplied as an optimization since ScLookupServiceAccount will
  1438. have been called before calling this routine.) It must be of
  1439. the form .\UserName or DomainName\UserName, where DomainName !=
  1440. the local computer name and UserName != LocalSystem.
  1441. ServiceToken - Receives a handle to the logon token for the
  1442. service. The handle returned is NULL if the service account
  1443. is LocalSystem (i.e. spawn as child process of the service
  1444. controller).
  1445. ServiceSid - Receives a pointer to the logon SID of the service.
  1446. This must be freed with LocalAlloc when done.
  1447. Return Value:
  1448. NO_ERROR - Secret object for password is changed to new value.
  1449. ERROR_SERVICE_LOGON_FAILED - for any error encountered in this
  1450. function.
  1451. --*/
  1452. {
  1453. DWORD status;
  1454. LPWSTR Separator;
  1455. LPWSTR LsaSecretName = NULL;
  1456. *ServiceToken = NULL;
  1457. *ServiceSid = NULL;
  1458. status = ScFormSecretName(ServiceName, &LsaSecretName);
  1459. if (status != NO_ERROR)
  1460. {
  1461. SC_LOG(ERROR, "ScLogonService: ScFormSecretname failed %lu\n", status);
  1462. return ERROR_SERVICE_LOGON_FAILED;
  1463. }
  1464. Separator = wcsrchr(AccountName, SCDOMAIN_USERNAME_SEPARATOR);
  1465. if (Separator == NULL)
  1466. {
  1467. SC_ASSERT(Separator != NULL);
  1468. LocalFree(LsaSecretName);
  1469. return ERROR_INVALID_SERVICE_ACCOUNT;
  1470. }
  1471. *Separator = 0;
  1472. //
  1473. // Get the service token
  1474. //
  1475. if (!LogonUserEx(Separator + 1, // Username
  1476. AccountName, // Domain
  1477. LsaSecretName, // Password
  1478. LOGON32_LOGON_SERVICE, // Logon type
  1479. LOGON32_PROVIDER_DEFAULT, // Default logon provider
  1480. ServiceToken, // Pointer to token handle
  1481. ServiceSid, // Logon Sid
  1482. NULL, // Profile buffer
  1483. NULL, // Length of profile buffer
  1484. NULL)) // Quota limits
  1485. {
  1486. status = GetLastError();
  1487. *Separator = SCDOMAIN_USERNAME_SEPARATOR;
  1488. SC_LOG2(ERROR,
  1489. "ScLogonService: LogonUser for %ws service failed %d\n",
  1490. ServiceName,
  1491. status);
  1492. ScLogEvent(NEVENT_FIRST_LOGON_FAILED_II,
  1493. ServiceName,
  1494. AccountName,
  1495. status);
  1496. goto Cleanup;
  1497. }
  1498. if (ARGUMENT_PRESENT(pProfileHandle))
  1499. {
  1500. //
  1501. // Load the user profile for the service
  1502. // (Errors are written to the event log, but otherwise ignored)
  1503. //
  1504. ScLoadUserProfile(*ServiceToken,
  1505. AccountName, // Domain
  1506. Separator + 1, // Username
  1507. pProfileHandle);
  1508. }
  1509. LocalFree(LsaSecretName);
  1510. *Separator = SCDOMAIN_USERNAME_SEPARATOR;
  1511. return NO_ERROR;
  1512. Cleanup:
  1513. LocalFree(LsaSecretName);
  1514. *Separator = SCDOMAIN_USERNAME_SEPARATOR;
  1515. LocalFree(*ServiceSid);
  1516. *ServiceSid = NULL;
  1517. if (*ServiceToken != NULL)
  1518. {
  1519. CloseHandle(*ServiceToken);
  1520. *ServiceToken = NULL;
  1521. }
  1522. return ERROR_SERVICE_LOGON_FAILED;
  1523. }
  1524. DWORD
  1525. ScGetAccountDomainInfo(
  1526. VOID
  1527. )
  1528. {
  1529. NTSTATUS ntstatus;
  1530. LSA_HANDLE PolicyHandle;
  1531. PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo;
  1532. NT_PRODUCT_TYPE ProductType;
  1533. //
  1534. // Account domain info is cached. Look it up it this is the first
  1535. // time.
  1536. //
  1537. if (ScAccountDomain.Buffer == NULL) {
  1538. if (! RtlGetNtProductType(&ProductType)) {
  1539. SC_LOG1(ERROR, "ScGetAccountDomainInfo: RtlGetNtProductType failed "
  1540. FORMAT_DWORD "\n", GetLastError());
  1541. return ERROR_INVALID_SERVICE_ACCOUNT;
  1542. }
  1543. if (ProductType == NtProductLanManNt) {
  1544. ScLocalDomain = &ScAccountDomain;
  1545. }
  1546. else {
  1547. ScLocalDomain = &ScComputerName;
  1548. }
  1549. //
  1550. // Open a handle to the local security policy.
  1551. //
  1552. if (ScOpenPolicy(
  1553. POLICY_VIEW_LOCAL_INFORMATION,
  1554. &PolicyHandle
  1555. ) != NO_ERROR) {
  1556. SC_LOG0(ERROR, "ScGetAccountDomainInfo: ScOpenPolicy failed\n");
  1557. return ERROR_INVALID_SERVICE_ACCOUNT;
  1558. }
  1559. //
  1560. // Get the name of the account domain from LSA if we have
  1561. // not done it already.
  1562. //
  1563. ntstatus = LsaQueryInformationPolicy(
  1564. PolicyHandle,
  1565. PolicyAccountDomainInformation,
  1566. (PVOID *) &AccountDomainInfo
  1567. );
  1568. if (! NT_SUCCESS(ntstatus)) {
  1569. SC_LOG1(ERROR, "ScGetAccountDomainInfo: LsaQueryInformationPolicy failed "
  1570. FORMAT_NTSTATUS "\n", ntstatus);
  1571. (void) LsaClose(PolicyHandle);
  1572. return ERROR_INVALID_SERVICE_ACCOUNT;
  1573. }
  1574. (void) LsaClose(PolicyHandle);
  1575. if ((ScAccountDomain.Buffer = (LPWSTR)LocalAlloc(
  1576. LMEM_ZEROINIT,
  1577. (UINT) (AccountDomainInfo->DomainName.Length +
  1578. sizeof(WCHAR))
  1579. )) == NULL) {
  1580. (void) LsaFreeMemory((PVOID) AccountDomainInfo);
  1581. return ERROR_NOT_ENOUGH_MEMORY;
  1582. }
  1583. ScAccountDomain.MaximumLength = (USHORT) (AccountDomainInfo->DomainName.Length +
  1584. sizeof(WCHAR));
  1585. RtlCopyUnicodeString(&ScAccountDomain, &AccountDomainInfo->DomainName);
  1586. SC_LOG1(ACCOUNT, "ScGetAccountDomainInfo got " FORMAT_LPWSTR "\n",
  1587. ScAccountDomain.Buffer);
  1588. (void) LsaFreeMemory((PVOID) AccountDomainInfo);
  1589. }
  1590. return NO_ERROR;
  1591. }
  1592. VOID
  1593. ScLoadUserProfile(
  1594. IN HANDLE LogonToken,
  1595. IN LPWSTR DomainName,
  1596. IN LPWSTR UserName,
  1597. OUT PHANDLE pProfileHandle
  1598. )
  1599. /*++
  1600. Routine Description:
  1601. This function loads the user profile for the account that a service
  1602. process will run under, so that the process has an HKEY_CURRENT_USER.
  1603. Arguments:
  1604. LogonToken - The token handle returned by LogonUser.
  1605. UserName - The account's user name. (Used by LoadUserProfile to
  1606. generate a profile directory name.)
  1607. pProfileHandle - A handle to the profile is returned here. It must
  1608. be closed by calling UnloadUserProfile after the service process
  1609. exits.
  1610. Return Value:
  1611. None. Errors from LoadUserProfile are written to the event log.
  1612. --*/
  1613. {
  1614. PROFILEINFO ProfileInfo =
  1615. {
  1616. sizeof(ProfileInfo), // dwSize
  1617. PI_NOUI, // dwFlags - no UI
  1618. UserName, // lpUserName (used for dir name)
  1619. NULL, // lpProfilePath
  1620. NULL, // lpDefaultPath
  1621. NULL, // lpServerName (used to get group info - N/A)
  1622. NULL, // lpPolicyPath
  1623. NULL // hProfile (filled in by LoadUserProfile)
  1624. };
  1625. SC_ASSERT(pProfileHandle != NULL);
  1626. if (_wcsicmp(DomainName, SC_LOCAL_NTAUTH_NAME) == 0)
  1627. {
  1628. //
  1629. // Hide LocalService/NetworkService profiles from the Admin
  1630. // (i.e., they won't show up via "dir", etc).
  1631. //
  1632. ProfileInfo.dwFlags |= PI_HIDEPROFILE;
  1633. }
  1634. //
  1635. // NOTE: This ignores a service with a roaming profile. May need
  1636. // to use the profile path from the LogonUserEx call.
  1637. //
  1638. if (LoadUserProfile(LogonToken, &ProfileInfo))
  1639. {
  1640. SC_ASSERT(ProfileInfo.hProfile != NULL);
  1641. *pProfileHandle = ProfileInfo.hProfile;
  1642. }
  1643. else
  1644. {
  1645. DWORD Error = GetLastError();
  1646. SC_LOG(ERROR, "LoadUserProfile failed %lu\n", Error);
  1647. ScLogEvent(
  1648. NEVENT_CALL_TO_FUNCTION_FAILED,
  1649. SC_LOAD_USER_PROFILE,
  1650. Error);
  1651. *pProfileHandle = NULL;
  1652. }
  1653. }
  1654. DWORD
  1655. ScUPNToAccountName(
  1656. IN LPWSTR lpUPN,
  1657. OUT LPWSTR *ppAccountName
  1658. )
  1659. /*++
  1660. Routine Description:
  1661. This function attempts to convert a UPN into Domain\User
  1662. Arguments:
  1663. lpUPN - The UPN
  1664. ppAccountName - Pointer to the location to create/copy the account name
  1665. Return Value:
  1666. NO_ERROR -- Success (ppAccountName contains the converted UPN)
  1667. Any other Win32 error -- error at some stage of conversion
  1668. --*/
  1669. {
  1670. DWORD dwError;
  1671. HANDLE hDS;
  1672. PDS_NAME_RESULT pdsResult;
  1673. SC_ASSERT(ppAccountName != NULL);
  1674. SC_LOG1(ACCOUNT, "ScUPNToAccountName: Converting %ws\n", lpUPN);
  1675. //
  1676. // Get a binding handle to the DS
  1677. //
  1678. dwError = DsBind(NULL, NULL, &hDS);
  1679. if (dwError != NO_ERROR)
  1680. {
  1681. SC_LOG1(ERROR, "ScUPNToAccountName: DsBind failed %d\n", dwError);
  1682. return dwError;
  1683. }
  1684. dwError = DsCrackNames(hDS, // Handle to the DS
  1685. DS_NAME_NO_FLAGS, // No parsing flags
  1686. DS_USER_PRINCIPAL_NAME, // We have a UPN
  1687. DS_NT4_ACCOUNT_NAME, // We want Domain\User
  1688. 1, // Number of names to crack
  1689. &lpUPN, // Array of name(s)
  1690. &pdsResult); // Filled in by API
  1691. if (dwError != NO_ERROR)
  1692. {
  1693. SC_LOG1(ERROR,
  1694. "ScUPNToAccountName: DsCrackNames failed %d\n",
  1695. dwError);
  1696. DsUnBind(&hDS);
  1697. return dwError;
  1698. }
  1699. SC_ASSERT(pdsResult->cItems == 1);
  1700. SC_ASSERT(pdsResult->rItems != NULL);
  1701. if (pdsResult->rItems[0].status == DS_NAME_ERROR_DOMAIN_ONLY)
  1702. {
  1703. //
  1704. // Couldn't crack the name but we got the name of
  1705. // the domain where it is -- let's try it
  1706. //
  1707. DsUnBind(&hDS);
  1708. SC_ASSERT(pdsResult->rItems[0].pDomain != NULL);
  1709. SC_LOG1(ACCOUNT,
  1710. "Retrying DsBind on domain %ws\n",
  1711. pdsResult->rItems[0].pDomain);
  1712. dwError = DsBind(NULL, pdsResult->rItems[0].pDomain, &hDS);
  1713. //
  1714. // Free up the structure holding the old info
  1715. //
  1716. DsFreeNameResult(pdsResult);
  1717. if (dwError != NO_ERROR)
  1718. {
  1719. SC_LOG1(ERROR,
  1720. "ScUPNToAccountName: DsBind #2 failed %d\n",
  1721. dwError);
  1722. return dwError;
  1723. }
  1724. dwError = DsCrackNames(hDS, // Handle to the DS
  1725. DS_NAME_NO_FLAGS, // No parsing flags
  1726. DS_USER_PRINCIPAL_NAME, // We have a UPN
  1727. DS_NT4_ACCOUNT_NAME, // We want Domain\User
  1728. 1, // Number of names to crack
  1729. &lpUPN, // Array of name(s)
  1730. &pdsResult); // Filled in by API
  1731. if (dwError != NO_ERROR)
  1732. {
  1733. SC_LOG1(ERROR,
  1734. "ScUPNToAccountName: DsCrackNames #2 failed %d\n",
  1735. dwError);
  1736. DsUnBind(&hDS);
  1737. return dwError;
  1738. }
  1739. SC_ASSERT(pdsResult->cItems == 1);
  1740. SC_ASSERT(pdsResult->rItems != NULL);
  1741. }
  1742. if (pdsResult->rItems[0].status != DS_NAME_NO_ERROR)
  1743. {
  1744. SC_LOG1(ERROR,
  1745. "ScUPNToAccountName: DsCrackNames failure (status %#x)\n",
  1746. pdsResult->rItems[0].status);
  1747. //
  1748. // DS errors don't map to Win32 errors -- this is the best we can do
  1749. //
  1750. dwError = ERROR_INVALID_SERVICE_ACCOUNT;
  1751. }
  1752. else
  1753. {
  1754. *ppAccountName = (LPWSTR)LocalAlloc(
  1755. LPTR,
  1756. (wcslen(pdsResult->rItems[0].pName) + 1) * sizeof(WCHAR));
  1757. if (*ppAccountName != NULL)
  1758. {
  1759. wcscpy(*ppAccountName, pdsResult->rItems[0].pName);
  1760. }
  1761. else
  1762. {
  1763. dwError = GetLastError();
  1764. SC_LOG1(ERROR, "ScUPNToAccountName: LocalAlloc failed %d\n", dwError);
  1765. }
  1766. }
  1767. DsUnBind(&hDS);
  1768. DsFreeNameResult(pdsResult);
  1769. return dwError;
  1770. }