Leaked source code of windows server 2003
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.

1442 lines
40 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation
  3. Module Name:
  4. SeSddl.c
  5. Abstract:
  6. This module implements the Security Descriptor Definition Language support
  7. functions for kernel mode
  8. Author:
  9. Mac McLain (MacM) Nov 07, 1997
  10. Environment:
  11. Kernel Mode
  12. Revision History:
  13. Jin Huang (JinHuang) 3/4/98 Fix validity flags (GetAceFlagsInTable)
  14. Jin Huang (JinHuang) 3/10/98 Add SD controls (GetSDControlForString)
  15. Set SidsInitialized flag
  16. Skip any possible spaces in string
  17. Jin Huang (JinHuang) 5/1/98 Fix memory leek, error checking
  18. improve performance
  19. Alaa Abdelhalim (Alaa) 7/20/99 Initialize sbz2 field to 0 in LocalGetAclForString
  20. function.
  21. Vishnu Patankar (VishnuP) 7/5/00 Added new API ConvertStringSDToSDDomain(A/W)
  22. Adrian J. Oney (AdriaO) 3/27/02 Ported small subset of
  23. advapi32\sddl.c to KernelMode
  24. --*/
  25. #include "WlDef.h"
  26. #include "SepSddl.h"
  27. #pragma hdrstop
  28. #pragma alloc_text(PAGE, SeSddlSecurityDescriptorFromSDDL)
  29. #pragma alloc_text(PAGE, SepSddlSecurityDescriptorFromSDDLString)
  30. #pragma alloc_text(PAGE, SepSddlDaclFromSDDLString)
  31. #pragma alloc_text(PAGE, SepSddlGetAclForString)
  32. #pragma alloc_text(PAGE, SepSddlLookupAccessMaskInTable)
  33. #pragma alloc_text(PAGE, SepSddlGetSidForString)
  34. #pragma alloc_text(PAGE, SepSddlAddAceToAcl)
  35. #pragma alloc_text(PAGE, SepSddlParseWideStringUlong)
  36. #define POOLTAG_SEACL 'lAeS'
  37. #define POOLTAG_SESD 'dSeS'
  38. #define POOLTAG_SETS 'sTeS'
  39. static STRSD_SID_LOOKUP SidLookup[] = {
  40. // World (WD) == SECURITY_WORLD_SID_AUTHORITY, also called Everyone.
  41. // Typically everyone but restricted code (in XP, anonymous logons also
  42. // lack world SID)
  43. DEFINE_SDDL_ENTRY( \
  44. SeWorldSid, \
  45. WIN2K_OR_LATER, \
  46. SDDL_EVERYONE, \
  47. SDDL_LEN_TAG( SDDL_EVERYONE ) ),
  48. // Administrators (BA) == DOMAIN_ALIAS_RID_ADMINS, Administrator group on
  49. // the machine
  50. DEFINE_SDDL_ENTRY( \
  51. SeAliasAdminsSid, \
  52. WIN2K_OR_LATER, \
  53. SDDL_BUILTIN_ADMINISTRATORS, \
  54. SDDL_LEN_TAG( SDDL_BUILTIN_ADMINISTRATORS ) ),
  55. // System (SY) == SECURITY_LOCAL_SYSTEM_RID, the OS itself (including its
  56. // user mode components)
  57. DEFINE_SDDL_ENTRY( \
  58. SeLocalSystemSid, \
  59. WIN2K_OR_LATER, \
  60. SDDL_LOCAL_SYSTEM, \
  61. SDDL_LEN_TAG( SDDL_LOCAL_SYSTEM ) ),
  62. // Interactive User (IU) == SECURITY_INTERACTIVE_RID, users logged on
  63. // locally (doesn't include TS users)
  64. DEFINE_SDDL_ENTRY( \
  65. SeInteractiveSid, \
  66. WIN2K_OR_LATER, \
  67. SDDL_INTERACTIVE, \
  68. SDDL_LEN_TAG( SDDL_INTERACTIVE ) ),
  69. // Restricted Code (RC) == SECURITY_RESTRICTED_CODE_RID, used to control
  70. // access by untrusted code (ACL's must contain World SID as well)
  71. DEFINE_SDDL_ENTRY( \
  72. SeRestrictedSid, \
  73. WIN2K_OR_LATER, \
  74. SDDL_RESTRICTED_CODE, \
  75. SDDL_LEN_TAG( SDDL_RESTRICTED_CODE ) ),
  76. // Authenticated Users (AU) == SECURITY_AUTHENTICATED_USER_RID, any user
  77. // recognized by the local machine or by a domain.
  78. DEFINE_SDDL_ENTRY( \
  79. SeAuthenticatedUsersSid, \
  80. WIN2K_OR_LATER, \
  81. SDDL_AUTHENTICATED_USERS, \
  82. SDDL_LEN_TAG( SDDL_AUTHENTICATED_USERS ) ),
  83. // Network Logon User (NU) == SECURITY_NETWORK_RID, any user logged in
  84. // remotely.
  85. DEFINE_SDDL_ENTRY( \
  86. SeNetworkSid, \
  87. WIN2K_OR_LATER, \
  88. SDDL_NETWORK, \
  89. SDDL_LEN_TAG( SDDL_NETWORK ) ),
  90. // Anonymous Logged-on User (AN) == SECURITY_ANONYMOUS_LOGON_RID, users
  91. // logged on without an indentity. No effect before Windows XP (SID
  92. // presense is harmless though)
  93. // Note: By default, World does not include Anonymous users on XP!
  94. DEFINE_SDDL_ENTRY( \
  95. SeAnonymousLogonSid, \
  96. WIN2K_OR_LATER, \
  97. SDDL_ANONYMOUS, \
  98. SDDL_LEN_TAG( SDDL_ANONYMOUS ) ),
  99. // Builtin guest account (BG) == DOMAIN_ALIAS_RID_GUESTS, users logging in
  100. // using the local guest account.
  101. DEFINE_SDDL_ENTRY( \
  102. SeAliasGuestsSid, \
  103. WIN2K_OR_LATER, \
  104. SDDL_BUILTIN_GUESTS, \
  105. SDDL_LEN_TAG( SDDL_BUILTIN_GUESTS ) ),
  106. // Builtin user account (BU) == DOMAIN_ALIAS_RID_USERS, local user accounts,
  107. // or users on the domain.
  108. DEFINE_SDDL_ENTRY( \
  109. SeAliasUsersSid, \
  110. WIN2K_OR_LATER, \
  111. SDDL_BUILTIN_USERS, \
  112. SDDL_LEN_TAG( SDDL_BUILTIN_USERS ) ),
  113. //
  114. // Don't expose these - they are either invalid or depricated
  115. //
  116. //{ SePrincipalSelfSid, SDDL_PERSONAL_SELF, SDDL_LEN_TAG( SDDL_PERSONAL_SELF ) },
  117. //{ SeServiceSid, SDDL_SERVICE, SDDL_LEN_TAG( SDDL_SERVICE ) },
  118. //{ SeAliasPowerUsersSid, SDDL_POWER_USERS, SDDL_LEN_TAG( SDDL_POWER_USERS ) },
  119. // Local Service (LS) == SECURITY_LOCAL_SERVICE_RID, a predefined account
  120. // for local services (which also belong to Authenticated and World)
  121. DEFINE_SDDL_ENTRY( \
  122. SeLocalServiceSid, \
  123. WINXP_OR_LATER, \
  124. SDDL_LOCAL_SERVICE, \
  125. SDDL_LEN_TAG( SDDL_LOCAL_SERVICE ) ),
  126. // Network Service (NS) == SECURITY_NETWORK_SERVICE_RID, a predefined
  127. // account for network services (which also belong to Authenticated and
  128. // World)
  129. DEFINE_SDDL_ENTRY( \
  130. SeNetworkServiceSid, \
  131. WINXP_OR_LATER, \
  132. SDDL_NETWORK_SERVICE, \
  133. SDDL_LEN_TAG( SDDL_NETWORK_SERVICE ) )
  134. };
  135. //
  136. // This is how the access mask is looked up. Always have the multi-char rights
  137. // before the single char ones
  138. //
  139. static STRSD_KEY_LOOKUP RightsLookup[] = {
  140. { SDDL_READ_CONTROL, SDDL_LEN_TAG( SDDL_READ_CONTROL ), READ_CONTROL },
  141. { SDDL_WRITE_DAC, SDDL_LEN_TAG( SDDL_WRITE_DAC ), WRITE_DAC },
  142. { SDDL_WRITE_OWNER, SDDL_LEN_TAG( SDDL_WRITE_OWNER ), WRITE_OWNER },
  143. { SDDL_STANDARD_DELETE, SDDL_LEN_TAG( SDDL_STANDARD_DELETE ), DELETE },
  144. { SDDL_GENERIC_ALL, SDDL_LEN_TAG( SDDL_GENERIC_ALL ), GENERIC_ALL },
  145. { SDDL_GENERIC_READ, SDDL_LEN_TAG( SDDL_GENERIC_READ ), GENERIC_READ },
  146. { SDDL_GENERIC_WRITE, SDDL_LEN_TAG( SDDL_GENERIC_WRITE ), GENERIC_WRITE },
  147. { SDDL_GENERIC_EXECUTE, SDDL_LEN_TAG( SDDL_GENERIC_EXECUTE ), GENERIC_EXECUTE },
  148. };
  149. //
  150. // Exported functions
  151. //
  152. NTSTATUS
  153. SeSddlSecurityDescriptorFromSDDL(
  154. IN PCUNICODE_STRING SecurityDescriptorString,
  155. IN LOGICAL SuppliedByDefaultMechanism,
  156. OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
  157. )
  158. /*++
  159. Routine Description:
  160. This routine creates a security descriptor given an SDDL string in
  161. UNICODE_STRING format. The security descriptor is self-relative
  162. (sans-pointers), so it can be persisted and used on subsequent boots.
  163. Only a subset of the SDDL format is currently supported. This subset is
  164. really tailored towards device object support.
  165. Format:
  166. D:P(ACE)(ACE)(ACE), where (ACE) is (AceType;;Access;;;SID)
  167. AceType - Only Allow ("A") is supported.
  168. AceFlags - No AceFlags are supported
  169. Access - Rights specified in either hex format (0xnnnnnnnn), or via the
  170. SDDL Generic/Standard abbreviations
  171. ObjectGuid - Not supported
  172. InheritObjectGuid - Not supported
  173. SID - Abbreviated security ID (example WD == World)
  174. The S-w-x-y-z form for SIDs is not supported
  175. Example - "D:P(A;;GA;;;SY)" which is Allow System to have Generic All access
  176. Arguments:
  177. SecurityDescriptorString - Stringized security descriptor to be converted.
  178. SuppliedByDefaultMechanism - TRUE if the DACL is being built due to some
  179. default mechanism (ie, not manually specified
  180. by an admin, etc).
  181. SecurityDescriptor - Receives the security descriptor on success, NULL
  182. on error.
  183. Return Value:
  184. NTSTATUS
  185. --*/
  186. {
  187. NTSTATUS Status;
  188. WCHAR GuardChar;
  189. LPWSTR TempStringBuffer;
  190. //
  191. // Look to see if we have a string built by RtlInitUnicodeString. It will
  192. // have a terminating NULL with it, so no conversion is neccessary.
  193. //
  194. if (SecurityDescriptorString->MaximumLength ==
  195. SecurityDescriptorString->Length + sizeof(UNICODE_NULL)) {
  196. GuardChar = SecurityDescriptorString->Buffer[SecurityDescriptorString->Length/sizeof(WCHAR)];
  197. if (GuardChar == UNICODE_NULL) {
  198. return SepSddlSecurityDescriptorFromSDDLString(
  199. SecurityDescriptorString->Buffer,
  200. SuppliedByDefaultMechanism,
  201. SecurityDescriptor
  202. );
  203. }
  204. }
  205. //
  206. // We need to allocate a slightly larger buffer so we can NULL-terminate it.
  207. //
  208. TempStringBuffer = (LPWSTR) ExAllocatePoolWithTag(
  209. PagedPool,
  210. SecurityDescriptorString->Length + sizeof(UNICODE_NULL),
  211. POOLTAG_SETS
  212. );
  213. if (TempStringBuffer == NULL) {
  214. *SecurityDescriptor = NULL;
  215. return STATUS_INSUFFICIENT_RESOURCES;
  216. }
  217. //
  218. // Build a null terminated WCHAR string
  219. //
  220. RtlCopyMemory(
  221. TempStringBuffer,
  222. SecurityDescriptorString->Buffer,
  223. SecurityDescriptorString->Length
  224. );
  225. TempStringBuffer[SecurityDescriptorString->Length/sizeof(WCHAR)] = UNICODE_NULL;
  226. //
  227. // Do the conversion
  228. //
  229. Status = SepSddlSecurityDescriptorFromSDDLString(
  230. TempStringBuffer,
  231. SuppliedByDefaultMechanism,
  232. SecurityDescriptor
  233. );
  234. //
  235. // Free the temporary string
  236. //
  237. ExFreePool(TempStringBuffer);
  238. return Status;
  239. }
  240. //
  241. // Private functions
  242. //
  243. NTSTATUS
  244. SepSddlSecurityDescriptorFromSDDLString(
  245. IN LPCWSTR SecurityDescriptorString,
  246. IN LOGICAL SuppliedByDefaultMechanism,
  247. OUT PSECURITY_DESCRIPTOR *SecurityDescriptor
  248. )
  249. /*++
  250. Routine Description:
  251. This routine creates a security descriptor given an SDDL string in LPWSTR
  252. format. The security descriptor is self-relative (sans-pointers), so it can
  253. be persisted and used on subsequent boots.
  254. Only the subset of the SDDL format is currently supported. This subset is
  255. really tailored towards device object support.
  256. Format:
  257. D:P(ACE)(ACE)(ACE), where (ACE) is (AceType;;Access;;;SID)
  258. AceType - Only Allow ("A") is supported.
  259. AceFlags - No AceFlags are supported
  260. Access - Rights specified in either hex format (0xnnnnnnnn), or via the
  261. SDDL Generic/Standard abbreviations
  262. ObjectGuid - Not supported
  263. InheritObjectGuid - Not supported
  264. SID - Abbreviated security ID (example WD == World)
  265. The S-w-x-y-z form for SIDs is not supported
  266. Example - "D:P(A;;GA;;;SY)" which is Allow System to have Generic All access
  267. Arguments:
  268. SecurityDescriptorString - Stringized security descriptor to be converted.
  269. SuppliedByDefaultMechanism - TRUE if the DACL is being built due to some
  270. default mechanism (ie, not manually specified
  271. by an admin, etc).
  272. SecurityDescriptor - Receives the security descriptor on success, NULL
  273. on error.
  274. Return Value:
  275. NTSTATUS
  276. --*/
  277. {
  278. SECURITY_DESCRIPTOR LocalSecurityDescriptor;
  279. PSECURITY_DESCRIPTOR NewSecurityDescriptor;
  280. ULONG SecurityDescriptorControlFlags;
  281. PACL DiscretionaryAcl;
  282. ULONG BufferLength;
  283. NTSTATUS IgnoredStatus;
  284. NTSTATUS Status;
  285. PAGED_CODE();
  286. //
  287. // Preinit
  288. //
  289. DiscretionaryAcl = NULL;
  290. NewSecurityDescriptor = NULL;
  291. *SecurityDescriptor = NULL;
  292. //
  293. // First convert the SDDL into a DACL + Descriptor flags
  294. //
  295. Status = SepSddlDaclFromSDDLString(
  296. SecurityDescriptorString,
  297. SuppliedByDefaultMechanism,
  298. &SecurityDescriptorControlFlags,
  299. &DiscretionaryAcl
  300. );
  301. if (!NT_SUCCESS(Status)) {
  302. goto ErrorExit;
  303. }
  304. //
  305. // Create an on-stack security descriptor
  306. //
  307. IgnoredStatus = RtlCreateSecurityDescriptor( &LocalSecurityDescriptor,
  308. SECURITY_DESCRIPTOR_REVISION );
  309. ASSERT(IgnoredStatus == STATUS_SUCCESS);
  310. //
  311. // Now set the control, owner, group, dacls, and sacls, etc
  312. //
  313. IgnoredStatus = RtlSetDaclSecurityDescriptor( &LocalSecurityDescriptor,
  314. TRUE,
  315. DiscretionaryAcl,
  316. FALSE );
  317. ASSERT(IgnoredStatus == STATUS_SUCCESS);
  318. //
  319. // Add in the descriptor flags (we do this afterwords as the RtlSet...
  320. // functions also munge the defaulted bits.)
  321. //
  322. LocalSecurityDescriptor.Control |= SecurityDescriptorControlFlags;
  323. //
  324. // Convert the security descriptor into a self-contained binary form
  325. // ("self-relative", ie sans-pointers) that can be written into the
  326. // registry and used on subsequent boots. Start by getting the required
  327. // size.
  328. //
  329. BufferLength = 0;
  330. IgnoredStatus = RtlAbsoluteToSelfRelativeSD(
  331. &LocalSecurityDescriptor,
  332. NULL,
  333. &BufferLength
  334. );
  335. ASSERT(IgnoredStatus == STATUS_BUFFER_TOO_SMALL);
  336. //
  337. // Allocate memory for the descriptor
  338. //
  339. NewSecurityDescriptor = (PSECURITY_DESCRIPTOR) ExAllocatePoolWithTag(
  340. PagedPool,
  341. BufferLength,
  342. POOLTAG_SESD
  343. );
  344. if (NewSecurityDescriptor == NULL) {
  345. Status = STATUS_INSUFFICIENT_RESOURCES;
  346. goto ErrorExit;
  347. }
  348. //
  349. // Do the conversion
  350. //
  351. Status = RtlAbsoluteToSelfRelativeSD(
  352. &LocalSecurityDescriptor,
  353. NewSecurityDescriptor,
  354. &BufferLength
  355. );
  356. if (!NT_SUCCESS(Status)) {
  357. goto ErrorExit;
  358. }
  359. //
  360. // At this point, the Dacl is no longer needed.
  361. //
  362. ExFreePool(DiscretionaryAcl);
  363. *SecurityDescriptor = NewSecurityDescriptor;
  364. return Status;
  365. ErrorExit:
  366. if ( DiscretionaryAcl != NULL ) {
  367. ExFreePool(DiscretionaryAcl);
  368. }
  369. if ( NewSecurityDescriptor != NULL ) {
  370. ExFreePool(NewSecurityDescriptor);
  371. }
  372. return Status;
  373. }
  374. NTSTATUS
  375. SepSddlDaclFromSDDLString(
  376. IN LPCWSTR SecurityDescriptorString,
  377. IN LOGICAL SuppliedByDefaultMechanism,
  378. OUT ULONG *SecurityDescriptorControlFlags,
  379. OUT PACL *DiscretionaryAcl
  380. )
  381. /*++
  382. Routine Description:
  383. This routine will create a DACL given an SDDL string in LPWSTR format. Only
  384. the subset of the SDDL format is currently supported. This subset is really
  385. tailored towards device object support.
  386. Format:
  387. D:P(ACE)(ACE)(ACE), where (ACE) is (AceType;;Access;;;SID)
  388. AceType - Only Allow ("A") is supported.
  389. AceFlags - No AceFlags are supported
  390. Access - Rights specified in either hex format (0xnnnnnnnn), or via the
  391. SDDL Generic/Standard abbreviations
  392. ObjectGuid - Not supported
  393. InheritObjectGuid - Not supported
  394. SID - Abbreviated security ID (example WD == World)
  395. The S-w-x-y-z form for SIDs is not supported
  396. Example - "D:P(A;;GA;;;SY)" which is Allow System to have Generic All access
  397. Arguments:
  398. SecurityDescriptorString - Stringized security descriptor to be converted.
  399. SuppliedByDefaultMechanism - TRUE if the DACL is being built due to some
  400. default mechanism (ie, not manually specified
  401. by an admin, etc).
  402. SecurityDescriptorControlFlags - Receives control flags to apply if a
  403. security descriptor is made from the DACL.
  404. Receives 0 on error.
  405. DiscretionaryAcl - Receives ACL allocated from paged pool, or NULL on
  406. error. A self-contained security descriptor can be made
  407. with this ACL using the RtlAbsoluteToSelfRelativeSD
  408. function.
  409. Return Value:
  410. NTSTATUS
  411. --*/
  412. {
  413. PACL Dacl;
  414. PWSTR Curr, End;
  415. NTSTATUS Status;
  416. ULONG ControlFlags;
  417. PAGED_CODE();
  418. //
  419. // Preinit for error.
  420. //
  421. *DiscretionaryAcl = NULL;
  422. *SecurityDescriptorControlFlags = 0;
  423. //
  424. // Now, we'll just start parsing and building
  425. //
  426. Curr = ( PWSTR )SecurityDescriptorString;
  427. //
  428. // skip any spaces
  429. //
  430. while(*Curr == L' ' ) {
  431. Curr++;
  432. }
  433. //
  434. // There must be a DACL entry (SDDL_DACL is a 1-char string)
  435. //
  436. if (*Curr != SDDL_DACL[0]) {
  437. return STATUS_INVALID_PARAMETER;
  438. } else {
  439. Curr++;
  440. }
  441. if ( *Curr != SDDL_DELIMINATORC ) {
  442. return STATUS_INVALID_PARAMETER;
  443. } else {
  444. Curr++;
  445. }
  446. //
  447. // Look for the protected control flag. We will set the SE_DACL_DEFAULTED
  448. // bit if the ACL is being built using a default mechanism.
  449. //
  450. ControlFlags = SuppliedByDefaultMechanism ? SE_DACL_DEFAULTED : 0;
  451. if (*Curr == SDDL_PROTECTED[0]) {
  452. //
  453. // This flag doesn't do much for device objects. However, we do not
  454. // want to discourage it, as it's use makes sense in a lot of other
  455. // contexts!
  456. //
  457. Curr++;
  458. ControlFlags |= SE_DACL_PROTECTED;
  459. }
  460. //
  461. // Get the DACL corresponding to this SDDL string
  462. //
  463. Status = SepSddlGetAclForString( Curr, &Dacl, &End );
  464. if ( Status == STATUS_SUCCESS ) {
  465. Curr = End;
  466. while(*Curr == L' ' ) {
  467. Curr++;
  468. }
  469. if (*Curr != L'\0') {
  470. Status = STATUS_INVALID_PARAMETER;
  471. }
  472. }
  473. if ( Status == STATUS_SUCCESS ) {
  474. *DiscretionaryAcl = Dacl;
  475. *SecurityDescriptorControlFlags = ControlFlags;
  476. } else {
  477. if ( Dacl ) {
  478. ExFreePool( Dacl );
  479. Dacl = NULL;
  480. }
  481. }
  482. return Status;
  483. }
  484. NTSTATUS
  485. SepSddlGetSidForString(
  486. IN PWSTR String,
  487. OUT PSID *SID,
  488. OUT PWSTR *End
  489. )
  490. /*++
  491. Routine Description:
  492. This routine will determine which sid is an appropriate match for the
  493. given string, either as a sid moniker or as a string representation of a
  494. sid (ie: "DA" or "S-1-0-0" )
  495. Arguments:
  496. String - The string to be converted
  497. Sid - Where the created SID is to be returned. May receive NULL if the
  498. specified SID doesn't exist for the current platform!
  499. End - Where in the string we stopped processing
  500. Return Value:
  501. STATUS_SUCCESS - success
  502. STATUS_NONE_MAPPED - An invalid format of the SID was given
  503. --*/
  504. {
  505. ULONG_PTR sidOffset;
  506. ULONG i;
  507. //
  508. // Set our end of string pointer
  509. //
  510. for ( i = 0; i < ARRAY_COUNT(SidLookup); i++ ) {
  511. //
  512. // check for the current key first
  513. //
  514. if ( _wcsnicmp( String, SidLookup[i].Key, SidLookup[i].KeyLen ) == 0 ) {
  515. *End = String += SidLookup[i].KeyLen;
  516. #ifndef _KERNELIMPLEMENTATION_
  517. if ((SidLookup[i].OsVer == WINXP_OR_LATER) &&
  518. (!IoIsWdmVersionAvailable(1, 0x20))) {
  519. *SID = NULL;
  520. } else {
  521. sidOffset = SidLookup[ i ].ExportSidFieldOffset;
  522. *SID = *((PSID *) (((PUCHAR) SeExports) + sidOffset));
  523. }
  524. #else
  525. *SID = *SidLookup[ i ].Sid;
  526. #endif
  527. return STATUS_SUCCESS;
  528. }
  529. }
  530. *SID = NULL;
  531. return STATUS_NONE_MAPPED;
  532. }
  533. LOGICAL
  534. SepSddlLookupAccessMaskInTable(
  535. IN PWSTR String,
  536. OUT ULONG *AccessMask,
  537. OUT PWSTR *End
  538. )
  539. /*++
  540. Routine Description:
  541. This routine will determine if the given access mask or string right exists
  542. in the lookup table.
  543. A pointer to the matching static lookup entry is returned.
  544. Arguments:
  545. String - The string to be looked up
  546. AccessMask - Receives access mask if match is found.
  547. End - Adjusted string pointer
  548. Return Value:
  549. TRUE if found, FALSE otherwise.
  550. --*/
  551. {
  552. ULONG i;
  553. for ( i = 0; i < ARRAY_COUNT(RightsLookup); i++ ) {
  554. if ( _wcsnicmp( String, RightsLookup[ i ].Key, RightsLookup[ i ].KeyLen ) == 0 ) {
  555. //
  556. // If a match was found, return it
  557. //
  558. *AccessMask = RightsLookup[ i ].Value;
  559. *End = String + RightsLookup[ i ].KeyLen;
  560. return TRUE;
  561. }
  562. }
  563. *AccessMask = 0;
  564. *End = String;
  565. return FALSE;
  566. }
  567. NTSTATUS
  568. SepSddlGetAclForString(
  569. IN PWSTR AclString,
  570. OUT PACL *Acl,
  571. OUT PWSTR *End
  572. )
  573. /*++
  574. Routine Description:
  575. This routine convert a string into an ACL. The format of the aces is:
  576. Ace := ( Type; Flags; Rights; ObjGuid; IObjGuid; Sid;
  577. Type : = A | D | OA | OD {Access, Deny, ObjectAccess, ObjectDeny}
  578. Flags := Flags Flag
  579. Flag : = CI | IO | NP | SA | FA {Container Inherit,Inherit Only, NoProp,
  580. SuccessAudit, FailAdit }
  581. Rights := Rights Right
  582. Right := DS_READ_PROPERTY | blah blah
  583. Guid := String representation of a GUID (via RPC UuidToString)
  584. Sid := DA | PS | AO | PO | AU | S-* (Domain Admins, PersonalSelf, Acct Ops,
  585. PrinterOps, AuthenticatedUsers, or
  586. the string representation of a sid)
  587. The seperator is a ';'.
  588. The returned ACL must be free via a call to ExFreePool
  589. Arguments:
  590. AclString - The string to be converted
  591. Acl - Where the created ACL is to be returned
  592. End - Where in the string we stopped processing
  593. Return Value:
  594. STATUS_SUCCESS indicates success
  595. STATUS_INSUFFICIENT_RESOURCES indicates a memory allocation for the ouput
  596. acl failed
  597. STATUS_INVALID_PARAMETER The string does not represent an ACL
  598. --*/
  599. {
  600. NTSTATUS Status = STATUS_SUCCESS;
  601. ULONG AclSize = 0, AclUsed = 0;
  602. ULONG Aces = 0, i, j;
  603. ULONG AccessMask;
  604. PWSTR Curr, MaskEnd;
  605. LOGICAL OpRes;
  606. PSTRSD_KEY_LOOKUP MatchedEntry;
  607. PSID SidPtr = NULL;
  608. //
  609. // First, we'll have to go through and count the number of entries that
  610. // we have. We'll do the by computing the length of this ACL (which is
  611. // delimited by either the end of the list or a ':' that seperates a key
  612. // from a value
  613. //
  614. *Acl = NULL;
  615. *End = wcschr( AclString, SDDL_DELIMINATORC );
  616. if ( *End == AclString ) {
  617. return STATUS_INVALID_PARAMETER;
  618. }
  619. if ( *End == NULL ) {
  620. *End = AclString + wcslen( AclString );
  621. } else {
  622. ( *End )--;
  623. }
  624. //
  625. // Now, do the count
  626. //
  627. Curr = AclString;
  628. OpRes = 0;
  629. while ( Curr < *End ) {
  630. if ( *Curr == SDDL_SEPERATORC ) {
  631. Aces++;
  632. } else if ( *Curr != L' ' ) {
  633. OpRes = 1;
  634. }
  635. Curr++;
  636. }
  637. //
  638. // Now, we've counted the total number of seperators. Make sure we
  639. // have the right number. (There is 5 seperators per ace)
  640. //
  641. if ( Aces % 5 == 0 ) {
  642. if ( Aces == 0 && OpRes ) {
  643. //
  644. // gabbage chars in between
  645. //
  646. Status = STATUS_INVALID_PARAMETER;
  647. } else {
  648. Aces = Aces / 5;
  649. }
  650. } else {
  651. Status = STATUS_INVALID_PARAMETER;
  652. }
  653. //
  654. // This is an empty ACL (ie no access to anyone, including the system)
  655. //
  656. if (( Status == STATUS_SUCCESS ) && ( Aces == 0 )) {
  657. *Acl = ExAllocatePoolWithTag( PagedPool, sizeof( ACL ), POOLTAG_SEACL );
  658. if ( *Acl == NULL ) {
  659. Status = STATUS_INSUFFICIENT_RESOURCES;
  660. } else {
  661. RtlZeroMemory( *Acl, sizeof( ACL ));
  662. ( *Acl )->AclRevision = ACL_REVISION;
  663. ( *Acl )->Sbz1 = ( UCHAR )0;
  664. ( *Acl )->AclSize = ( USHORT )sizeof( ACL );
  665. ( *Acl )->AceCount = 0;
  666. ( *Acl )->Sbz2 = ( USHORT )0;
  667. }
  668. return Status;
  669. }
  670. //
  671. // Ok now do the allocation. We'll do a sort of worst case initial
  672. // allocation. This saves us from having to process everything twice
  673. // (once to size, once to build). If we determine later that we have
  674. // an acl that is not big enough, we allocate additional space. The only
  675. // time that this reallocation should happen is if the input string
  676. // contains a lot of explicit SIDs. Otherwise, the chosen buffer size
  677. // should be pretty close to the proper size
  678. //
  679. if ( Status == STATUS_SUCCESS ) {
  680. AclSize = sizeof( ACL ) + ( Aces * ( sizeof( ACCESS_ALLOWED_ACE ) +
  681. sizeof( SID ) + ( 6 * sizeof( ULONG ) ) ) );
  682. if ( AclSize > SDDL_MAX_ACL_SIZE ) {
  683. AclSize = SDDL_MAX_ACL_SIZE;
  684. }
  685. *Acl = ( PACL ) ExAllocatePoolWithTag( PagedPool, AclSize, POOLTAG_SEACL );
  686. if ( *Acl == NULL ) {
  687. Status = STATUS_INSUFFICIENT_RESOURCES;
  688. } else {
  689. AclUsed = sizeof( ACL );
  690. RtlZeroMemory( *Acl, AclSize );
  691. //
  692. // We'll start initializing it...
  693. //
  694. ( *Acl )->AclRevision = ACL_REVISION;
  695. ( *Acl )->Sbz1 = ( UCHAR )0;
  696. ( *Acl )->AclSize = ( USHORT )AclSize;
  697. ( *Acl )->AceCount = 0;
  698. ( *Acl )->Sbz2 = ( USHORT )0;
  699. //
  700. // Ok, now we'll go through and start building them all
  701. //
  702. Curr = AclString;
  703. for( i = 0; i < Aces; i++ ) {
  704. //
  705. // First, get the type..
  706. //
  707. UCHAR Flags = 0;
  708. USHORT Size;
  709. ACCESS_MASK Mask = 0;
  710. PWSTR Next;
  711. ULONG AceSize = 0;
  712. //
  713. // skip any space before (
  714. //
  715. while(*Curr == L' ' ) {
  716. Curr++;
  717. }
  718. //
  719. // Skip any parens that may exist in the ace list
  720. //
  721. if ( *Curr == SDDL_ACE_BEGINC ) {
  722. Curr++;
  723. }
  724. //
  725. // skip any space after (
  726. //
  727. while(*Curr == L' ' ) {
  728. Curr++;
  729. }
  730. //
  731. // Look for an allow ACE
  732. //
  733. if ( _wcsnicmp( Curr, SDDL_ACCESS_ALLOWED, SDDL_LEN_TAG( SDDL_ACCESS_ALLOWED ) ) == 0 ) {
  734. Curr += SDDL_LEN_TAG( SDDL_ACCESS_ALLOWED ) + 1;
  735. } else {
  736. //
  737. // Found an invalid type
  738. //
  739. Status = STATUS_INVALID_PARAMETER;
  740. break;
  741. }
  742. //
  743. // skip any space before ;
  744. //
  745. while(*Curr == L' ' ) {
  746. Curr++;
  747. }
  748. //
  749. // This function doesn't support any ACE Flags. As such, any
  750. // flags found are invalid
  751. //
  752. if ( *Curr == SDDL_SEPERATORC ) {
  753. Curr++;
  754. } else {
  755. Status = STATUS_INVALID_PARAMETER;
  756. break;
  757. }
  758. //
  759. // skip any space after ;
  760. //
  761. while(*Curr == L' ' ) {
  762. Curr++;
  763. }
  764. //
  765. // Now, get the access mask
  766. //
  767. while( TRUE ) {
  768. if ( *Curr == SDDL_SEPERATORC ) {
  769. Curr++;
  770. break;
  771. }
  772. //
  773. // Skip any blanks
  774. //
  775. while ( *Curr == L' ' ) {
  776. Curr++;
  777. }
  778. if (SepSddlLookupAccessMaskInTable( Curr, &AccessMask, &MaskEnd )) {
  779. Mask |= AccessMask;
  780. Curr = MaskEnd;
  781. } else {
  782. //
  783. // If the rights couldn't be looked up, see if it's a
  784. // converted mask
  785. //
  786. #ifndef _KERNELIMPLEMENTATION_
  787. SepSddlParseWideStringUlong(Curr, &MaskEnd, &Mask);
  788. #else
  789. Mask = wcstoul( Curr, &MaskEnd, 0 );
  790. #endif
  791. if ( MaskEnd != Curr ) {
  792. Curr = MaskEnd;
  793. } else {
  794. //
  795. // Found an invalid right
  796. //
  797. Status = STATUS_INVALID_PARAMETER;
  798. break;
  799. }
  800. }
  801. }
  802. if ( Status != STATUS_SUCCESS ) {
  803. break;
  804. }
  805. //
  806. // If that worked, we'll get the ids
  807. //
  808. for ( j = 0; j < 2; j++ ) {
  809. //
  810. // skip any space before ;
  811. //
  812. while(*Curr == L' ' ) {
  813. Curr++;
  814. }
  815. if ( *Curr != SDDL_SEPERATORC ) {
  816. //
  817. // Object GUIDs are not supported, as this function
  818. // currently doesn't handle object-allow ACEs.
  819. //
  820. Status = STATUS_INVALID_PARAMETER;
  821. }
  822. Curr++;
  823. }
  824. if ( Status != STATUS_SUCCESS ) {
  825. break;
  826. }
  827. //
  828. // skip any space before ;
  829. //
  830. while(*Curr == L' ' ) {
  831. Curr++;
  832. }
  833. //
  834. // Finally, the SID
  835. //
  836. if ( STATUS_SUCCESS == Status ) {
  837. PWSTR EndLocation;
  838. Status = SepSddlGetSidForString( Curr, &SidPtr, &EndLocation );
  839. if ( Status == STATUS_SUCCESS ) {
  840. if ( EndLocation == NULL ) {
  841. Status = STATUS_INVALID_ACL;
  842. } else {
  843. while(*EndLocation == L' ' ) {
  844. EndLocation++;
  845. }
  846. //
  847. // a ace must be terminated by ')'
  848. //
  849. if ( *EndLocation != SDDL_ACE_ENDC ) {
  850. Status = STATUS_INVALID_ACL;
  851. } else {
  852. Curr = EndLocation + 1;
  853. }
  854. }
  855. }
  856. }
  857. //
  858. // Quit on an error
  859. //
  860. if ( Status != STATUS_SUCCESS ) {
  861. break;
  862. }
  863. //
  864. // Note that the SID pointer may be NULL if the SID wasn't
  865. // relevant for this OS version.
  866. //
  867. if (SidPtr != NULL) {
  868. //
  869. // Now, we'll create the ace, and add it...
  870. //
  871. Status = SepSddlAddAceToAcl( Acl,
  872. &AclUsed,
  873. ACCESS_ALLOWED_ACE_TYPE,
  874. Flags,
  875. Mask,
  876. ( Aces - i ),
  877. SidPtr );
  878. //
  879. // Handle any errors
  880. //
  881. if ( Status != STATUS_SUCCESS ) {
  882. break;
  883. }
  884. }
  885. if ( *Curr == SDDL_ACE_BEGINC ) {
  886. Curr++;
  887. }
  888. }
  889. //
  890. // If something didn't work, clean up
  891. //
  892. if ( Status != STATUS_SUCCESS ) {
  893. ExFreePool( *Acl );
  894. *Acl = NULL;
  895. } else {
  896. //
  897. // Set a more realistic acl size
  898. //
  899. ( *Acl )->AclSize = ( USHORT )AclUsed;
  900. }
  901. }
  902. }
  903. return Status;
  904. }
  905. NTSTATUS
  906. SepSddlAddAceToAcl(
  907. IN OUT PACL *Acl,
  908. IN OUT ULONG *TrueAclSize,
  909. IN ULONG AceType,
  910. IN ULONG AceFlags,
  911. IN ULONG AccessMask,
  912. IN ULONG RemainingAces,
  913. IN PSID SidPtr
  914. )
  915. /*++
  916. Routine Description:
  917. This routine adds an ACE to the passed in ACL, growing the ACL size as
  918. neccessary.
  919. Arguments:
  920. Acl - Specifies the ACL to receive the new ACE. May be reallocated if
  921. Acl->AclSize cannot contain the ACE.
  922. TrueAclSize - Contains the true working size of the ACL (as opposed to
  923. Acl->AclSize, which may be bigger for performance reasons)
  924. AceType - Type of ACE to add. Currently, only ACCESS_ALLOW ACEs are
  925. supported.
  926. AceFlags - Ace control flags, specifying inheritance, etc.
  927. *Currently this must be zero*!!!!
  928. AccessMask - Contains the ACCESS rights mask for the ACE
  929. SID - Contains the SID for the ACE.
  930. Return Value:
  931. STATUS_SUCCESS indicates success
  932. STATUS_INSUFFICIENT_RESOURCES indicates a memory allocation for the ouput
  933. acl failed
  934. --*/
  935. {
  936. PACL WorkingAcl;
  937. ULONG WorkingAclSize;
  938. ULONG AceSize;
  939. ASSERT(AceType == ACCESS_ALLOWED_ACE_TYPE);
  940. ASSERT(RemainingAces != 0);
  941. #ifndef _KERNELIMPLEMENTATION_
  942. ASSERT(AceFlags == 0);
  943. #endif
  944. WorkingAcl = *Acl;
  945. WorkingAclSize = *TrueAclSize;
  946. //
  947. // First, make sure we have the room for it
  948. // ACCESS_ALLOWED_ACE_TYPE:
  949. //
  950. AceSize = sizeof( ACCESS_ALLOWED_ACE );
  951. AceSize += RtlLengthSid( SidPtr ) - sizeof( ULONG );
  952. if (AceSize + WorkingAclSize > WorkingAcl->AclSize) {
  953. //
  954. // We'll have to reallocate, since our buffer isn't big enough. Assume
  955. // all the remaining ACE's will be as big as this one is...
  956. //
  957. PACL NewAcl;
  958. ULONG NewSize = WorkingAclSize + ( RemainingAces * AceSize );
  959. NewAcl = ( PACL ) ExAllocatePoolWithTag( PagedPool, NewSize, POOLTAG_SEACL );
  960. if ( NewAcl == NULL ) {
  961. return STATUS_INSUFFICIENT_RESOURCES;
  962. } else {
  963. //
  964. // Copy over the new data.
  965. //
  966. RtlZeroMemory( NewAcl, NewSize);
  967. RtlCopyMemory( NewAcl, *Acl, WorkingAclSize );
  968. NewAcl->AclSize = ( USHORT )NewSize;
  969. ExFreePool( WorkingAcl );
  970. *Acl = NewAcl;
  971. WorkingAcl = NewAcl;
  972. }
  973. }
  974. WorkingAclSize += AceSize;
  975. *TrueAclSize = WorkingAclSize;
  976. #ifndef _KERNELIMPLEMENTATION_
  977. //
  978. // Our ACE is an Allow ACE
  979. //
  980. return RtlAddAccessAllowedAce( WorkingAcl,
  981. ACL_REVISION,
  982. AccessMask,
  983. SidPtr );
  984. #else
  985. //
  986. // This version is not exported by the kernel today...
  987. //
  988. return RtlAddAccessAllowedAceEx( WorkingAcl,
  989. ACL_REVISION,
  990. AceFlags,
  991. AccessMask,
  992. SidPtr );
  993. #endif // _KERNELIMPLEMENTATION_
  994. }
  995. #ifndef _KERNELIMPLEMENTATION_
  996. LOGICAL
  997. SepSddlParseWideStringUlong(
  998. IN LPCWSTR Buffer,
  999. OUT LPCWSTR *FinalPosition,
  1000. OUT ULONG *Value
  1001. )
  1002. /*++
  1003. Routine Description:
  1004. This routine parses a wide string for an unsigned long, in a similar
  1005. fashion to wcstoul. It exists because not all CRT library string functions
  1006. are exported by the kernel today.
  1007. Arguments:
  1008. Buffer - Points to location in string to begin parsing.
  1009. FinalPosition - Receives final string location, Buffer on error.
  1010. Value - Receives value parsed by routine, 0 on error.
  1011. Return Value:
  1012. TRUE if the parse succeeded, FALSE if it failed.
  1013. --*/
  1014. {
  1015. ULONG oldValue, newValue, newDigit, base;
  1016. LPCWSTR curr, initial;
  1017. PAGED_CODE();
  1018. //
  1019. // Preinit
  1020. //
  1021. *Value = 0;
  1022. *FinalPosition = Buffer;
  1023. initial = Buffer;
  1024. curr = initial;
  1025. if ((curr[0] == L'0') && ((curr[1] == L'x') || (curr[1] == L'X'))) {
  1026. //
  1027. // Starts with 0x, skip the rest.
  1028. //
  1029. initial += 2;
  1030. curr = initial;
  1031. base = 16;
  1032. } else if ((curr[0] >= L'0') && (curr[0] <= L'9')) {
  1033. base = 10;
  1034. } else {
  1035. base = 16;
  1036. }
  1037. oldValue = 0;
  1038. while(curr[0]) {
  1039. if ((curr[0] >= L'0') && (curr[0] <= L'9')) {
  1040. newDigit = curr[0] - L'0';
  1041. } else if ((base == 16) && (curr[0] >= L'A') && (curr[0] <= L'F')) {
  1042. newDigit = curr[0] - L'A' + 10;
  1043. } else if ((base == 16) && (curr[0] >= L'a') && (curr[0] <= L'f')) {
  1044. newDigit = curr[0] - L'a' + 10;
  1045. } else {
  1046. break;
  1047. }
  1048. newValue = (oldValue * base) + newDigit;
  1049. if (newValue < oldValue) {
  1050. //
  1051. // Wrapped, too many digits
  1052. //
  1053. return FALSE;
  1054. }
  1055. oldValue = newValue;
  1056. curr++;
  1057. }
  1058. //
  1059. // No real digits were found.
  1060. //
  1061. if (curr == initial) {
  1062. return FALSE;
  1063. }
  1064. *FinalPosition = curr;
  1065. *Value = oldValue;
  1066. return TRUE;
  1067. }
  1068. #endif // _KERNELIMPLEMENTATION_