NTSTATUS LwRtlAnsiStringAllocateFromCString( OUT PANSI_STRING pNewString, IN PCSTR pszString ) { NTSTATUS status = 0; PSTR pszNewString = NULL; ANSI_STRING newString = { 0 }; status = RtlCStringDuplicate(&pszNewString, pszString); GOTO_CLEANUP_ON_STATUS(status); status = LwRtlAnsiStringInitEx(&newString, pszNewString); GOTO_CLEANUP_ON_STATUS(status); pszNewString = 0; cleanup: if (status) { RtlCStringFree(&pszNewString); RtlAnsiStringFree(&newString); } *pNewString = newString; return status; }
LW_NTSTATUS LwRtlAnsiStringAllocatePrintfV( LW_OUT LW_PANSI_STRING pNewString, LW_IN LW_PCSTR Format, LW_IN va_list Args ) { NTSTATUS status = 0; PSTR pOutputString = NULL; ANSI_STRING newString = { 0 }; status = LwRtlCStringAllocatePrintfV( &pOutputString, Format, Args); GOTO_CLEANUP_ON_STATUS(status); status = LwRtlAnsiStringInitEx(&newString, pOutputString); GOTO_CLEANUP_ON_STATUS(status); pOutputString = NULL; cleanup: if (status) { RTL_ANSI_STRING_FREE(&newString); } RTL_FREE(&pOutputString); *pNewString = newString; return status; }
NTSTATUS RtlGetRidSid( OUT PULONG Rid, IN PSID Sid ) { NTSTATUS status = STATUS_SUCCESS; if (!Rid || !Sid) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP_ON_STATUS(status); } if (Sid->SubAuthorityCount == 0) { status = STATUS_INVALID_SUB_AUTHORITY; GOTO_CLEANUP_ON_STATUS(status); } *Rid = Sid->SubAuthority[Sid->SubAuthorityCount-1]; cleanup: return status; }
LW_NTSTATUS LwRtlWC16StringAllocateFromUnicodeString( LW_OUT LW_PWSTR* ppszNewString, LW_IN LW_PUNICODE_STRING pOriginalString ) { NTSTATUS status = 0; UNICODE_STRING terminatedOriginalString = { 0 }; PWSTR pszNewString = NULL; // Since duplicate always does NULL-termination, we can // safely use the Buffer field as a WC16String. status = LwRtlUnicodeStringDuplicate(&terminatedOriginalString, pOriginalString); GOTO_CLEANUP_ON_STATUS(status); pszNewString = terminatedOriginalString.Buffer; terminatedOriginalString.Buffer = NULL; terminatedOriginalString.Length = 0; terminatedOriginalString.MaximumLength = 0; cleanup: if (!NT_SUCCESS(status)) { RTL_FREE(&pszNewString); } LwRtlUnicodeStringFree(&terminatedOriginalString); *ppszNewString = pszNewString; return status; }
NTSTATUS LwRtlAnsiStringAllocateFromUnicodeString( OUT PANSI_STRING pNewString, IN PUNICODE_STRING pString ) { NTSTATUS status = 0; PSTR pszNewString = NULL; ANSI_STRING newString = { 0 }; status = RtlCStringDuplicate(&pszNewString, pszString); GOTO_CLEANUP_ON_STATUS(status); newString.Buffer = pszNewString; pszNewString = 0; newString.Length = wc16slen(newString.Buffer) * sizeof(newString.Buffer[0]); newString.MaximumLength = newString.Length + sizeof(newString.Buffer[0]); cleanup: if (status) { RtlCStringFree(&pszNewString); RtlAnsiStringFree(&newString); } *pString = newString; return status; }
NTSTATUS RtlDuplicateSid( OUT PSID* NewSid, IN PSID OriginalSid ) { NTSTATUS status = STATUS_SUCCESS; ULONG length = RtlLengthSid(OriginalSid); PSID resultSid = NULL; status = RTL_ALLOCATE(&resultSid, SID, length); GOTO_CLEANUP_ON_STATUS(status); RtlCopyMemory(resultSid, OriginalSid, length); cleanup: if (!NT_SUCCESS(status)) { RTL_FREE(&resultSid); } *NewSid = resultSid; return status; }
static NTSTATUS LwRtlAnsiStringAllocateAppendPrintfV( IN OUT PANSI_STRING pString, IN PCSTR Format, IN va_list Args ) { NTSTATUS status = 0; ANSI_STRING addString = { 0 }; ANSI_STRING newString = { 0 }; status = LwRtlAnsiStringAllocatePrintfV(&addString, Format, Args); GOTO_CLEANUP_ON_STATUS(status); if (pString->Buffer) { status = LwRtlAnsiStringAllocatePrintf(&newString, "%Z%Z", pString, &addString); GOTO_CLEANUP_ON_STATUS(status); } else { newString = addString; LwRtlZeroMemory(&addString, sizeof(addString)); } cleanup: if (status) { LW_RTL_ANSI_STRING_FREE(&newString); } else { LW_RTL_ANSI_STRING_FREE(pString); *pString = newString; } LW_RTL_ANSI_STRING_FREE(&addString); return status; }
static NTSTATUS LwRtlWC16StringAllocateAppendPrintfV( IN OUT PWSTR* ppszString, IN PCSTR pszFormat, IN va_list Args ) { NTSTATUS status = 0; PWSTR pszAddString = NULL; PWSTR pszNewString = NULL; status = LwRtlWC16StringAllocatePrintfV(&pszAddString, pszFormat, Args); GOTO_CLEANUP_ON_STATUS(status); if (*ppszString) { status = LwRtlWC16StringAllocatePrintf(&pszNewString, "%ws%ws", *ppszString, pszAddString); GOTO_CLEANUP_ON_STATUS(status); } else { pszNewString = pszAddString; pszAddString = NULL; } cleanup: if (status) { LwRtlWC16StringFree(&pszNewString); } else { LwRtlWC16StringFree(ppszString); *ppszString = pszNewString; } LwRtlWC16StringFree(&pszAddString); return status; }
static NTSTATUS RtlValidateSelfRelativeSid( PSID pSid, ULONG ulOffset, ULONG ulRelativeSize ) { NTSTATUS status = STATUS_SUCCESS; status = CheckOffset(ulOffset, SID_MIN_SIZE, ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); status = CheckOffset(ulOffset, RtlLengthSid(pSid), ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); cleanup: return status; }
NTSTATUS RtlAllocateAnsiStringFromSid( OUT PANSI_STRING StringSid, IN PSID Sid ) { NTSTATUS status = STATUS_SUCCESS; PSTR resultBuffer = NULL; ANSI_STRING result = { 0 }; if (!StringSid) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } status = RtlAllocateCStringFromSid(&resultBuffer, Sid); GOTO_CLEANUP_ON_STATUS(status); status = RtlAnsiStringInitEx(&result, resultBuffer); GOTO_CLEANUP_ON_STATUS(status); resultBuffer = NULL; status = STATUS_SUCCESS; cleanup: if (!NT_SUCCESS(status)) { RtlAnsiStringFree(&result); } RTL_FREE(&resultBuffer); if (StringSid) { *StringSid = result; } return status; }
NTSTATUS RtlAllocateWC16StringFromSid( OUT PWSTR* StringSid, IN PSID Sid ) { NTSTATUS status = STATUS_SUCCESS; PWSTR result = NULL; PSTR convertString = NULL; if (!StringSid) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } status = RtlAllocateCStringFromSid(&convertString, Sid); GOTO_CLEANUP_ON_STATUS(status); status = RtlWC16StringAllocateFromCString(&result, convertString); GOTO_CLEANUP_ON_STATUS(status); cleanup: if (!NT_SUCCESS(status)) { RTL_FREE(&result); } RTL_FREE(&convertString); if (StringSid) { *StringSid = result; } return status; }
static NTSTATUS LwIoNormalizePath( IN PUNICODE_STRING Path, OUT PUNICODE_STRING NormalPath ) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING normalPath = { 0 }; ULONG inputIndex = 0; ULONG outputIndex = 0; ULONG count = 0; status = LwRtlUnicodeStringDuplicate(&normalPath, Path); GOTO_CLEANUP_ON_STATUS(status); count = LW_RTL_STRING_NUM_CHARS(&normalPath); for (inputIndex = outputIndex = 0; inputIndex < count; inputIndex++) { switch (normalPath.Buffer[inputIndex]) { case '\\': case '/': normalPath.Buffer[outputIndex++] = '/'; while (((inputIndex + 1) < count) && IoRtlPathIsSeparator(normalPath.Buffer[inputIndex+1])) { inputIndex++; } break; default: normalPath.Buffer[outputIndex++] = normalPath.Buffer[inputIndex]; break; } } normalPath.Length = LwRtlPointerToOffset(normalPath.Buffer, &normalPath.Buffer[outputIndex]); normalPath.MaximumLength = normalPath.Length; cleanup: if (status) { LwRtlUnicodeStringFree(&normalPath); } *NormalPath = normalPath; return status; }
static NTSTATUS LwIoFindPathCreds( IN PUNICODE_STRING Path, IN BOOLEAN bPrecise, OUT PIO_PATH_CREDS* ppCreds ) { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING normalPath = { 0 }; PIO_PATH_CREDS pFoundCreds = NULL; PLW_LIST_LINKS pLink = NULL; status = LwIoNormalizePath(Path, &normalPath); GOTO_CLEANUP_ON_STATUS(status); while ((pLink = LwListTraverse(&gPathCreds, pLink))) { PIO_PATH_CREDS pCreds = LW_STRUCT_FROM_FIELD(pLink, IO_PATH_CREDS, link); if ((bPrecise && LwRtlUnicodeStringIsEqual(&normalPath, &pCreds->PathPrefix, TRUE)) || (!bPrecise && LwRtlUnicodeStringIsPrefix(&pCreds->PathPrefix, &normalPath, TRUE))) { pFoundCreds = pCreds; break; } } cleanup: if (status) { pFoundCreds = NULL; } LwRtlUnicodeStringFree(&normalPath); *ppCreds = pFoundCreds; return status; }
NTSTATUS RtlAccessTokenToSelfRelativeAccessToken( IN PACCESS_TOKEN pToken, OUT OPTIONAL PACCESS_TOKEN_SELF_RELATIVE pRelative, IN OUT PULONG pulSize ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN isLocked = FALSE; ULONG ulRelativeSize = RtlAccessTokenRelativeSize(pToken); PSID_AND_ATTRIBUTES_SELF_RELATIVE pGroups = NULL; PBYTE pBuffer = NULL; ULONG ulOffset = 0; ULONG i = 0; if (pRelative) { if (*pulSize < ulRelativeSize) { status = STATUS_BUFFER_TOO_SMALL; GOTO_CLEANUP_ON_STATUS(status); } pBuffer = (PBYTE) pRelative; SHARED_LOCK_RWLOCK(&pToken->RwLock, isLocked); pRelative->Flags = pToken->Flags; pRelative->User.Attributes = pToken->User.Attributes; pRelative->GroupCount = pToken->GroupCount; pRelative->PrivilegeCount = pToken->PrivilegeCount; pRelative->Uid = pToken->Uid; pRelative->Gid = pToken->Gid; pRelative->Umask = pToken->Umask; ulOffset += sizeof(*pRelative); Align32(&ulOffset); pRelative->User.SidOffset = ulOffset; memcpy(pBuffer + ulOffset, pToken->User.Sid, RtlLengthSid(pToken->User.Sid)); ulOffset += RtlLengthSid(pToken->User.Sid); Align32(&ulOffset); if (pToken->Groups) { pRelative->GroupsOffset = ulOffset; pGroups = (PSID_AND_ATTRIBUTES_SELF_RELATIVE) (pBuffer + ulOffset); ulOffset += sizeof(SID_AND_ATTRIBUTES_SELF_RELATIVE) * pToken->GroupCount; Align32(&ulOffset); for (i = 0; i < pToken->GroupCount; i++) { pGroups[i].Attributes = pToken->Groups[i].Attributes; pGroups[i].SidOffset = ulOffset; memcpy(pBuffer + ulOffset, pToken->Groups[i].Sid, RtlLengthSid(pToken->Groups[i].Sid)); ulOffset += RtlLengthSid(pToken->Groups[i].Sid); Align32(&ulOffset); } } else { pRelative->GroupsOffset = 0; } if (pToken->Privileges) { pRelative->PrivilegesOffset = ulOffset; memcpy(pBuffer + ulOffset, pToken->Privileges, sizeof(pToken->Privileges[0]) * pToken->PrivilegeCount); ulOffset += sizeof(pToken->Privileges[0]) * pToken->PrivilegeCount; Align32(&ulOffset); } else { pRelative->PrivilegesOffset = 0; } if (pToken->Owner) { pRelative->OwnerOffset = ulOffset; memcpy(pBuffer + ulOffset, pToken->Owner, RtlLengthSid(pToken->Owner)); ulOffset += RtlLengthSid(pToken->Owner); Align32(&ulOffset); } else { pRelative->OwnerOffset = 0; } if (pToken->PrimaryGroup) { pRelative->PrimaryGroupOffset = ulOffset; memcpy(pBuffer + ulOffset, pToken->PrimaryGroup, RtlLengthSid(pToken->PrimaryGroup)); ulOffset += RtlLengthSid(pToken->PrimaryGroup); Align32(&ulOffset); } else { pRelative->PrimaryGroupOffset = 0; } if (pToken->DefaultDacl) { pRelative->DefaultDaclOffset = ulOffset; memcpy(pBuffer + ulOffset, pToken->DefaultDacl, RtlGetAclSize(pToken->DefaultDacl)); ulOffset += RtlGetAclSize(pToken->DefaultDacl); Align32(&ulOffset); } else { pRelative->DefaultDaclOffset = 0; } assert(ulOffset == ulRelativeSize); } cleanup: UNLOCK_RWLOCK(&pToken->RwLock, isLocked); *pulSize = ulRelativeSize; return status; }
NTSTATUS RtlSelfRelativeAccessTokenToAccessToken( IN PACCESS_TOKEN_SELF_RELATIVE pRelative, IN ULONG ulRelativeSize, OUT PACCESS_TOKEN* ppToken ) { NTSTATUS status = STATUS_SUCCESS; ULONG ulOffset = 0; PBYTE pBuffer = (PBYTE) pRelative; PSID pSid = NULL; PSID_AND_ATTRIBUTES_SELF_RELATIVE pGroups = NULL; ULONG ulSize = 0; ULONG ulRealSize = 0; ULONG i = 0; TOKEN_USER User = {{0}}; TOKEN_OWNER Owner = {0}; TOKEN_PRIMARY_GROUP PrimaryGroup = {0}; TOKEN_UNIX Unix = {0}; PTOKEN_GROUPS pTokenGroups = NULL; PTOKEN_PRIVILEGES pTokenPrivileges = NULL; TOKEN_DEFAULT_DACL DefaultDacl = {0}; status = CheckOffset(0, sizeof(*pRelative), ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); if (pRelative->Flags & ACCESS_TOKEN_FLAG_UNIX_PRESENT) { Unix.Uid = pRelative->Uid; Unix.Gid = pRelative->Gid; Unix.Umask = pRelative->Umask; } User.User.Attributes = pRelative->User.Attributes; ulOffset = pRelative->User.SidOffset; pSid = (PSID) (pBuffer + ulOffset); status = RtlValidateSelfRelativeSid(pSid, ulOffset, ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); User.User.Sid = pSid; ulOffset = pRelative->GroupsOffset; if (ulOffset) { status = LwRtlSafeMultiplyULONG( &ulSize, sizeof(SID_AND_ATTRIBUTES_SELF_RELATIVE), pRelative->GroupCount); GOTO_CLEANUP_ON_STATUS(status); status = LwRtlSafeMultiplyULONG( &ulRealSize, sizeof(SID_AND_ATTRIBUTES), pRelative->GroupCount); GOTO_CLEANUP_ON_STATUS(status); status = LwRtlSafeAddULONG( &ulRealSize, ulRealSize, sizeof(TOKEN_GROUPS)); GOTO_CLEANUP_ON_STATUS(status); status = CheckOffset(ulOffset, ulSize, ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); pGroups = (PSID_AND_ATTRIBUTES_SELF_RELATIVE) (pBuffer + ulOffset); status = RTL_ALLOCATE(&pTokenGroups, TOKEN_GROUPS, ulRealSize); GOTO_CLEANUP_ON_STATUS(status); pTokenGroups->GroupCount = pRelative->GroupCount; for (i = 0; i < pRelative->GroupCount; i++) { pTokenGroups->Groups[i].Attributes = pGroups[i].Attributes; ulOffset = pGroups[i].SidOffset; pSid = (PSID) (pBuffer + ulOffset); status = RtlValidateSelfRelativeSid(pSid, ulOffset, ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); pTokenGroups->Groups[i].Sid = pSid; } } ulOffset = pRelative->PrivilegesOffset; if (ulOffset) { status = LwRtlSafeMultiplyULONG( &ulSize, sizeof(LUID_AND_ATTRIBUTES), pRelative->PrivilegeCount); GOTO_CLEANUP_ON_STATUS(status); status = LwRtlSafeMultiplyULONG( &ulRealSize, sizeof(LUID_AND_ATTRIBUTES), pRelative->PrivilegeCount); GOTO_CLEANUP_ON_STATUS(status); status = LwRtlSafeAddULONG( &ulRealSize, ulRealSize, sizeof(TOKEN_PRIVILEGES)); GOTO_CLEANUP_ON_STATUS(status); status = CheckOffset(ulOffset, ulSize, ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); status = RTL_ALLOCATE(&pTokenPrivileges, TOKEN_PRIVILEGES, ulRealSize); GOTO_CLEANUP_ON_STATUS(status); pTokenPrivileges->PrivilegeCount = pRelative->PrivilegeCount; memcpy(pTokenPrivileges->Privileges, pBuffer + ulOffset, sizeof(LUID_AND_ATTRIBUTES) * pTokenPrivileges->PrivilegeCount); } ulOffset = pRelative->OwnerOffset; if (ulOffset) { pSid = (PSID) (pBuffer + ulOffset); status = RtlValidateSelfRelativeSid(pSid, ulOffset, ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); Owner.Owner = pSid; } ulOffset = pRelative->PrimaryGroupOffset; if (ulOffset) { pSid = (PSID) (pBuffer + ulOffset); status = RtlValidateSelfRelativeSid(pSid, ulOffset, ulRelativeSize); GOTO_CLEANUP_ON_STATUS(status); PrimaryGroup.PrimaryGroup = pSid; } status = RtlCreateAccessToken( ppToken, &User, pTokenGroups, pTokenPrivileges, &Owner, &PrimaryGroup, &DefaultDacl, pRelative->Flags & ACCESS_TOKEN_FLAG_UNIX_PRESENT ? &Unix : NULL); GOTO_CLEANUP_ON_STATUS(status); cleanup: RTL_FREE(&pTokenGroups); if (!NT_SUCCESS(status)) { *ppToken = NULL; } return status; }
NTSTATUS RtlCreateAccessToken( OUT PACCESS_TOKEN* AccessToken, IN PTOKEN_USER User, IN PTOKEN_GROUPS Groups, IN PTOKEN_PRIVILEGES Privileges, IN PTOKEN_OWNER Owner, IN PTOKEN_PRIMARY_GROUP PrimaryGroup, IN PTOKEN_DEFAULT_DACL DefaultDacl, IN OPTIONAL PTOKEN_UNIX Unix ) { NTSTATUS status = STATUS_SUCCESS; int unixError = 0; ULONG requiredSize = 0; PACCESS_TOKEN token = NULL; ULONG i = 0; ULONG size = 0; PVOID location = NULL; if (!User || !User->User.Sid || !Groups || !Owner || !PrimaryGroup || !DefaultDacl) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if (!RtlValidSid(User->User.Sid) || (Owner->Owner && !RtlValidSid(Owner->Owner)) || (PrimaryGroup->PrimaryGroup && !RtlValidSid(PrimaryGroup->PrimaryGroup))) { status = STATUS_INVALID_SID; GOTO_CLEANUP(); } // No user attributes currently exist. if (User->User.Attributes != 0) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } for (i = 0; i < Groups->GroupCount; i++) { // TODO-Perhaps validate Group attributes if (!Groups->Groups[i].Sid) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if (!RtlValidSid(Groups->Groups[i].Sid)) { status = STATUS_INVALID_SID; GOTO_CLEANUP(); } } if (DefaultDacl->DefaultDacl && !RtlValidAcl(DefaultDacl->DefaultDacl, NULL)) { status = STATUS_INVALID_ACL; GOTO_CLEANUP(); } // Compute size required requiredSize = sizeof(*token); size = RtlLengthSid(User->User.Sid); status = RtlSafeAddULONG(&requiredSize, requiredSize, size); GOTO_CLEANUP_ON_STATUS(status); if (Owner->Owner) { size = RtlLengthSid(Owner->Owner); status = RtlSafeAddULONG(&requiredSize, requiredSize, size); GOTO_CLEANUP_ON_STATUS(status); } if (PrimaryGroup->PrimaryGroup) { size = RtlLengthSid(PrimaryGroup->PrimaryGroup); status = RtlSafeAddULONG(&requiredSize, requiredSize, size); GOTO_CLEANUP_ON_STATUS(status); } if (DefaultDacl->DefaultDacl) { status = RtlSafeAddULONG(&requiredSize, requiredSize, DefaultDacl->DefaultDacl->AclSize); GOTO_CLEANUP_ON_STATUS(status); } status = RtlSafeMultiplyULONG(&size, sizeof(Groups->Groups[0]), Groups->GroupCount); GOTO_CLEANUP_ON_STATUS(status); status = RtlSafeAddULONG(&requiredSize, requiredSize, size); GOTO_CLEANUP_ON_STATUS(status); for (i = 0; i < Groups->GroupCount; i++) { size = RtlLengthSid(Groups->Groups[i].Sid); status = RtlSafeAddULONG(&requiredSize, requiredSize, size); GOTO_CLEANUP_ON_STATUS(status); } status = RtlSafeMultiplyULONG(&size, sizeof(Privileges->Privileges[0]), Privileges->PrivilegeCount); GOTO_CLEANUP_ON_STATUS(status); status = RtlSafeAddULONG(&requiredSize, requiredSize, size); GOTO_CLEANUP_ON_STATUS(status); status = RTL_ALLOCATE(&token, ACCESS_TOKEN, requiredSize); GOTO_CLEANUP_ON_STATUS(status); location = LW_PTR_ADD(token, sizeof(*token)); // Initialize token->ReferenceCount = 1; token->Flags = 0; unixError = pthread_rwlock_init(&token->RwLock, NULL); if (unixError) { LW_RTL_LOG_ERROR("Failed to init rwlock in access token " "(error = %d).", unixError); status = LwErrnoToNtStatus(unixError); GOTO_CLEANUP(); } token->pRwLock = &token->RwLock; token->User.Attributes = User->User.Attributes; token->User.Sid = (PSID) location; location = RtlpAppendData(location, User->User.Sid, RtlLengthSid(User->User.Sid)); token->GroupCount = Groups->GroupCount; token->Groups = (PSID_AND_ATTRIBUTES) location; location = LwRtlOffsetToPointer(location, sizeof(Groups->Groups[0]) * Groups->GroupCount); for (i = 0; i < Groups->GroupCount; i++) { token->Groups[i].Attributes = Groups->Groups[i].Attributes; token->Groups[i].Sid = (PSID) location; location = RtlpAppendData(location, Groups->Groups[i].Sid, RtlLengthSid(Groups->Groups[i].Sid)); } token->PrivilegeCount = Privileges->PrivilegeCount; token->Privileges = (PLUID_AND_ATTRIBUTES) location; location = LwRtlOffsetToPointer( location, sizeof(Privileges->Privileges[0]) * Privileges->PrivilegeCount); memcpy(token->Privileges, Privileges->Privileges, sizeof(token->Privileges[0]) * token->PrivilegeCount); if (Owner->Owner) { token->Owner = (PSID) location; location = RtlpAppendData(location, Owner->Owner, RtlLengthSid(Owner->Owner)); } if (PrimaryGroup->PrimaryGroup) { token->PrimaryGroup = (PSID) location; location = RtlpAppendData(location, PrimaryGroup->PrimaryGroup, RtlLengthSid(PrimaryGroup->PrimaryGroup)); } if (DefaultDacl->DefaultDacl) { token->DefaultDacl = (PACL) location; location = RtlpAppendData(location, DefaultDacl->DefaultDacl, DefaultDacl->DefaultDacl->AclSize); } if (Unix) { SetFlag(token->Flags, ACCESS_TOKEN_FLAG_UNIX_PRESENT); token->Uid = Unix->Uid; token->Gid = Unix->Gid; token->Umask = Unix->Umask; } if (location != LW_PTR_ADD(token, requiredSize)) { status = STATUS_ASSERTION_FAILURE; GOTO_CLEANUP(); } status = STATUS_SUCCESS; cleanup: if (!NT_SUCCESS(status)) { RtlReleaseAccessToken(&token); } *AccessToken = token; return status; }
NTSTATUS RtlQueryAccessTokenInformation( IN PACCESS_TOKEN AccessToken, IN TOKEN_INFORMATION_CLASS TokenInformationClass, OUT OPTIONAL PVOID TokenInformation, IN ULONG TokenInformationLength, OUT PULONG ReturnedLength ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN isLocked = FALSE; ULONG requiredSize = 0; ULONG i = 0; PVOID location = NULL; if (!AccessToken || !ReturnedLength) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if (!TokenInformation && (TokenInformationLength != 0)) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } SHARED_LOCK_RWLOCK(&AccessToken->RwLock, isLocked); // Check required size switch (TokenInformationClass) { case TokenUser: status = RtlSafeAddULONG( &requiredSize, sizeof(TOKEN_USER), RtlLengthSid(AccessToken->User.Sid)); GOTO_CLEANUP_ON_STATUS(status); break; case TokenGroups: status = RtlSafeMultiplyULONG( &requiredSize, sizeof(AccessToken->Groups[0]), AccessToken->GroupCount); GOTO_CLEANUP_ON_STATUS(status); status = RtlSafeAddULONG(&requiredSize, requiredSize, sizeof(TOKEN_GROUPS)); GOTO_CLEANUP_ON_STATUS(status); for (i = 0; i < AccessToken->GroupCount; i++) { status = RtlSafeAddULONG( &requiredSize, requiredSize, RtlLengthSid(AccessToken->Groups[i].Sid)); GOTO_CLEANUP_ON_STATUS(status); } break; case TokenPrivileges: status = RtlSafeMultiplyULONG( &requiredSize, sizeof(AccessToken->Privileges[0]), AccessToken->PrivilegeCount); GOTO_CLEANUP_ON_STATUS(status); status = RtlSafeAddULONG(&requiredSize, requiredSize, sizeof(TOKEN_PRIVILEGES)); GOTO_CLEANUP_ON_STATUS(status); break; case TokenOwner: status = RtlSafeAddULONG( &requiredSize, sizeof(TOKEN_OWNER), RtlLengthSid(AccessToken->Owner)); GOTO_CLEANUP_ON_STATUS(status); break; case TokenPrimaryGroup: status = RtlSafeAddULONG( &requiredSize, sizeof(TOKEN_PRIMARY_GROUP), RtlLengthSid(AccessToken->PrimaryGroup)); GOTO_CLEANUP_ON_STATUS(status); break; case TokenDefaultDacl: status = RtlSafeAddULONG( &requiredSize, sizeof(TOKEN_DEFAULT_DACL), (AccessToken->DefaultDacl ? AccessToken->DefaultDacl->AclSize : 0)); GOTO_CLEANUP_ON_STATUS(status); break; default: status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if (requiredSize > TokenInformationLength) { status = STATUS_BUFFER_TOO_SMALL; GOTO_CLEANUP(); } if (!TokenInformation) { status = STATUS_ASSERTION_FAILURE; GOTO_CLEANUP(); } // Copy data switch (TokenInformationClass) { case TokenUser: { PTOKEN_USER tokenInfo = (PTOKEN_USER) TokenInformation; location = LW_PTR_ADD(TokenInformation, sizeof(TOKEN_USER)); tokenInfo->User.Attributes = AccessToken->User.Attributes; tokenInfo->User.Sid = (PSID) location; location = RtlpAppendData(location, AccessToken->User.Sid, RtlLengthSid(AccessToken->User.Sid)); break; } case TokenGroups: { PTOKEN_GROUPS tokenInfo = (PTOKEN_GROUPS) TokenInformation; location = LW_PTR_ADD(TokenInformation, (sizeof(TOKEN_GROUPS) + (sizeof(AccessToken->Groups[0]) * AccessToken->GroupCount))); tokenInfo->GroupCount = AccessToken->GroupCount; for (i = 0; i < AccessToken->GroupCount; i++) { tokenInfo->Groups[i].Attributes = AccessToken->Groups[i].Attributes; tokenInfo->Groups[i].Sid = (PSID) location; location = RtlpAppendData(location, AccessToken->Groups[i].Sid, RtlLengthSid(AccessToken->Groups[i].Sid)); } break; } case TokenPrivileges: { PTOKEN_PRIVILEGES tokenInfo = (PTOKEN_PRIVILEGES) TokenInformation; location = LW_PTR_ADD(TokenInformation, (sizeof(TOKEN_PRIVILEGES) + (sizeof(AccessToken->Privileges[0]) * AccessToken->PrivilegeCount))); tokenInfo->PrivilegeCount = AccessToken->PrivilegeCount; if (AccessToken->PrivilegeCount) { memcpy(tokenInfo->Privileges, AccessToken->Privileges, sizeof(tokenInfo->Privileges[0]) * tokenInfo->PrivilegeCount); } break; } case TokenOwner: { PTOKEN_OWNER tokenInfo = (PTOKEN_OWNER) TokenInformation; location = LW_PTR_ADD(TokenInformation, sizeof(TOKEN_OWNER)); tokenInfo->Owner = (PSID) location; location = RtlpAppendData(location, AccessToken->Owner, RtlLengthSid(AccessToken->Owner)); break; } case TokenPrimaryGroup: { PTOKEN_PRIMARY_GROUP tokenInfo = (PTOKEN_PRIMARY_GROUP) TokenInformation; location = LW_PTR_ADD(TokenInformation, sizeof(TOKEN_PRIMARY_GROUP)); tokenInfo->PrimaryGroup = (PSID) location; location = RtlpAppendData(location, AccessToken->PrimaryGroup, RtlLengthSid(AccessToken->PrimaryGroup)); break; } case TokenDefaultDacl: { PTOKEN_DEFAULT_DACL tokenInfo = (PTOKEN_DEFAULT_DACL) TokenInformation; location = LW_PTR_ADD(TokenInformation, sizeof(TOKEN_DEFAULT_DACL)); if (AccessToken->DefaultDacl) { tokenInfo->DefaultDacl = (PACL) location; location = RtlpAppendData(location, AccessToken->DefaultDacl, AccessToken->DefaultDacl->AclSize); } break; } default: // We should have already checked. status = STATUS_ASSERTION_FAILURE; GOTO_CLEANUP(); } if (location != LW_PTR_ADD(TokenInformation, requiredSize)) { status = STATUS_ASSERTION_FAILURE; GOTO_CLEANUP(); } status = STATUS_SUCCESS; cleanup: UNLOCK_RWLOCK(&AccessToken->RwLock, isLocked); if (ReturnedLength) { *ReturnedLength = requiredSize; } return status; }
BOOLEAN RtlAccessCheckEx( IN PSECURITY_DESCRIPTOR_ABSOLUTE SecurityDescriptor, IN PACCESS_TOKEN AccessToken, IN ACCESS_MASK DesiredAccess, IN ACCESS_MASK PreviouslyGrantedAccess, IN PGENERIC_MAPPING GenericMapping, OUT PACCESS_MASK RemainingDesiredAccess, OUT PACCESS_MASK GrantedAccess, OUT PNTSTATUS AccessStatus ) { NTSTATUS status = STATUS_ACCESS_DENIED; BOOLEAN isLocked = FALSE; ACCESS_MASK grantedAccess = PreviouslyGrantedAccess; ACCESS_MASK deniedAccess = 0; ACCESS_MASK desiredAccess = DesiredAccess; BOOLEAN wantMaxAllowed = FALSE; USHORT aclSizeUsed = 0; USHORT aceOffset = 0; PACE_HEADER aceHeader = NULL; union { SID Sid; BYTE Buffer[SID_MAX_SIZE]; } sidBuffer; ULONG ulSidSize = sizeof(sidBuffer); if (!SecurityDescriptor || !AccessToken || !GenericMapping) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if (!LW_IS_VALID_FLAGS(DesiredAccess, VALID_DESIRED_ACCESS_MASK)) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if (!LW_IS_VALID_FLAGS(PreviouslyGrantedAccess, VALID_GRANTED_ACCESS_MASK)) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } if ((SecurityDescriptor->Owner == NULL) || (SecurityDescriptor->Group == NULL)) { status = STATUS_INVALID_SECURITY_DESCR; GOTO_CLEANUP(); } wantMaxAllowed = IsSetFlag(desiredAccess, MAXIMUM_ALLOWED); ClearFlag(desiredAccess, MAXIMUM_ALLOWED); RtlMapGenericMask(&desiredAccess, GenericMapping); // // NT AUTHORITY\SYSTEM is always allowed an access // status = RtlCreateWellKnownSid(WinLocalSystemSid, NULL, &sidBuffer.Sid, &ulSidSize); GOTO_CLEANUP_ON_STATUS(status); SHARED_LOCK_RWLOCK(&AccessToken->RwLock, isLocked); if (RtlIsSidMemberOfToken(AccessToken, &sidBuffer.Sid)) { if (wantMaxAllowed) { SetFlag(desiredAccess, STANDARD_RIGHTS_ALL); SetFlag(desiredAccess, GENERIC_ALL); RtlMapGenericMask(&desiredAccess, GenericMapping); } SetFlag(grantedAccess, desiredAccess); desiredAccess = 0; status = STATUS_SUCCESS; GOTO_CLEANUP(); } if (wantMaxAllowed || IsSetFlag(desiredAccess, ACCESS_SYSTEM_SECURITY)) { // TODO-Handle ACCESS_SYSTEM_SECURITY by checking SE_SECURITY_NAME // privilege. For now, requesting ACCESS_SYSTEM_SECURITY is not // allowed. ulSidSize = sizeof(sidBuffer); status = RtlCreateWellKnownSid( WinBuiltinAdministratorsSid, NULL, &sidBuffer.Sid, &ulSidSize); GOTO_CLEANUP_ON_STATUS(status); if (RtlIsSidMemberOfToken(AccessToken, &sidBuffer.Sid)) { SetFlag(grantedAccess, ACCESS_SYSTEM_SECURITY); ClearFlag(desiredAccess, ACCESS_SYSTEM_SECURITY); } else if (IsSetFlag(desiredAccess, ACCESS_SYSTEM_SECURITY)) { status = STATUS_PRIVILEGE_NOT_HELD; GOTO_CLEANUP(); } } if (wantMaxAllowed || IsSetFlag(desiredAccess, WRITE_OWNER)) { // TODO-Allow WRITE_OWNER if have SE_TAKE_OWNERSHIP_NAME regardless // of DACL. // // BUILTIN\Administrators are always allowed WRITE_OWNER // ulSidSize = sizeof(sidBuffer); status = RtlCreateWellKnownSid( WinBuiltinAdministratorsSid, NULL, &sidBuffer.Sid, &ulSidSize); GOTO_CLEANUP_ON_STATUS(status); if (RtlIsSidMemberOfToken(AccessToken, &sidBuffer.Sid)) { SetFlag(grantedAccess, WRITE_OWNER); ClearFlag(desiredAccess, WRITE_OWNER); } } // // Owner can always read the SD and write the DACL. // if (wantMaxAllowed || IsSetFlag(desiredAccess, READ_CONTROL | WRITE_DAC)) { if (RtlIsSidMemberOfToken(AccessToken, SecurityDescriptor->Owner)) { if (wantMaxAllowed) { desiredAccess |= (READ_CONTROL | WRITE_DAC); } SetFlag(grantedAccess, (READ_CONTROL | WRITE_DAC) & desiredAccess); ClearFlag(desiredAccess, grantedAccess); } } // TODO-MAXIMUM_ALLOWED wrt privileges and WRITE_OWNER and // ACCESS_SYSTEM_SECURITY above. if (!SecurityDescriptor->Dacl) { // TODO-Interplay with special bits above? if (wantMaxAllowed) { SetFlag(desiredAccess, STANDARD_RIGHTS_ALL); SetFlag(desiredAccess, GENERIC_ALL); RtlMapGenericMask(&desiredAccess, GenericMapping); } SetFlag(grantedAccess, desiredAccess); desiredAccess = 0; status = STATUS_SUCCESS; GOTO_CLEANUP(); } if (!RtlValidAcl(SecurityDescriptor->Dacl, &aclSizeUsed)) { status = STATUS_INVALID_ACL; GOTO_CLEANUP(); } while (wantMaxAllowed || desiredAccess) { status = RtlIterateAce(SecurityDescriptor->Dacl, aclSizeUsed, &aceOffset, &aceHeader); if (STATUS_NO_MORE_ENTRIES == status) { break; } GOTO_CLEANUP_ON_STATUS(status); // Check ACE switch (aceHeader->AceType) { case ACCESS_ALLOWED_ACE_TYPE: { PACCESS_ALLOWED_ACE ace = (PACCESS_ALLOWED_ACE) aceHeader; ACCESS_MASK mask = ace->Mask; RtlMapGenericMask(&mask, GenericMapping); if (wantMaxAllowed || IsSetFlag(desiredAccess, mask)) { // SID in token => add bits to granted bits PSID sid = RtlpGetSidAccessAllowedAce(ace); if (RtlIsSidMemberOfToken(AccessToken, sid)) { if (wantMaxAllowed) { SetFlag(grantedAccess, mask & ~deniedAccess); } else { SetFlag(grantedAccess, mask & desiredAccess); } ClearFlag(desiredAccess, grantedAccess); } } break; } case ACCESS_DENIED_ACE_TYPE: { // Allowed and deny ACEs are isomorphic. PACCESS_ALLOWED_ACE ace = (PACCESS_ALLOWED_ACE) aceHeader; ACCESS_MASK mask = ace->Mask; RtlMapGenericMask(&mask, GenericMapping); if (wantMaxAllowed || IsSetFlag(desiredAccess, mask)) { // SID in token => exit with STATUS_ACCESS_DENIED PSID sid = RtlpGetSidAccessAllowedAce(ace); if (RtlIsSidMemberOfToken(AccessToken, sid)) { if (wantMaxAllowed) { SetFlag(deniedAccess, mask); } else { status = STATUS_ACCESS_DENIED; GOTO_CLEANUP(); } ClearFlag(desiredAccess, deniedAccess); } } break; } default: // ignore break; } } status = desiredAccess ? STATUS_ACCESS_DENIED : STATUS_SUCCESS; cleanup: UNLOCK_RWLOCK(&AccessToken->RwLock, isLocked); if (NT_SUCCESS(status) && !LW_IS_VALID_FLAGS(grantedAccess, VALID_GRANTED_ACCESS_MASK)) { status = STATUS_ASSERTION_FAILURE; } if (!NT_SUCCESS(status)) { grantedAccess = PreviouslyGrantedAccess; } if (RemainingDesiredAccess) { *RemainingDesiredAccess = desiredAccess; } *GrantedAccess = grantedAccess; *AccessStatus = status; return NT_SUCCESS(status) ? TRUE : FALSE; }
NTSTATUS RtlAllocateCStringFromSid( OUT PSTR* StringSid, IN PSID Sid ) { NTSTATUS status = STATUS_SUCCESS; PSTR result = NULL; size_t size = 0; int count = 0; ULONG i = 0; if (!StringSid || !RtlValidSid(Sid)) { status = STATUS_INVALID_PARAMETER; GOTO_CLEANUP(); } size = RTLP_STRING_SID_MAX_CHARS(Sid->SubAuthorityCount); status = RTL_ALLOCATE(&result, CHAR, size); GOTO_CLEANUP_ON_STATUS(status); if (Sid->IdentifierAuthority.Value[0] || Sid->IdentifierAuthority.Value[1]) { count += snprintf(result + count, size - count, "S-%u-0x%.2X%.2X%.2X%.2X%.2X%.2X", Sid->Revision, Sid->IdentifierAuthority.Value[0], Sid->IdentifierAuthority.Value[1], Sid->IdentifierAuthority.Value[2], Sid->IdentifierAuthority.Value[3], Sid->IdentifierAuthority.Value[4], Sid->IdentifierAuthority.Value[5]); } else { ULONG value = 0; value |= (ULONG) Sid->IdentifierAuthority.Value[5]; value |= (ULONG) Sid->IdentifierAuthority.Value[4] << 8; value |= (ULONG) Sid->IdentifierAuthority.Value[3] << 16; value |= (ULONG) Sid->IdentifierAuthority.Value[2] << 24; count += snprintf(result + count, size - count, "S-%u-%u", Sid->Revision, value); } for (i = 0; i < Sid->SubAuthorityCount; i++) { count += snprintf(result + count, size - count, "-%u", Sid->SubAuthority[i]); } status = STATUS_SUCCESS; cleanup: if (!NT_SUCCESS(status)) { RTL_FREE(&result); } if (StringSid) { *StringSid = result; } return status; }
NTSTATUS IopIrpDispatch( IN PIRP pIrp, IN OUT OPTIONAL PIO_ASYNC_CONTROL_BLOCK AsyncControlBlock, OUT PIO_STATUS_BLOCK pIoStatusBlock ) { NTSTATUS status = STATUS_SUCCESS; BOOLEAN isAsyncCall = FALSE; LW_RTL_EVENT event = LW_RTL_EVENT_ZERO_INITIALIZER; PIRP pExtraIrpReference = NULL; PIRP_INTERNAL irpInternal = IopIrpGetInternal(pIrp); BOOLEAN needCancel = FALSE; IRP_TYPE irpType = pIrp->Type; LWIO_ASSERT(pIoStatusBlock); isAsyncCall = AsyncControlBlock ? TRUE : FALSE; if (isAsyncCall) { LWIO_ASSERT(!AsyncControlBlock->AsyncCancelContext); LWIO_ASSERT(AsyncControlBlock->Callback); irpInternal->Completion.Async.Callback = AsyncControlBlock->Callback; irpInternal->Completion.Async.CallbackContext = AsyncControlBlock->CallbackContext; irpInternal->Completion.Async.pIoStatusBlock = pIoStatusBlock; // Assert that caller has set required out params via IopIrpSetOutput*(). LWIO_ASSERT(!IopIrpIsCreate(pIrp) || irpInternal->Completion.Async.OpOut.Create.pFileHandle); LWIO_ASSERT(!IopIrpIsPrepareZctReadWrite(pIrp) || irpInternal->Completion.Async.OpOut.PrepareZctReadWrite.pCompletionContext); // Reference IRP since we may need to return an an async cancel context. IopIrpReference(pIrp); pExtraIrpReference = pIrp; } else { // Since sync, assert IopIrpSetOutput*() has not actually set anything. LWIO_ASSERT(!irpInternal->Completion.IsAsyncCall); status = LwRtlInitializeEvent(&event); GOTO_CLEANUP_ON_STATUS(status); irpInternal->Completion.Sync.Event = &event; } irpInternal->Completion.IsAsyncCall = isAsyncCall; // We have to dispatch once we add the IRP as "dipatched" // and we have to call IopIrpCompleteInternal() so that // it gets subtracted. status = IopFileObjectAddDispatched(pIrp->FileHandle, pIrp->Type); GOTO_CLEANUP_ON_STATUS(status); SetFlag(irpInternal->Flags, IRP_FLAG_DISPATCHED); status = IopDeviceCallDriver(pIrp->DeviceHandle, pIrp); // Handle synchronous completion if (STATUS_PENDING != status) { IopIrpCompleteInternal(pIrp, FALSE); } // Handle asynchronous dispatch else { IopIrpAcquireCancelLock(pIrp); LWIO_ASSERT(IsSetFlag(irpInternal->Flags, IRP_FLAG_PENDING)); LWIO_ASSERT(irpInternal->Cancel.Callback); needCancel = IsSetFlag(irpInternal->Flags, IRP_FLAG_CANCEL_PENDING); IopIrpReleaseCancelLock(pIrp); if (needCancel) { IopIrpCancel(pIrp); } // Handle waiting for asynchronous completion for synchronous caller if (!isAsyncCall) { LwRtlWaitEvent(&event, NULL); LWIO_ASSERT(pIrp->IoStatusBlock.Status != STATUS_PENDING); status = pIrp->IoStatusBlock.Status; } } // // At this point, we are either complete or this is // an async call that returned STATUS_PENDING. // LWIO_ASSERT((STATUS_PENDING == status) || (pIrp->IoStatusBlock.Status == status)); cleanup: LwRtlCleanupEvent(&event); if (STATUS_PENDING == status) { LWIO_ASSERT(isAsyncCall); AsyncControlBlock->AsyncCancelContext = IopIrpGetAsyncCancelContextFromIrp(pIrp); } else { if (isAsyncCall) { // // Remove async cancel context reference added earlier since we // are returning synchronously w/o an async cancel context. // IopIrpDereference(&pExtraIrpReference); } pIrp->IoStatusBlock.Status = status; *pIoStatusBlock = pIrp->IoStatusBlock; } LWIO_ASSERT(IS_BOTH_OR_NEITHER(pExtraIrpReference, (STATUS_PENDING == status))); LWIO_ASSERT((STATUS_PENDING != status) || isAsyncCall); LWIO_ASSERT(IsValidStatusForIrpType(status, irpType)); return status; }
LW_NTSTATUS LwIoRdrGetPhysicalPath( IO_FILE_HANDLE File, LW_PWSTR* ppResolved ) { NTSTATUS status = STATUS_SUCCESS; IO_STATUS_BLOCK ioStatus; ULONG length = RESOLVED_PATH_LENGTH; PBYTE pBuffer = NULL; PBYTE pNewBuffer = NULL; PWSTR pResolved = NULL; status = RTL_ALLOCATE(&pBuffer, BYTE, length); GOTO_CLEANUP_ON_STATUS(status); do { status = LwNtDeviceIoControlFile( File, NULL, &ioStatus, RDR_DEVCTL_GET_PHYSICAL_PATH, NULL, 0, pBuffer, length); if (status == STATUS_BUFFER_TOO_SMALL) { length *= 2; pNewBuffer = LwRtlMemoryRealloc(pBuffer, length); if (!pNewBuffer) { status = STATUS_INSUFFICIENT_RESOURCES; GOTO_CLEANUP_ON_STATUS(status); } pBuffer = pNewBuffer; } } while (status == STATUS_BUFFER_TOO_SMALL); GOTO_CLEANUP_ON_STATUS(status); if (ioStatus.BytesTransferred > 0) { status = LW_RTL_ALLOCATE( &pResolved, WCHAR, ioStatus.BytesTransferred + sizeof(WCHAR)); GOTO_CLEANUP_ON_STATUS(status); #ifdef WORDS_BIGENDIAN swab(pBuffer, pResolved, ioStatus.BytesTransferred); #else memcpy(pResolved, pBuffer, ioStatus.BytesTransferred); #endif } cleanup: *ppResolved = pResolved; RTL_FREE(&pBuffer); return status; }