/*++ Copyright (c) 1992 Microsoft Corporation Module Name: regacl.c Abstract: This provides routines to parse the ACE lists present in the regini text input files. It also provides routines to create the appropriate security descriptor from the list of ACEs. Author: John Vert (jvert) 15-Sep-1992 Notes: This is based on the SETACL program used in SETUP, written by RobertRe Revision History: John Vert (jvert) 15-Sep-1992 created Lonny McMichael (lonnym) 25-March-1999 added new predefined ACEs (UserR and PowerR) --*/ #include #include #include #include #include #include #include #include // // Private function prototypes // BOOLEAN RegpInitializeACEs( VOID ); // // Universal well-known SIDs // PSID SeNullSid; PSID SeWorldSid; PSID SeCreatorOwnerSid; PSID SeInteractiveUserSid; PSID SeTerminalUserSid; // // SIDs defined by NT // PSID SeNtAuthoritySid; PSID SeLocalSystemSid; PSID SeLocalAdminSid; PSID SeAliasAdminsSid; PSID SeAliasSystemOpsSid; PSID SeAliasPowerUsersSid; PSID SeAliasUsersSid; SID_IDENTIFIER_AUTHORITY SepNullSidAuthority = SECURITY_NULL_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY SepWorldSidAuthority = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY SepLocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY SepCreatorSidAuthority = SECURITY_CREATOR_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY SepNtAuthority = SECURITY_NT_AUTHORITY; // // SID of primary domain, and admin account in that domain. // PSID SepPrimaryDomainSid; PSID SepPrimaryDomainAdminSid; // // Number of ACEs currently defined // #define ACE_COUNT 32 typedef struct _ACE_DATA { ACCESS_MASK AccessMask; PSID *Sid; UCHAR AceType; UCHAR AceFlags; } ACE_DATA, *PACE_DATA; // // Table describing the data to put into each ACE. // // This table is read during initialization and used to construct a // series of ACEs. The index of each ACE in the Aces array defined below // corresponds to the ordinals used in the input data file. // ACE_DATA AceDataTable[ACE_COUNT] = { { 0, NULL, 0, 0 }, // // ACE 1 - ADMIN Full // { KEY_ALL_ACCESS, &SeAliasAdminsSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 2 - ADMIN Read // { KEY_READ, &SeAliasAdminsSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 3 - ADMIN Read Write // { KEY_READ | KEY_WRITE, &SeAliasAdminsSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 4 - ADMIN Read Write Delete // { KEY_READ | KEY_WRITE | DELETE, &SeAliasAdminsSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 5 - Creator Full // { KEY_ALL_ACCESS, &SeCreatorOwnerSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 6 - Creator Read Write // { KEY_READ | KEY_WRITE, &SeCreatorOwnerSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 7 - World Full // { KEY_ALL_ACCESS, &SeWorldSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 8 - World Read // { KEY_READ, &SeWorldSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 9 - World Read Write // { KEY_READ | KEY_WRITE, &SeWorldSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 10 - World Read Write Delete // { KEY_READ | KEY_WRITE | DELETE, &SeWorldSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 11 - PowerUser Full // { KEY_ALL_ACCESS, &SeAliasPowerUsersSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 12 - PowerUser Read Write // { KEY_READ | KEY_WRITE, &SeAliasPowerUsersSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 13 - PowerUser Read Write Delete // { KEY_READ | KEY_WRITE | DELETE, &SeAliasPowerUsersSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 14 - System Ops Full // { KEY_ALL_ACCESS, &SeAliasSystemOpsSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 15 - System Ops Read Write // { KEY_READ | KEY_WRITE, &SeAliasSystemOpsSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 16 - System Ops Read Write Delete // { KEY_READ | KEY_WRITE | DELETE, &SeAliasSystemOpsSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 17 - System Full // { KEY_ALL_ACCESS, &SeLocalSystemSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 18 - System Read Write // { KEY_READ | KEY_WRITE, &SeLocalSystemSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 19 - System Read // { KEY_READ, &SeLocalSystemSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 20 - ADMIN Read Write Execute // { KEY_READ | KEY_WRITE | KEY_EXECUTE, &SeAliasAdminsSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 21 - Interactive User Full // { KEY_ALL_ACCESS, &SeInteractiveUserSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 22 - Interactive User Read // { KEY_READ, &SeInteractiveUserSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 23 - Interactive User Read Write // { KEY_READ | KEY_WRITE, &SeInteractiveUserSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 24 - Interactive User Read Write Delete // { KEY_READ | KEY_WRITE | DELETE, &SeInteractiveUserSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 25 - Normal Users Read / Write // { KEY_READ | KEY_WRITE, &SeAliasUsersSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 26 - Terminal User Full // { KEY_ALL_ACCESS, &SeTerminalUserSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 27 - Terminal User Read // { KEY_READ, &SeTerminalUserSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 28 - Terminal User Read Write // { KEY_READ | KEY_WRITE, &SeTerminalUserSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 29 - Terminal User Read Write Delete // { KEY_READ | KEY_WRITE | DELETE, &SeTerminalUserSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 30 - Normal Users Read // { KEY_READ, &SeAliasUsersSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE }, // // ACE 31 - PowerUser Read // { KEY_READ, &SeAliasPowerUsersSid, ACCESS_ALLOWED_ACE_TYPE, CONTAINER_INHERIT_ACE } }; PKNOWN_ACE Aces[ACE_COUNT]; BOOLEAN RegInitializeSecurity( VOID ) /*++ Routine Description: This routine initializes the defined ACEs. It must be called before any of the routines to create security descriptors Arguments: None. Return Value: TRUE - initialization successful FALSE - initialization failed --*/ { NTSTATUS Status; SID_IDENTIFIER_AUTHORITY NullSidAuthority; SID_IDENTIFIER_AUTHORITY WorldSidAuthority; SID_IDENTIFIER_AUTHORITY LocalSidAuthority; SID_IDENTIFIER_AUTHORITY CreatorSidAuthority; SID_IDENTIFIER_AUTHORITY SeNtAuthority; NullSidAuthority = SepNullSidAuthority; WorldSidAuthority = SepWorldSidAuthority; LocalSidAuthority = SepLocalSidAuthority; CreatorSidAuthority = SepCreatorSidAuthority; SeNtAuthority = SepNtAuthority; SeNullSid = (PSID)RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthRequiredSid(1) ); SeWorldSid = (PSID)RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthRequiredSid(1) ); SeCreatorOwnerSid = (PSID)RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthRequiredSid(1) ); SeInteractiveUserSid = (PSID)RtlAllocateHeap(RtlProcessHeap(), 0, RtlLengthRequiredSid(2) ); SeTerminalUserSid = (PSID)RtlAllocateHeap(RtlProcessHeap(), 0, RtlLengthRequiredSid(2) ); // // Fail initialization if we didn't get enough memory for the universal // SIDs // if (SeNullSid==NULL || SeWorldSid==NULL || SeCreatorOwnerSid==NULL || SeInteractiveUserSid == NULL || SeTerminalUserSid == NULL ) { return FALSE; } Status = RtlInitializeSid(SeNullSid, &NullSidAuthority, 1); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid(SeWorldSid, &WorldSidAuthority, 1); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid(SeCreatorOwnerSid, &CreatorSidAuthority, 1); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid( SeInteractiveUserSid, &SeNtAuthority, 1 ); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid( SeTerminalUserSid, &SeNtAuthority, 1 ); if (!NT_SUCCESS( Status )) { return FALSE; } *(RtlSubAuthoritySid(SeNullSid, 0)) = SECURITY_NULL_RID; *(RtlSubAuthoritySid(SeWorldSid, 0)) = SECURITY_WORLD_RID; *(RtlSubAuthoritySid(SeCreatorOwnerSid, 0)) = SECURITY_CREATOR_OWNER_RID; *(RtlSubAuthoritySid(SeInteractiveUserSid, 0 )) = SECURITY_INTERACTIVE_RID; *(RtlSubAuthoritySid(SeTerminalUserSid, 0 )) = SECURITY_TERMINAL_SERVER_RID; // // Allocate and initialize the NT defined SIDs // SeNtAuthoritySid = (PSID)RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthRequiredSid(0) ); SeLocalSystemSid = (PSID)RtlAllocateHeap( RtlProcessHeap(), 0, RtlLengthRequiredSid(1) ); SeAliasAdminsSid = (PSID)RtlAllocateHeap(RtlProcessHeap(), 0, RtlLengthRequiredSid(2) ); SeAliasSystemOpsSid = (PSID)RtlAllocateHeap(RtlProcessHeap(), 0, RtlLengthRequiredSid(2) ); SeAliasPowerUsersSid = (PSID)RtlAllocateHeap(RtlProcessHeap(), 0, RtlLengthRequiredSid(2) ); SeAliasUsersSid = (PSID)RtlAllocateHeap(RtlProcessHeap(), 0, RtlLengthRequiredSid(2) ); // // fail initialization if we couldn't allocate memory for the NT SIDs // if (SeNtAuthoritySid == NULL || SeLocalSystemSid == NULL || SeAliasAdminsSid == NULL || SeAliasPowerUsersSid == NULL || SeAliasSystemOpsSid == NULL ) { return FALSE; } Status = RtlInitializeSid( SeNtAuthoritySid, &SeNtAuthority, 0 ); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid( SeLocalSystemSid, &SeNtAuthority, 1 ); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid( SeAliasAdminsSid, &SeNtAuthority, 2 ); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid( SeAliasSystemOpsSid, &SeNtAuthority, 2 ); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid( SeAliasPowerUsersSid, &SeNtAuthority, 2 ); if (!NT_SUCCESS( Status )) { return FALSE; } Status = RtlInitializeSid( SeAliasUsersSid, &SeNtAuthority, 2 ); if (!NT_SUCCESS( Status )) { return FALSE; } *(RtlSubAuthoritySid( SeLocalSystemSid, 0 )) = SECURITY_LOCAL_SYSTEM_RID; *(RtlSubAuthoritySid( SeAliasAdminsSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; *(RtlSubAuthoritySid( SeAliasAdminsSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS; *(RtlSubAuthoritySid( SeAliasSystemOpsSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; *(RtlSubAuthoritySid( SeAliasSystemOpsSid, 1 )) = DOMAIN_ALIAS_RID_SYSTEM_OPS; *(RtlSubAuthoritySid( SeAliasPowerUsersSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; *(RtlSubAuthoritySid( SeAliasPowerUsersSid, 1 )) = DOMAIN_ALIAS_RID_POWER_USERS; *(RtlSubAuthoritySid( SeAliasUsersSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID; *(RtlSubAuthoritySid( SeAliasUsersSid, 1 )) = DOMAIN_ALIAS_RID_USERS; // // The SIDs have been successfully created. Now create the table of ACEs // return RegpInitializeACEs(); } BOOLEAN RegpInitializeACEs( VOID ) /*++ Routine Description: Initializes the table of ACEs described in the AceDataTable. This is called at initialization time by RiInitializeSecurity after the SIDs have been created. Arguments: None. Return Value: TRUE - ACEs successfully constructed. FALSE - initialization failed. --*/ { ULONG i; ULONG LengthRequired; NTSTATUS Status; for (i=1; iHeader.AceType = AceDataTable[i].AceType; Aces[i]->Header.AceFlags = AceDataTable[i].AceFlags; Aces[i]->Header.AceSize = (USHORT)LengthRequired; Aces[i]->Mask = AceDataTable[i].AccessMask; Status = RtlCopySid( RtlLengthSid(*(AceDataTable[i].Sid)), &Aces[i]->SidStart, *(AceDataTable[i].Sid) ); if (!NT_SUCCESS( Status )) { return FALSE; } } return TRUE; } BOOLEAN RegUnicodeToDWORD( IN OUT PWSTR *String, IN ULONG Base OPTIONAL, OUT PULONG Value ) { PCWSTR s; WCHAR c, Sign; ULONG nChars, Result, Digit, Shift; s = *String; Sign = UNICODE_NULL; while (*s != UNICODE_NULL && *s <= ' ') { s += 1; } c = *s; if (c == L'-' || c == L'+') { Sign = c; c = *++s; } if (Base == 0) { Base = 10; Shift = 0; if (c == L'0') { c = *++s; if (c == L'x') { c = *++s; Base = 16; Shift = 4; } else if (c == L'o') { c = *++s; Base = 8; Shift = 3; } else if (c == L'b') { c = *++s; Base = 2; Shift = 1; } else { c = *--s; } } } else { switch( Base ) { case 16: Shift = 4; break; case 8: Shift = 3; break; case 2: Shift = 1; break; case 10: Shift = 0; break; default: return FALSE; } } // // Return an error if end of string before we start // if (c == UNICODE_NULL) { return FALSE; } Result = 0; while (c != UNICODE_NULL) { if (c >= L'0' && c <= L'9') { Digit = c - L'0'; } else if (c >= L'A' && c <= L'F') { Digit = c - L'A' + 10; } else if (c >= L'a' && c <= L'f') { Digit = c - L'a' + 10; } else { break; } if (Digit >= Base) { break; } if (Shift == 0) { Result = (Base * Result) + Digit; } else { Result = (Result << Shift) | Digit; } c = *++s; } if (Sign == L'-') { Result = (ULONG)(-(LONG)Result); } try { *String = (PWSTR)s; *Value = Result; } except( EXCEPTION_EXECUTE_HANDLER ) { return FALSE; } return TRUE; } BOOLEAN RegCreateSecurity( IN PWSTR AclStart, OUT PSECURITY_DESCRIPTOR SecurityDescriptor ) /*++ Routine Description: Computes the appropriate security descriptor based on a string of the form "1 2 3 ..." where each number is the index of a particular ACE from the pre-defined list of ACEs. Arguments: AclStart - Supplies a unicode string representing a list of ACEs SecurityDescriptor - Returns the initialized security descriptor that represents all the ACEs supplied Return Value: TRUE if successful and FALSE if not. --*/ { PWSTR p; PWSTR StringEnd, StringStart; ULONG AceCount=0; ULONG AceIndex; ULONG i; PACL Acl; NTSTATUS Status; // // First we need to count the number of ACEs in the ACL. // p=AclStart; StringEnd = AclStart + wcslen( AclStart ); // // strip leading white space // while ((*p == L' ' || *p == L'\t') && p != StringEnd) { p += 1; } StringStart = p; // // Count number of digits in the string // while (p != StringEnd) { if (iswdigit( *p )) { ++AceCount; do { p += 1; } while (iswdigit( *p ) && p != StringEnd); } else { p += 1; } } Acl = RtlAllocateHeap( RtlProcessHeap(), 0, 256 ); if (Acl == NULL) { return FALSE; } Status = RtlCreateAcl( Acl, 256, ACL_REVISION2 ); if (!NT_SUCCESS( Status )) { RtlFreeHeap( RtlProcessHeap(), 0, Acl ); return FALSE; } p = StringStart; for (i=0; iHeader.AceSize ); if (!NT_SUCCESS( Status )) { RtlFreeHeap( RtlProcessHeap(), 0, Acl ); return FALSE; } } // // We now have an appropriately formed ACL, initialize the security // descriptor. // Status = RtlCreateSecurityDescriptor( SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION ); if (!NT_SUCCESS( Status )) { RtlFreeHeap( RtlProcessHeap(), 0, Acl ); return FALSE; } Status = RtlSetDaclSecurityDescriptor( SecurityDescriptor, TRUE, Acl, FALSE ); if (!NT_SUCCESS( Status )) { RtlFreeHeap( RtlProcessHeap(), 0, Acl ); return FALSE; } return TRUE; } BOOLEAN RegFormatSecurity( IN PSECURITY_DESCRIPTOR SecurityDescriptor, OUT PWSTR AceList ) { NTSTATUS Status; BOOLEAN DaclPresent, DaclDefaulted; PACL Acl; PWSTR s; ULONG AceIndex, MyAceIndex; PKNOWN_ACE Ace; s = AceList; *s = UNICODE_NULL; Acl = NULL; Status = RtlGetDaclSecurityDescriptor( SecurityDescriptor, &DaclPresent, &Acl, &DaclDefaulted ); if (NT_SUCCESS( Status ) && DaclPresent && Acl != NULL) { for (AceIndex=0; AceIndexAceCount; AceIndex++) { Status = RtlGetAce( Acl, AceIndex, &Ace ); if (!NT_SUCCESS( Status )) { return FALSE; } for (MyAceIndex=1; MyAceIndexHeader.AceType == Aces[ MyAceIndex ]->Header.AceType && Ace->Header.AceFlags == Aces[ MyAceIndex ]->Header.AceFlags && Ace->Mask == Aces[ MyAceIndex ]->Mask ) { if (RtlEqualSid( (PSID)&Ace->SidStart, (PSID)&Aces[ MyAceIndex ]->SidStart )) { if (s != AceList) { *s++ = L' '; } s += swprintf( s, L"%d", MyAceIndex ); break; } } } } } *s = UNICODE_NULL; return s != AceList; } VOID RegDestroySecurity( IN PSECURITY_DESCRIPTOR SecurityDescriptor ) /*++ Routine Description: This routine cleans up and destroys a security descriptor that was previously created with RegCreateSecurity. Arguments: SecurityDescriptor - Supplies a pointer to the security descriptor that was previously initialized by RegCreateSecurity. Return Value: None. --*/ { NTSTATUS Status; BOOLEAN DaclPresent, DaclDefaulted; PACL Acl; ULONG AceIndex; PKNOWN_ACE Ace; Acl = NULL; Status = RtlGetDaclSecurityDescriptor( SecurityDescriptor, &DaclPresent, &Acl, &DaclDefaulted ); if (NT_SUCCESS( Status ) && DaclPresent && Acl != NULL) { RtlFreeHeap( RtlProcessHeap(), 0, Acl ); } return; }