/************************************************************************ Copyright (c) 2000 - 2001 Microsoft Corporation Module Name : nt4thunks.cpp Abstract : General helper functions to get BITS to work on NT4 Author : Revision History : ***********************************************************************/ #include "qmgrlibp.h" #include #include #include #if !defined(BITS_V12_ON_NT4) #include "nt4thunks.tmh" #endif #if defined( BITS_V12_ON_NT4 ) BOOL BITSAltGetFileSizeEx( HANDLE hFile, // handle to file PLARGE_INTEGER lpFileSize // file size ) { DWORD HighPart; DWORD Result = GetFileSize( hFile, &HighPart ); if ( INVALID_FILE_SIZE == Result && GetLastError() != NO_ERROR ) return FALSE; lpFileSize->HighPart = (LONG)HighPart; lpFileSize->LowPart = Result; return TRUE; } BOOL BITSAltSetFilePointerEx( HANDLE hFile, // handle to file LARGE_INTEGER liDistanceToMove, // bytes to move pointer PLARGE_INTEGER lpNewFilePointer, // new file pointer DWORD dwMoveMethod // starting point ) { LONG DistanceToMoveHigh = liDistanceToMove.HighPart; DWORD DistanceToMoveLow = liDistanceToMove.LowPart; DWORD Result = SetFilePointer( hFile, (LONG)DistanceToMoveLow, &DistanceToMoveHigh, dwMoveMethod ); if ( INVALID_SET_FILE_POINTER == Result && NO_ERROR != GetLastError() ) return FALSE; if ( lpNewFilePointer ) { lpNewFilePointer->HighPart = DistanceToMoveHigh; lpNewFilePointer->LowPart = (DWORD)DistanceToMoveLow; } return TRUE; } // // Local macros // #define STRING_GUID_LEN 36 #define STRING_GUID_SIZE ( STRING_GUID_LEN * sizeof( WCHAR ) ) #define SDDL_LEN_TAG( tagdef ) ( sizeof( tagdef ) / sizeof( WCHAR ) - 1 ) #define SDDL_SIZE_TAG( tagdef ) ( wcslen( tagdef ) * sizeof( WCHAR ) ) #define SDDL_SIZE_SEP( sep ) (sizeof( WCHAR ) ) #define SDDL_VALID_DACL 0x00000001 #define SDDL_VALID_SACL 0x00000002 ULONG BITSAltSetLastNTError( IN NTSTATUS Status ) { ULONG dwErrorCode; dwErrorCode = RtlNtStatusToDosError( Status ); SetLastError( dwErrorCode ); return( dwErrorCode ); } BOOL BITSAltConvertSidToStringSidW( IN PSID Sid, OUT LPWSTR *StringSid ) /*++ Routine Description: This routine converts a SID into a string representation of a SID, suitable for framing or display Arguments: Sid - SID to be converted. StringSid - Where the converted SID is returned. Allocated via LocalAlloc and needs to be freed via LocalFree. Return Value: TRUE - Success FALSE - Failure Extended error status is available using GetLastError. --*/ { NTSTATUS Status; UNICODE_STRING UnicodeStringSid; if ( NULL == Sid || NULL == StringSid ) { // // invalid parameter // SetLastError( ERROR_INVALID_PARAMETER ); return( FALSE ); } // // Convert using the Rtl functions // Status = RtlConvertSidToUnicodeString( &UnicodeStringSid, Sid, TRUE ); if ( !NT_SUCCESS( Status ) ) { BITSAltSetLastNTError( Status ); return( FALSE ); } // // Convert it to the proper allocator // *StringSid = (LPWSTR)LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, UnicodeStringSid.Length + sizeof( WCHAR ) ); if ( *StringSid == NULL ) { RtlFreeUnicodeString( &UnicodeStringSid ); SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return( FALSE ); } RtlCopyMemory( *StringSid, UnicodeStringSid.Buffer, UnicodeStringSid.Length ); RtlFreeUnicodeString( &UnicodeStringSid ); SetLastError(ERROR_SUCCESS); return( TRUE ); } // // Private functions // BOOL LocalConvertStringSidToSid ( IN PWSTR StringSid, OUT PSID *Sid, OUT PWSTR *End ) /*++ Routine Description: This routine will convert a string representation of a SID back into a sid. The expected format of the string is: "S-1-5-32-549" If a string in a different format or an incorrect or incomplete string is given, the operation is failed. The returned sid must be free via a call to LocalFree Arguments: StringSid - The string to be converted Sid - Where the created SID is to be returned End - Where in the string we stopped processing Return Value: TRUE - Success. FALSE - Failure. Additional information returned from GetLastError(). Errors set are: ERROR_SUCCESS indicates success ERROR_NOT_ENOUGH_MEMORY indicates a memory allocation for the ouput sid failed ERROR_INVALID_SID indicates that the given string did not represent a sid --*/ { DWORD Err = ERROR_SUCCESS; UCHAR Revision, Subs; SID_IDENTIFIER_AUTHORITY IDAuth; PULONG SubAuth = NULL; PWSTR CurrEnd, Curr, Next; WCHAR Stub, *StubPtr = NULL; ULONG Index; INT gBase=10; INT lBase=10; ULONG Auto; if ( NULL == StringSid || NULL == Sid || NULL == End ) { SetLastError( ERROR_INVALID_PARAMETER ); return( FALSE ); } // if ( wcslen( StringSid ) < 2 || ( *StringSid != L'S' && *( StringSid + 1 ) != L'-' ) ) { // // no need to check length because StringSid is NULL // and if the first char is NULL, it won't access the second char // if ( (*StringSid != L'S' && *StringSid != L's') || *( StringSid + 1 ) != L'-' ) { // // string sid should always start with S- // SetLastError( ERROR_INVALID_SID ); return( FALSE ); } Curr = StringSid + 2; if ( (*Curr == L'0') && ( *(Curr+1) == L'x' || *(Curr+1) == L'X' ) ) { gBase = 16; } Revision = ( UCHAR )wcstol( Curr, &CurrEnd, gBase ); if ( CurrEnd == Curr || *CurrEnd != L'-' || *(CurrEnd+1) == L'\0' ) { // // no revision is provided, or invalid delimeter // SetLastError( ERROR_INVALID_SID ); return( FALSE ); } Curr = CurrEnd + 1; // // Count the number of characters in the indentifer authority... // Next = wcschr( Curr, L'-' ); /* Length = 6 doesn't mean each digit is a id authority value, could be 0x... if ( Next != NULL && (Next - Curr == 6) ) { for ( Index = 0; Index < 6; Index++ ) { // IDAuth.Value[Index] = (UCHAR)Next[Index]; what is this ??? IDAuth.Value[Index] = (BYTE) (Curr[Index]-L'0'); } Curr +=6; } else { */ if ( (*Curr == L'0') && ( *(Curr+1) == L'x' || *(Curr+1) == L'X' ) ) { lBase = 16; } else { lBase = gBase; } Auto = wcstoul( Curr, &CurrEnd, lBase ); if ( CurrEnd == Curr || *CurrEnd != L'-' || *(CurrEnd+1) == L'\0' ) { // // no revision is provided, or invalid delimeter // SetLastError( ERROR_INVALID_SID ); return( FALSE ); } IDAuth.Value[0] = IDAuth.Value[1] = 0; IDAuth.Value[5] = ( UCHAR )Auto & 0xFF; IDAuth.Value[4] = ( UCHAR )(( Auto >> 8 ) & 0xFF ); IDAuth.Value[3] = ( UCHAR )(( Auto >> 16 ) & 0xFF ); IDAuth.Value[2] = ( UCHAR )(( Auto >> 24 ) & 0xFF ); Curr = CurrEnd; // } // // Now, count the number of sub auths, at least one sub auth is required // Subs = 0; Next = Curr; // // We'll have to count our sub authoritys one character at a time, // since there are several deliminators that we can have... // while ( Next ) { if ( *Next == L'-' && *(Next-1) != L'-') { // // do not allow two continuous '-'s // We've found one! // Subs++; if ( (*(Next+1) == L'0') && ( *(Next+2) == L'x' || *(Next+2) == L'X' ) ) { // // this is hex indicator // Next += 2; } } else if ( *Next == SDDL_SEPERATORC || *Next == L'\0' || *Next == SDDL_ACE_ENDC || *Next == L' ' || ( *(Next+1) == SDDL_DELIMINATORC && (*Next == L'G' || *Next == L'O' || *Next == L'S')) ) { // // space is a terminator too // if ( *( Next - 1 ) == L'-' ) { // // shouldn't allow a SID terminated with '-' // Err = ERROR_INVALID_SID; Next--; } else { Subs++; } *End = Next; break; } else if ( !iswxdigit( *Next ) ) { Err = ERROR_INVALID_SID; *End = Next; // Subs++; break; } else { // // Note: SID is also used as a owner or group // // Some of the tags (namely 'D' for Dacl) fall under the category of iswxdigit, so // if the current character is a character we care about and the next one is a // delminiator, we'll quit // if ( *Next == L'D' && *( Next + 1 ) == SDDL_DELIMINATORC ) { // // We'll also need to temporarily truncate the string to this length so // we don't accidentally include the character in one of the conversions // Stub = *Next; StubPtr = Next; *StubPtr = UNICODE_NULL; *End = Next; Subs++; break; } } Next++; } if ( Err == ERROR_SUCCESS ) { if ( Subs != 0 ) Subs--; if ( Subs != 0 ) { Curr++; SubAuth = ( PULONG )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, Subs * sizeof( ULONG ) ); if ( SubAuth == NULL ) { Err = ERROR_NOT_ENOUGH_MEMORY; } else { for ( Index = 0; Index < Subs; Index++ ) { if ( (*Curr == L'0') && ( *(Curr+1) == L'x' || *(Curr+1) == L'X' ) ) { lBase = 16; } else { lBase = gBase; } SubAuth[Index] = wcstoul( Curr, &CurrEnd, lBase ); Curr = CurrEnd + 1; } } } else { Err = ERROR_INVALID_SID; } } // // Now, create the SID // if ( Err == ERROR_SUCCESS ) { *Sid = ( PSID )LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT, sizeof( SID ) + Subs * sizeof( ULONG ) ); if ( *Sid == NULL ) { Err = ERROR_NOT_ENOUGH_MEMORY; } else { PISID ISid = ( PISID )*Sid; ISid->Revision = Revision; ISid->SubAuthorityCount = Subs; RtlCopyMemory( &( ISid->IdentifierAuthority ), &IDAuth, sizeof( SID_IDENTIFIER_AUTHORITY ) ); RtlCopyMemory( ISid->SubAuthority, SubAuth, Subs * sizeof( ULONG ) ); } } LocalFree( SubAuth ); // // Restore any character we may have stubbed out // if ( StubPtr ) { *StubPtr = Stub; } SetLastError( Err ); return( Err == ERROR_SUCCESS ); } BOOL BITSAltConvertStringSidToSidW( IN LPCWSTR StringSid, OUT PSID *Sid ) /*++ Routine Description: This routine converts a stringized SID into a valid, functional SID Arguments: StringSid - SID to be converted. Sid - Where the converted SID is returned. Buffer is allocated via LocalAlloc and should be free via LocalFree. Return Value: TRUE - Success FALSE - Failure Extended error status is available using GetLastError. ERROR_INVALID_PARAMETER - A NULL name was given ERROR_INVALID_SID - The format of the given sid was incorrect --*/ { PWSTR End = NULL; BOOL ReturnValue = FALSE; PSID pSASid=NULL; ULONG Len=0; DWORD SaveCode=0; DWORD Err=0; if ( StringSid == NULL || Sid == NULL ) { SetLastError( ERROR_INVALID_PARAMETER ); return ReturnValue; } ReturnValue = LocalConvertStringSidToSid( ( PWSTR )StringSid, Sid, &End ); if ( !ReturnValue ) { SetLastError( ERROR_INVALID_PARAMETER ); return ReturnValue; } if ( ( ULONG )( End - StringSid ) != wcslen( StringSid ) ) { SetLastError( ERROR_INVALID_SID ); LocalFree( *Sid ); *Sid = FALSE; ReturnValue = FALSE; } else { SetLastError(ERROR_SUCCESS); } return ReturnValue; } BOOL BITSAltCheckTokenMembership( IN HANDLE TokenHandle OPTIONAL, IN PSID SidToCheck, OUT PBOOL IsMember ) /*++ Routine Description: This function checks to see whether the specified sid is enabled in the specified token. Arguments: TokenHandle - If present, this token is checked for the sid. If not present then the current effective token will be used. This must be an impersonation token. SidToCheck - The sid to check for presence in the token IsMember - If the sid is enabled in the token, contains TRUE otherwise false. Return Value: TRUE - The API completed successfully. It does not indicate that the sid is a member of the token. FALSE - The API failed. A more detailed status code can be retrieved via GetLastError() --*/ { HANDLE ProcessToken = NULL; HANDLE EffectiveToken = NULL; NTSTATUS Status = STATUS_SUCCESS; PISECURITY_DESCRIPTOR SecDesc = NULL; ULONG SecurityDescriptorSize; GENERIC_MAPPING GenericMapping = { STANDARD_RIGHTS_READ, STANDARD_RIGHTS_EXECUTE, STANDARD_RIGHTS_WRITE, STANDARD_RIGHTS_ALL }; // // The size of the privilege set needs to contain the set itself plus // any privileges that may be used. The privileges that are used // are SeTakeOwnership and SeSecurity, plus one for good measure // BYTE PrivilegeSetBuffer[sizeof(PRIVILEGE_SET) + 3*sizeof(LUID_AND_ATTRIBUTES)]; PPRIVILEGE_SET PrivilegeSet = (PPRIVILEGE_SET) PrivilegeSetBuffer; ULONG PrivilegeSetLength = sizeof(PrivilegeSetBuffer); ACCESS_MASK AccessGranted = 0; NTSTATUS AccessStatus = 0; PACL Dacl = NULL; #define MEMBER_ACCESS 1 *IsMember = FALSE; // // Get a handle to the token // if (ARGUMENT_PRESENT(TokenHandle)) { EffectiveToken = TokenHandle; } else { Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, FALSE, // don't open as self &EffectiveToken ); // // if there is no thread token, try the process token // if (Status == STATUS_NO_TOKEN) { Status = NtOpenProcessToken( NtCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &ProcessToken ); // // If we have a process token, we need to convert it to an // impersonation token // if (NT_SUCCESS(Status)) { BOOL Result; Result = DuplicateToken( ProcessToken, SecurityImpersonation, &EffectiveToken ); CloseHandle(ProcessToken); if (!Result) { return(FALSE); } } } if (!NT_SUCCESS(Status)) { goto Cleanup; } } // // Construct a security descriptor to pass to access check // // // The size is equal to the size of an SD + twice the length of the SID // (for owner and group) + size of the DACL = sizeof ACL + size of the // ACE, which is an ACE + length of // ths SID. // SecurityDescriptorSize = sizeof(SECURITY_DESCRIPTOR) + sizeof(ACCESS_ALLOWED_ACE) + sizeof(ACL) + 3 * RtlLengthSid(SidToCheck); SecDesc = (PISECURITY_DESCRIPTOR) LocalAlloc(LMEM_ZEROINIT, SecurityDescriptorSize ); if (SecDesc == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } Dacl = (PACL) (SecDesc + 1); RtlCreateSecurityDescriptor( SecDesc, SECURITY_DESCRIPTOR_REVISION ); // // Fill in fields of security descriptor // RtlSetOwnerSecurityDescriptor( SecDesc, SidToCheck, FALSE ); RtlSetGroupSecurityDescriptor( SecDesc, SidToCheck, FALSE ); Status = RtlCreateAcl( Dacl, SecurityDescriptorSize - sizeof(SECURITY_DESCRIPTOR), ACL_REVISION ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = RtlAddAccessAllowedAce( Dacl, ACL_REVISION, MEMBER_ACCESS, SidToCheck ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // Set the DACL on the security descriptor // Status = RtlSetDaclSecurityDescriptor( SecDesc, TRUE, // DACL present Dacl, FALSE // not defaulted ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = NtAccessCheck( SecDesc, EffectiveToken, MEMBER_ACCESS, &GenericMapping, PrivilegeSet, &PrivilegeSetLength, &AccessGranted, &AccessStatus ); if (!NT_SUCCESS(Status)) { goto Cleanup; } // // if the access check failed, then the sid is not a member of the // token // if ((AccessStatus == STATUS_SUCCESS) && (AccessGranted == MEMBER_ACCESS)) { *IsMember = TRUE; } Cleanup: if (!ARGUMENT_PRESENT(TokenHandle) && (EffectiveToken != NULL)) { (VOID) NtClose(EffectiveToken); } if (SecDesc != NULL) { LocalFree(SecDesc); } if (!NT_SUCCESS(Status)) { BITSAltSetLastNTError(Status); return(FALSE); } else { return(TRUE); } } LPHANDLER_FUNCTION_EX g_BITSAltRegisterServiceFunc = NULL; typedef SERVICE_STATUS_HANDLE (*REGISTER_FUNC_TYPE)(LPCTSTR, LPHANDLER_FUNCTION_EX, LPVOID lpContext); VOID WINAPI BITSAltRegisterServiceThunk( DWORD dwControl // requested control code ) { (*g_BITSAltRegisterServiceFunc)( dwControl, 0, NULL, NULL ); return; } SERVICE_STATUS_HANDLE BITSAltRegisterServiceCtrlHandlerExW( LPCTSTR lpServiceName, // name of service LPHANDLER_FUNCTION_EX lpHandlerProc, // handler function LPVOID lpContext // user data ) { // First check if RegisterServerCtrlHandlerEx if available and use // it, otherwise thunk the call. HMODULE AdvapiHandle = LoadLibraryW( L"advapi32.dll" ); if ( !AdvapiHandle ) { // Something is messed up, every machine should have this DLL. return NULL; } SERVICE_STATUS_HANDLE ReturnValue; FARPROC RegisterFunc = GetProcAddress( AdvapiHandle, "RegisterServiceCtrlHandlerExW" ); if ( RegisterFunc ) { ReturnValue = (*(REGISTER_FUNC_TYPE)RegisterFunc)( lpServiceName, lpHandlerProc, lpContext ); } else { if ( g_BITSAltRegisterServiceFunc || lpContext ) { ReturnValue = 0; SetLastError( ERROR_INVALID_PARAMETER ); } else { g_BITSAltRegisterServiceFunc = lpHandlerProc; ReturnValue = RegisterServiceCtrlHandler( lpServiceName, BITSAltRegisterServiceThunk ); if ( !ReturnValue) g_BITSAltRegisterServiceFunc = NULL; } } DWORD OldError = GetLastError(); FreeLibrary( AdvapiHandle ); SetLastError( OldError ); return ReturnValue; } #endif