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.

1264 lines
29 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. security.c
  5. Abstract:
  6. Helpers for NT security API.
  7. Author:
  8. Jim Schmidt (jimschm) 05-Feb-1997
  9. Revision History:
  10. ovidiut 14-Mar-2000 Updated CreateLocalAccount for encrypted password feature
  11. jimschm 02-Jun-1999 Added SetRegKeySecurity
  12. jimschm 18-Mar-1998 Updated CreateLocalAccount for random password
  13. feature. Added password change if account
  14. already exists.
  15. --*/
  16. #include "pch.h"
  17. #include "migmainp.h"
  18. #include "security.h"
  19. #include "encrypt.h"
  20. #include <ntdsapi.h>
  21. #include <dsgetdc.h>
  22. #ifndef UNICODE
  23. #error UNICODE definition required for account lookup code
  24. #endif
  25. #define UNDOCUMENTED_UI_FLAG 0x0200
  26. //
  27. // NT 5 - net share-specific flag
  28. //
  29. DWORD
  30. ConvertNetRightsToAccessMask (
  31. IN DWORD Flags
  32. )
  33. /*++
  34. Routine Description:
  35. Routine that converts LAN Man flags into NT security flags.
  36. Arguments:
  37. Flags - Access flags used with NetAccess* APIs
  38. Return value:
  39. A DWORD containing the NT security flags.
  40. --*/
  41. {
  42. DWORD OutFlags;
  43. if (Flags == ACCESS_READ) {
  44. //
  45. // Read only permissions
  46. //
  47. OutFlags = FILE_GENERIC_READ|FILE_GENERIC_EXECUTE;
  48. } else if (Flags == ACCESS_WRITE) {
  49. //
  50. // Change only permission
  51. //
  52. OutFlags = FILE_GENERIC_WRITE|DELETE;
  53. } else if (Flags == (ACCESS_READ|ACCESS_WRITE)) {
  54. //
  55. // Full control permissions
  56. //
  57. OutFlags = FILE_ALL_ACCESS|UNDOCUMENTED_UI_FLAG;
  58. } else {
  59. //
  60. // Unsupported options... disable the share
  61. //
  62. OutFlags = 0;
  63. DEBUGMSG ((DBG_VERBOSE, "Unsupported permission %u was translated to disable permission", Flags));
  64. }
  65. return OutFlags;
  66. }
  67. DWORD
  68. AddAclMember (
  69. IN OUT PGROWBUFFER GrowBuf,
  70. IN PCTSTR UserOrGroup,
  71. IN DWORD Attributes
  72. )
  73. /*++
  74. Routine Description:
  75. Appends user/group account, attributes and enable flag to a list
  76. of members. This funciton is used to build a list of members which
  77. is passed to CreateAclFromMemberList to create an ACL.
  78. Arguments:
  79. GrowBuf - A GROWBUFFER variable that is zero-initialized
  80. UserOrGroup - String specifying user name or group
  81. Attributes - A list of access rights (a combination of flags
  82. from NetAccess* APIs). Currently the only flags
  83. that are used are:
  84. 0 - Deny all access
  85. ACCESS_READ - Read-Only access
  86. ACCESS_WRITE - Change-Only access
  87. ACCESS_READ|ACCESS_WRITE - Full access
  88. Return value:
  89. The number of bytes needed to store UserOrGroup, Attributes and Enabled,
  90. or zero if the function fails. GrowBuf may be expanded to hold the
  91. new data.
  92. GrowBuf must be freed by the caller after the ACL is generated.
  93. --*/
  94. {
  95. DWORD Size;
  96. PACLMEMBER AclMemberPtr;
  97. TCHAR RealName[MAX_USER_NAME];
  98. DWORD OriginalAttribs;
  99. BOOL Everyone;
  100. PCTSTR p;
  101. p = _tcschr (UserOrGroup, TEXT('\\'));
  102. if (p) {
  103. UserOrGroup = _tcsinc (p);
  104. }
  105. if (StringMatch (UserOrGroup, TEXT("*"))) {
  106. _tcssafecpy (RealName, g_EveryoneStr, MAX_USER_NAME);
  107. } else {
  108. _tcssafecpy (RealName, UserOrGroup, MAX_USER_NAME);
  109. }
  110. Everyone = StringIMatch (RealName, g_EveryoneStr);
  111. Size = SizeOfString (RealName) + sizeof (ACLMEMBER);
  112. AclMemberPtr = (PACLMEMBER) GrowBuffer (GrowBuf, Size);
  113. OriginalAttribs = Attributes;
  114. if (!Attributes && !Everyone) {
  115. Attributes = ACCESS_READ|ACCESS_WRITE;
  116. }
  117. AclMemberPtr->Attribs = ConvertNetRightsToAccessMask (Attributes);
  118. AclMemberPtr->Enabled = Everyone || OriginalAttribs != 0;
  119. AclMemberPtr->Failed = FALSE;
  120. StringCopy (AclMemberPtr->UserOrGroup, RealName);
  121. return Size;
  122. }
  123. PACL
  124. CreateAclFromMemberList (
  125. PBYTE AclMemberList,
  126. DWORD MemberCount
  127. )
  128. /*++
  129. Routine Description:
  130. CreateAclFromMemberList takes a member list (prepared by AddAclMember)
  131. and generates an ACL.
  132. Arguments:
  133. AclMemberList - A pointer to the buffer maintained by AddAclMember. This
  134. is usually the Buf member of a GROWBUFFER variable.
  135. MemberCount - The number of members in AclMemberList (i.e. the number of
  136. AddAclMember calls)
  137. Return value:
  138. A pointer to a MemAlloc'd ACL, or NULL if an error occurred. Call
  139. FreeMemberListAcl to free a non-NULL return value.
  140. --*/
  141. {
  142. PACLMEMBER AclMemberPtr;
  143. DWORD AllowedAceCount;
  144. DWORD DeniedAceCount;
  145. DWORD d;
  146. PACL Acl = NULL;
  147. DWORD AclSize;
  148. BOOL b = FALSE;
  149. UINT SidSize = 0;
  150. __try {
  151. //
  152. // Create SID array for all members
  153. //
  154. AclMemberPtr = (PACLMEMBER) AclMemberList;
  155. AllowedAceCount = 0;
  156. DeniedAceCount = 0;
  157. for (d = 0 ; d < MemberCount ; d++) {
  158. AclMemberPtr->Sid = GetSidForUser (AclMemberPtr->UserOrGroup);
  159. if (!AclMemberPtr->Sid) {
  160. // Mark an error
  161. AclMemberPtr->Failed = TRUE;
  162. } else {
  163. // Found SID, adjust ace count and sid size
  164. if (AclMemberPtr->Enabled) {
  165. AllowedAceCount++;
  166. } else {
  167. DeniedAceCount++;
  168. }
  169. SidSize += GetLengthSid (AclMemberPtr->Sid);
  170. }
  171. GetNextAclMember (&AclMemberPtr);
  172. }
  173. //
  174. // Calculate size of ACL (an ACL struct plus the ACEs) and allocate it.
  175. //
  176. // We subtract a DWORD from the struct size because the actual size of all
  177. // SidStart members is given by SidSize.
  178. //
  179. AclSize = sizeof (ACL) +
  180. AllowedAceCount * (sizeof (ACCESS_ALLOWED_ACE) - sizeof (DWORD)) +
  181. DeniedAceCount * (sizeof (ACCESS_DENIED_ACE) - sizeof (DWORD)) +
  182. SidSize;
  183. Acl = (PACL) MemAlloc (g_hHeap, 0, AclSize);
  184. if (!Acl) {
  185. LOG ((LOG_ERROR, "Couldn't allocate an ACL"));
  186. __leave;
  187. }
  188. //
  189. // Create the ACL
  190. //
  191. if (!InitializeAcl (Acl, AclSize, ACL_REVISION)) {
  192. LOG ((LOG_ERROR, "Couldn't initialize ACL"));
  193. __leave;
  194. }
  195. //
  196. // Add the access-denied ACLs first
  197. //
  198. AclMemberPtr = (PACLMEMBER) AclMemberList;
  199. for (d = 0 ; d < MemberCount ; d++) {
  200. if (AclMemberPtr->Failed) {
  201. continue;
  202. }
  203. if (!AclMemberPtr->Enabled) {
  204. if (!AddAccessDeniedAce (
  205. Acl,
  206. ACL_REVISION,
  207. AclMemberPtr->Attribs,
  208. AclMemberPtr->Sid
  209. )) {
  210. LOG ((
  211. LOG_ERROR,
  212. "Couldn't add denied ACE for %s",
  213. AclMemberPtr->UserOrGroup
  214. ));
  215. }
  216. }
  217. GetNextAclMember (&AclMemberPtr);
  218. }
  219. //
  220. // Add the access-enabled ACLs last
  221. //
  222. // Reset the SID pointer because CreateAclFromMemberList is the
  223. // only ones who uses this member
  224. //
  225. AclMemberPtr = (PACLMEMBER) AclMemberList;
  226. for (d = 0 ; d < MemberCount ; d++) {
  227. if (AclMemberPtr->Failed) {
  228. continue;
  229. }
  230. //
  231. // Add member to list
  232. //
  233. if (AclMemberPtr->Enabled) {
  234. if (!AddAccessAllowedAce (
  235. Acl,
  236. ACL_REVISION,
  237. AclMemberPtr->Attribs,
  238. AclMemberPtr->Sid
  239. )) {
  240. LOG ((
  241. LOG_ERROR,
  242. "Couldn't add allowed ACE for %s",
  243. AclMemberPtr->UserOrGroup
  244. ));
  245. }
  246. }
  247. AclMemberPtr->Sid = NULL;
  248. GetNextAclMember (&AclMemberPtr);
  249. }
  250. b = TRUE;
  251. }
  252. __finally {
  253. if (!b) {
  254. if (Acl) {
  255. MemFree (g_hHeap, 0, Acl);
  256. }
  257. Acl = NULL;
  258. }
  259. }
  260. return Acl;
  261. }
  262. VOID
  263. FreeMemberListAcl (
  264. PACL Acl
  265. )
  266. /*++
  267. Routine Description:
  268. Routine to free the value returned by CreateAclFromMemberList
  269. Arguments:
  270. Acl - The return value of CreateAclFromMemberList
  271. Return value:
  272. none
  273. --*/
  274. {
  275. if (Acl) {
  276. MemFree (g_hHeap, 0, (LPVOID) Acl);
  277. }
  278. }
  279. VOID
  280. GetNextAclMember (
  281. PACLMEMBER *AclMemberPtrToPtr
  282. )
  283. /*++
  284. Routine Description:
  285. GetNextAclMember adjusts an ACLMEMBER pointer to point to the next
  286. member. Each member is a variable-length structure, so this funciton
  287. is required to walk the structure array.
  288. Arguments:
  289. AclMemberPtrToPtr - A pointer to a PACLMEMBER variable.
  290. Return value:
  291. none
  292. --*/
  293. {
  294. *AclMemberPtrToPtr = (PACLMEMBER) ((PBYTE) (*AclMemberPtrToPtr) +
  295. sizeof (ACLMEMBER) +
  296. SizeOfString ((*AclMemberPtrToPtr)->UserOrGroup)
  297. );
  298. }
  299. LONG
  300. CreateLocalAccount (
  301. IN PACCOUNTPROPERTIES Properties,
  302. IN PCWSTR User OPTIONAL
  303. )
  304. /*++
  305. Routine Description:
  306. CreateLocalAccount creates an account for a local user
  307. Arguments:
  308. Properties - Specifies a set of attributes for a user
  309. User - An optional name to override Properties->User
  310. Return value:
  311. A Win32 error code
  312. --*/
  313. {
  314. USER_INFO_3 ui;
  315. PUSER_INFO_3 ExistingInfo;
  316. DWORD rc;
  317. LONG ErrParam;
  318. PCWSTR UnicodeUser;
  319. PCWSTR UnicodePassword;
  320. PCWSTR UnicodeFullName;
  321. PCWSTR UnicodeComment;
  322. //
  323. // Create local account
  324. //
  325. if (!User) {
  326. User = Properties->User;
  327. }
  328. UnicodeUser = CreateUnicode (User);
  329. UnicodePassword = CreateUnicode (Properties->Password);
  330. UnicodeComment = CreateUnicode (Properties->AdminComment);
  331. UnicodeFullName = CreateUnicode (Properties->FullName);
  332. ZeroMemory (&ui, sizeof (ui));
  333. ui.usri3_name = (PWSTR) UnicodeUser;
  334. ui.usri3_password = (PWSTR) UnicodePassword;
  335. ui.usri3_comment = (PWSTR) UnicodeComment;
  336. ui.usri3_full_name = (PWSTR) UnicodeFullName;
  337. ui.usri3_priv = USER_PRIV_USER; // do not change
  338. //
  339. // don't expire nor require a password for this account
  340. //
  341. ui.usri3_flags = UF_SCRIPT|UF_NORMAL_ACCOUNT|UF_DONT_EXPIRE_PASSWD|UF_PASSWD_NOTREQD;
  342. ui.usri3_acct_expires = TIMEQ_FOREVER;
  343. ui.usri3_max_storage = USER_MAXSTORAGE_UNLIMITED;
  344. ui.usri3_primary_group_id = DOMAIN_GROUP_RID_USERS;
  345. ui.usri3_max_storage = USER_MAXSTORAGE_UNLIMITED;
  346. ui.usri3_acct_expires = TIMEQ_FOREVER;
  347. ui.usri3_password_expired = (INT) g_ConfigOptions.ForcePasswordChange;
  348. rc = NetUserAdd (NULL, 3, (PBYTE) &ui, &ErrParam);
  349. if (rc == ERROR_SUCCESS) {
  350. if (Properties->PasswordAttribs & PASSWORD_ATTR_ENCRYPTED) {
  351. //
  352. // change user's password using encrypted password APIs
  353. //
  354. rc = SetLocalUserEncryptedPassword (
  355. User,
  356. Properties->Password,
  357. FALSE,
  358. Properties->EncryptedPassword,
  359. TRUE
  360. );
  361. if (rc != ERROR_SUCCESS) {
  362. if (rc == ERROR_PASSWORD_RESTRICTION) {
  363. LOG ((
  364. LOG_WARNING,
  365. "Unable to set supplied password on user %s because a password rule has been violated.",
  366. User
  367. ));
  368. } else if (rc == ERROR_INVALID_PARAMETER) {
  369. LOG ((
  370. LOG_WARNING,
  371. "Illegal encrypted password supplied for user %s.",
  372. User
  373. ));
  374. } else {
  375. LOG ((
  376. LOG_WARNING,
  377. "Unable to set password on user %s, rc=%u",
  378. User,
  379. rc
  380. ));
  381. rc = ERROR_INVALID_PARAMETER;
  382. }
  383. }
  384. }
  385. } else if (rc == NERR_UserExists) {
  386. //
  387. // Try to change password if user already exists and this is the intent
  388. //
  389. DEBUGMSG ((DBG_WARNING, "User %s already exists", User));
  390. if ((Properties->PasswordAttribs & PASSWORD_ATTR_DONT_CHANGE_IF_EXIST) == 0) {
  391. if (Properties->PasswordAttribs & PASSWORD_ATTR_ENCRYPTED) {
  392. rc = SetLocalUserEncryptedPassword (
  393. User,
  394. Properties->Password,
  395. FALSE,
  396. Properties->EncryptedPassword,
  397. TRUE
  398. );
  399. if (rc != ERROR_SUCCESS) {
  400. if (rc == ERROR_PASSWORD_RESTRICTION) {
  401. LOG ((
  402. LOG_WARNING,
  403. "Unable to set supplied password on user %s because a password rule has been violated.",
  404. User
  405. ));
  406. } else if (rc == ERROR_INVALID_PARAMETER) {
  407. LOG ((
  408. LOG_WARNING,
  409. "Illegal encrypted password supplied for user %s.",
  410. User
  411. ));
  412. } else {
  413. LOG ((
  414. LOG_WARNING,
  415. "Unable to set password on user %s, rc=%u",
  416. User,
  417. rc
  418. ));
  419. rc = ERROR_INVALID_PARAMETER;
  420. }
  421. }
  422. } else {
  423. rc = NetUserGetInfo (NULL, User, 3, (PBYTE *) &ExistingInfo);
  424. if (rc == ERROR_SUCCESS) {
  425. ExistingInfo->usri3_password = ui.usri3_password;
  426. ExistingInfo->usri3_comment = ui.usri3_comment;
  427. ExistingInfo->usri3_full_name = ui.usri3_full_name;
  428. ExistingInfo->usri3_flags = ui.usri3_flags;
  429. ExistingInfo->usri3_password_expired = ui.usri3_password_expired;
  430. rc = NetUserSetInfo (NULL, User, 3, (PBYTE) ExistingInfo, &ErrParam);
  431. NetApiBufferFree ((PVOID) ExistingInfo);
  432. if (rc != ERROR_SUCCESS) {
  433. LOG ((LOG_WARNING, "NetUserSetInfo failed for %s. rc=%u.", User, rc));
  434. rc = ERROR_INVALID_PARAMETER;
  435. }
  436. } else {
  437. LOG ((LOG_WARNING, "NetUserGetInfo failed for %s. rc=%u.", User, rc));
  438. rc = ERROR_INVALID_PARAMETER;
  439. }
  440. }
  441. } else {
  442. rc = ERROR_SUCCESS;
  443. }
  444. } else {
  445. LOG ((LOG_ERROR, "NetUserAdd failed for %s. ErrParam=%i.", User, ErrParam));
  446. }
  447. DestroyUnicode (UnicodeUser);
  448. DestroyUnicode (UnicodePassword);
  449. DestroyUnicode (UnicodeComment);
  450. DestroyUnicode (UnicodeFullName);
  451. return rc;
  452. }
  453. VOID
  454. ClearAdminPassword (
  455. VOID
  456. )
  457. {
  458. ACCOUNTPROPERTIES Properties;
  459. Properties.Password = L"";
  460. Properties.AdminComment = L"";
  461. Properties.User = g_AdministratorStr;
  462. Properties.FullName = g_AdministratorStr;
  463. CreateLocalAccount (&Properties, NULL);
  464. }
  465. BOOL
  466. AddSidToLocalGroup (
  467. PSID Sid,
  468. PCWSTR Group
  469. )
  470. /*++
  471. Routine Description:
  472. Routine that adds the supplied SID to the Administrators group.
  473. Arguments:
  474. Sid - A valid security id for the user to be added to the
  475. Administrators group
  476. Group - Specifies the group name to join the user to
  477. Return value:
  478. TRUE if the member was added successfully
  479. --*/
  480. {
  481. LOCALGROUP_MEMBERS_INFO_0 lgrmi0;
  482. DWORD rc;
  483. lgrmi0.lgrmi0_sid = Sid;
  484. rc = NetLocalGroupAddMembers (
  485. NULL,
  486. Group,
  487. 0, // level 0
  488. (PBYTE) &lgrmi0,
  489. 1 // member count
  490. );
  491. return rc == ERROR_SUCCESS;
  492. }
  493. NTSTATUS
  494. pGetPrimaryDomainInfo (
  495. POLICY_PRIMARY_DOMAIN_INFO **PrimaryInfoPtr
  496. )
  497. /*++
  498. Routine Description:
  499. Private function that retrieves the primary domain info.
  500. Arguments:
  501. PrimaryInfoPtr - Pointer to a variable to receive the address
  502. of the POLICY_PRIMARY_DOMAIN_INFO structure
  503. allocated by the Lsa APIs. Free memory by
  504. calling LsaFreeMemory.
  505. Return value:
  506. NT status code indicating outcome
  507. --*/
  508. {
  509. LSA_HANDLE policyHandle;
  510. NTSTATUS status;
  511. //
  512. // Open local LSA policy to retrieve domain name
  513. //
  514. status = OpenPolicy (
  515. NULL, // local target machine
  516. POLICY_VIEW_LOCAL_INFORMATION, // Access type
  517. &policyHandle // resultant policy handle
  518. );
  519. if (status == ERROR_SUCCESS) {
  520. //
  521. // Query LSA Primary domain info
  522. //
  523. status = LsaQueryInformationPolicy (
  524. policyHandle,
  525. PolicyPrimaryDomainInformation,
  526. (PVOID *) PrimaryInfoPtr
  527. );
  528. LsaClose (policyHandle);
  529. }
  530. return status;
  531. }
  532. BOOL
  533. GetPrimaryDomainName (
  534. OUT PTSTR DomainName
  535. )
  536. {
  537. NTSTATUS status;
  538. POLICY_PRIMARY_DOMAIN_INFO *PrimaryInfo;
  539. PCTSTR TcharName;
  540. status = pGetPrimaryDomainInfo (&PrimaryInfo);
  541. if (status == ERROR_SUCCESS) {
  542. TcharName = ConvertWtoT (PrimaryInfo->Name.Buffer);
  543. MYASSERT (TcharName);
  544. StringCopy (DomainName, TcharName);
  545. FreeWtoT (TcharName);
  546. LsaFreeMemory (PrimaryInfo);
  547. }
  548. ELSE_DEBUGMSG ((DBG_WARNING, "Can't get primary domain info. rc=%u", status));
  549. return status == ERROR_SUCCESS;
  550. }
  551. BOOL
  552. GetPrimaryDomainSid (
  553. OUT PBYTE DomainSid,
  554. IN UINT MaxBytes
  555. )
  556. {
  557. NTSTATUS status;
  558. POLICY_PRIMARY_DOMAIN_INFO *PrimaryInfo;
  559. UINT Size;
  560. status = pGetPrimaryDomainInfo (&PrimaryInfo);
  561. if (status == ERROR_SUCCESS) {
  562. Size = GetLengthSid (PrimaryInfo->Sid);
  563. if (MaxBytes < Size) {
  564. status = ERROR_INSUFFICIENT_BUFFER;
  565. } else {
  566. CopyMemory (DomainSid, PrimaryInfo->Sid, Size);
  567. }
  568. LsaFreeMemory (PrimaryInfo);
  569. }
  570. ELSE_DEBUGMSG ((DBG_WARNING, "Can't get primary domain SID. rc=%u", status));
  571. return status == ERROR_SUCCESS;
  572. }
  573. BOOL
  574. IsMemberOfDomain (
  575. VOID
  576. )
  577. /*++
  578. Routine Description:
  579. Determines if the machine is participating in a domain, or if it is
  580. only participating in a workgroup. This determination is done by
  581. obtaining the primary domain information, looking for the server's
  582. SID. If the SID is NULL, the machine is not in a domain.
  583. Arguments:
  584. none
  585. Return value:
  586. TRUE if the machine is in a domain, FALSE if its in a workgroup.
  587. --*/
  588. {
  589. NET_API_STATUS rc;
  590. PWSTR WorkgroupOrDomain = NULL;
  591. NETSETUP_JOIN_STATUS Type;
  592. rc = NetGetJoinInformation (NULL, &WorkgroupOrDomain, &Type);
  593. DEBUGMSG ((DBG_VERBOSE, "NetGetJoinInformation: name=%s, type=%u", WorkgroupOrDomain, Type));
  594. if (WorkgroupOrDomain) {
  595. NetApiBufferFree (WorkgroupOrDomain);
  596. }
  597. if (rc != ERROR_SUCCESS) {
  598. LOG ((LOG_ERROR, "NetGetJoinInformation failed: error %u", rc));
  599. }
  600. return rc == ERROR_SUCCESS && Type == NetSetupDomainName;
  601. #if 0
  602. POLICY_PRIMARY_DOMAIN_INFO *PrimaryInfo;
  603. BOOL b;
  604. NTSTATUS rc;
  605. rc = pGetPrimaryDomainInfo (&PrimaryInfo);
  606. if (rc == ERROR_SUCCESS) {
  607. b = PrimaryInfo->Sid != NULL;
  608. } else {
  609. b = FALSE;
  610. SetLastError (rc);
  611. LOG ((LOG_ERROR, "Can't get domain security info"));
  612. }
  613. // Domain name is in PrimaryInfo->Name.Buffer
  614. LsaFreeMemory (PrimaryInfo) ;
  615. return b;
  616. #endif
  617. }
  618. LONG
  619. GetAnyDC (
  620. IN PCWSTR Domain,
  621. IN PWSTR ServerBuf,
  622. IN BOOL GetNewServer
  623. )
  624. /*++
  625. Routine Description:
  626. Gets the list of all domain controllers and randomly chooses one. If
  627. the listed DC is not online, other listed DCs are queried until an
  628. alive DC is found.
  629. Arguments:
  630. Domain - The name of the domain to find DCs for
  631. ServerBuf - A buffer to hold the name of the server
  632. Return value:
  633. NT status code indicating outcome.
  634. --*/
  635. {
  636. DWORD rc;
  637. PDOMAIN_CONTROLLER_INFO dci;
  638. DWORD Flags = DS_IS_FLAT_NAME;
  639. //
  640. // This API is fast because its WINS based...
  641. //
  642. rc = DsGetDcName (
  643. NULL, // computer to remote to
  644. Domain,
  645. NULL, // Domain GUID
  646. NULL, // Site GUID
  647. Flags | (GetNewServer ? DS_FORCE_REDISCOVERY : 0),
  648. &dci
  649. );
  650. if (rc == NO_ERROR) {
  651. StringCopyW (ServerBuf, dci->DomainControllerAddress);
  652. NetApiBufferFree (dci);
  653. DEBUGMSG ((DBG_VERBOSE, "Found server %s for the %s domain", ServerBuf, Domain));
  654. return rc;
  655. }
  656. return rc;
  657. }
  658. VOID
  659. InitLsaString (
  660. OUT PLSA_UNICODE_STRING LsaString,
  661. IN PWSTR String
  662. )
  663. /*++
  664. Routine Description:
  665. LSA uses a special Pascal-style string structure. This
  666. routine assigns String to a member of LsaString, and computes
  667. its length and maximum length.
  668. Arguments:
  669. LsaString - A pointer to the structure to receive a pointer
  670. to the nul-terminated string, the length in bytes
  671. (excluding the nul), and the maximum length including
  672. the nul.
  673. Return value:
  674. none
  675. --*/
  676. {
  677. USHORT StringLength;
  678. if (!String) {
  679. ZeroMemory (LsaString, sizeof (LSA_UNICODE_STRING));
  680. return;
  681. }
  682. StringLength = ByteCountW (String);
  683. LsaString->Buffer = String;
  684. LsaString->Length = StringLength;
  685. LsaString->MaximumLength = StringLength + sizeof(WCHAR);
  686. }
  687. NTSTATUS
  688. OpenPolicy (
  689. IN PWSTR ServerName,
  690. IN DWORD DesiredAccess,
  691. OUT PLSA_HANDLE policyHandle
  692. )
  693. /*++
  694. Routine Description:
  695. A wrapper to simplify LsaOpenPolicy
  696. Arguments:
  697. ServerName - Supplies the server to open the policy on. Specify
  698. NULL for local machine.
  699. DesiredAccess - The access flags passed to the LSA API
  700. policyHandle - Receives the policy handle if successful
  701. Return value:
  702. NT status code indicating outcome
  703. --*/
  704. {
  705. LSA_OBJECT_ATTRIBUTES objectAttributes;
  706. LSA_UNICODE_STRING ServerString;
  707. PLSA_UNICODE_STRING Server;
  708. //
  709. // Always initialize the object attributes to all zeroes
  710. //
  711. ZeroMemory (&objectAttributes, sizeof(objectAttributes));
  712. if (ServerName != NULL) {
  713. //
  714. // Make a LSA_UNICODE_STRING out of the PWSTR passed in
  715. //
  716. InitLsaString (&ServerString, ServerName);
  717. Server = &ServerString;
  718. } else {
  719. Server = NULL;
  720. }
  721. //
  722. // Attempt to open the policy
  723. //
  724. return LsaOpenPolicy (
  725. Server,
  726. &objectAttributes,
  727. DesiredAccess,
  728. policyHandle
  729. );
  730. }
  731. BOOL
  732. IsDomainController(
  733. IN PWSTR Server,
  734. OUT PBOOL DomainControllerFlag
  735. )
  736. /*++
  737. Routine Description:
  738. Queries if the machine is a server or workstation via
  739. the NetServerGetInfo API.
  740. Arguments:
  741. Server - The machine to query, or NULL for the local machine
  742. DomainControllerFlag - Receives TRUE if the machine is a
  743. domain controller, or FALSE if the
  744. machine is a workstation.
  745. Return value:
  746. TRUE if the API was successful, or FALSE if not. GetLastError
  747. gives failure code.
  748. --*/
  749. {
  750. PSERVER_INFO_101 si101;
  751. NET_API_STATUS nas;
  752. nas = NetServerGetInfo(
  753. Server,
  754. 101, // info-level
  755. (PBYTE *) &si101
  756. );
  757. if (nas != NO_ERROR) {
  758. SetLastError (nas);
  759. return FALSE;
  760. }
  761. if ((si101->sv101_type & SV_TYPE_DOMAIN_CTRL) ||
  762. (si101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL)) {
  763. //
  764. // We are dealing with a DC
  765. //
  766. *DomainControllerFlag = TRUE;
  767. } else {
  768. *DomainControllerFlag = FALSE;
  769. }
  770. NetApiBufferFree (si101);
  771. return TRUE;
  772. }
  773. DWORD
  774. pConvertFlagsToRights (
  775. DWORD Flags
  776. )
  777. {
  778. while (Flags > 0x0f) {
  779. Flags >>= 4;
  780. }
  781. if (Flags & 0x01) {
  782. return 0;
  783. }
  784. if (Flags == 0x02) {
  785. return ACCESS_READ;
  786. }
  787. if (Flags == 0x04) {
  788. return ACCESS_WRITE;
  789. }
  790. if ((Flags & 0x06) == 0x06) {
  791. return ACCESS_READ|ACCESS_WRITE;
  792. }
  793. DEBUGMSG ((DBG_WHOOPS, "Undefined access flags specified: 0x%X", Flags));
  794. return 0;
  795. }
  796. DWORD
  797. SetRegKeySecurity (
  798. IN PCTSTR KeyStr,
  799. IN DWORD DaclFlags, OPTIONAL
  800. IN PSID Owner, OPTIONAL
  801. IN PSID PrimaryGroup, OPTIONAL
  802. IN BOOL Recursive
  803. )
  804. /*++
  805. Routine Description:
  806. SetRegKeySecurity updates the security of a registry key, or an entire
  807. registry node. The caller can change the DACL, owner or primary group.
  808. Change of the SACL is intentionally not implemented.
  809. Arguments:
  810. KeyStr - Specifies the key to modify the permissions. If Recursive
  811. is set to TRUE, this key will be updated along with all
  812. subkeys.
  813. DaclFlags - Specifies zero or more SF_* flags, indicating how access to
  814. the key should be set.
  815. Owner - Specifies the SID of the new owner.
  816. PrimaryGroup - Specifies the SID of the primary group.
  817. Recursive - Specifies TRUE to apply the security to the key and all of
  818. its subkeys, or FALSE to update the key only, leaving the
  819. subkeys alone.
  820. Return Value:
  821. A Win32 status code.
  822. --*/
  823. {
  824. DWORD rc = ERROR_SUCCESS;
  825. SECURITY_DESCRIPTOR sd;
  826. GROWBUFFER AclMemberList = GROWBUF_INIT;
  827. HKEY Key = NULL;
  828. REGSAM OldSam;
  829. DWORD AclMembers = 0;
  830. PACL Acl = NULL;
  831. SECURITY_INFORMATION WhatToSet = 0;
  832. REGTREE_ENUM e;
  833. LONG rc2;
  834. _try {
  835. //
  836. // Open key with full permission
  837. //
  838. OldSam = SetRegOpenAccessMode (KEY_ALL_ACCESS);
  839. Key = OpenRegKeyStr (KeyStr);
  840. if (!Key) {
  841. rc = GetLastError();
  842. __leave;
  843. }
  844. //
  845. // Prepare a security descriptor
  846. //
  847. InitializeSecurityDescriptor (&sd, SECURITY_DESCRIPTOR_REVISION);
  848. if (Owner) {
  849. if (!SetSecurityDescriptorOwner (&sd, Owner, FALSE)) {
  850. rc = GetLastError();
  851. __leave;
  852. }
  853. WhatToSet |= OWNER_SECURITY_INFORMATION;
  854. }
  855. if (PrimaryGroup) {
  856. if (!SetSecurityDescriptorGroup (&sd, PrimaryGroup, FALSE)) {
  857. rc = GetLastError();
  858. __leave;
  859. }
  860. WhatToSet |= GROUP_SECURITY_INFORMATION;
  861. }
  862. //
  863. // Add the DACL
  864. //
  865. if (DaclFlags & SF_EVERYONE_MASK) {
  866. AddAclMember (
  867. &AclMemberList,
  868. g_EveryoneStr,
  869. pConvertFlagsToRights (DaclFlags & SF_EVERYONE_MASK)
  870. );
  871. AclMembers++;
  872. }
  873. if (DaclFlags & SF_ADMINISTRATORS_MASK) {
  874. AddAclMember (
  875. &AclMemberList,
  876. g_AdministratorsGroupStr,
  877. pConvertFlagsToRights (DaclFlags & SF_ADMINISTRATORS_MASK)
  878. );
  879. AclMembers++;
  880. }
  881. if (AclMembers) {
  882. Acl = CreateAclFromMemberList (AclMemberList.Buf, AclMembers);
  883. if (!Acl) {
  884. rc = GetLastError();
  885. __leave;
  886. }
  887. WhatToSet |= DACL_SECURITY_INFORMATION;
  888. }
  889. //
  890. // Set the security
  891. //
  892. if (Recursive) {
  893. DEBUGMSG_IF ((
  894. rc != ERROR_SUCCESS,
  895. DBG_WARNING,
  896. "RegSetKeySecurity failed for %s with rc=%u",
  897. KeyStr,
  898. rc
  899. ));
  900. if (EnumFirstRegKeyInTree (&e, KeyStr)) {
  901. do {
  902. rc2 = RegSetKeySecurity (e.CurrentKey->KeyHandle, WhatToSet, &sd);
  903. if (rc2 != ERROR_SUCCESS) {
  904. rc = (DWORD) rc2;
  905. }
  906. DEBUGMSG_IF ((
  907. rc2 != ERROR_SUCCESS,
  908. DBG_WARNING,
  909. "RegSetKeySecurity failed for %s with rc=%u",
  910. e.FullKeyName,
  911. rc2
  912. ));
  913. } while (EnumNextRegKeyInTree (&e));
  914. }
  915. } else {
  916. rc = (DWORD) RegSetKeySecurity (Key, WhatToSet, &sd);
  917. }
  918. }
  919. __finally {
  920. FreeGrowBuffer (&AclMemberList);
  921. if (Key) {
  922. CloseRegKey (Key);
  923. }
  924. SetRegOpenAccessMode (OldSam);
  925. if (Acl) {
  926. FreeMemberListAcl (Acl);
  927. }
  928. }
  929. return rc;
  930. }