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.
 
 
 
 
 
 

1010 lines
24 KiB

/*++
Copyright (c) 1996, 1997 Microsoft Corporation
Module Name:
secmisc.c
Abstract:
This module contains miscellaneous security routines for the Protected
Storage.
Author:
Scott Field (sfield) 25-Mar-97
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include "secmisc.h"
#include "unicode5.h"
#include "debug.h"
BOOL
GetUserHKEYEx(
IN LPCWSTR szUser,
IN DWORD dwDesiredAccess,
IN OUT HKEY *hKeyUser,
IN BOOL fCheckDefault // check .Default registry hive when user's not available?
)
{
HKEY hKey = NULL;
LONG lRet;
//
// first, try HKEY_USERS\szUser
// note on WinNT, szUser is an textual sid of the form S-1-5-21-xxx ...
// on Win95, szUser is the username associated with the logged on user
//
//
// unfortunately, if szUser is null or empty, the RegOpenKeyEx below will
// succeed, which is not correct behavior for us. Check for this case
// and retry with default on if fCheckDefault is TRUE.
//
if(szUser == NULL || szUser[0] == L'\0') {
// invalid szUser specified.
lRet = ERROR_FILE_NOT_FOUND;
} else {
lRet = RegOpenKeyExU(
HKEY_USERS,
szUser,
0, // dwOptions
dwDesiredAccess,
&hKey
);
}
if( lRet != ERROR_SUCCESS && fCheckDefault ) {
//
// if that failed, try HKEY_USERS\.Default (for services on NT).
// TODO (lookat), for now, don't fall back to HKEY_USERS\.Default on Win95
// because that is shared across users when profiles are disabled.
//
lRet = RegOpenKeyExU(
HKEY_USERS,
L".Default",
0, // dwOptions
dwDesiredAccess,
&hKey
);
}
if(lRet != ERROR_SUCCESS) {
SetLastError( (DWORD)lRet );
return FALSE;
}
*hKeyUser = hKey;
return TRUE;
}
BOOL
GetUserHKEY(
IN LPCWSTR szUser,
IN DWORD dwDesiredAccess,
IN OUT HKEY *hKeyUser
)
{
//
// winnt: try HKEY_USERS\.Default
// win95: don't go to HKEY_USERS\.Default when profiles disabled.
//
BOOL fRet = GetUserHKEYEx(szUser, dwDesiredAccess, hKeyUser, FALSE);
if(!fRet)
{
//
// see if local system. If so, retry against .Default
//
static const WCHAR szTextualSidSystem[] = TEXTUAL_SID_LOCAL_SYSTEM;
if( memcmp(szUser, szTextualSidSystem, sizeof(szTextualSidSystem)) == 0 )
fRet = GetUserHKEYEx(szUser, dwDesiredAccess, hKeyUser, TRUE);
}
return fRet;
}
BOOL
GetUserTextualSid(
IN HANDLE hUserToken, // optional
IN OUT LPWSTR lpBuffer,
IN OUT LPDWORD nSize
)
{
HANDLE hToken;
PSID pSidUser = NULL;
BOOL fSuccess = FALSE;
if(hUserToken == NULL)
{
if(!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&hToken
))
{
return FALSE;
}
}
else
{
hToken = hUserToken;
}
fSuccess = GetTokenUserSid(hToken, &pSidUser);
if(fSuccess)
{
//
// obtain the textual representaion of the Sid
//
fSuccess = GetTextualSid(
pSidUser, // user binary Sid
lpBuffer, // buffer for TextualSid
nSize // required/result buffer size in chars (including NULL)
);
}
if(pSidUser)
SSFree(pSidUser);
if(hToken != hUserToken)
{
CloseHandle(hToken);
}
return fSuccess;
}
BOOL
GetTextualSid(
IN PSID pSid, // binary Sid
IN OUT LPWSTR TextualSid, // buffer for Textual representaion of Sid
IN OUT LPDWORD dwBufferLen // required/provided TextualSid buffersize
)
{
PSID_IDENTIFIER_AUTHORITY psia;
DWORD dwSubAuthorities;
DWORD dwCounter;
DWORD dwSidSize;
if(!IsValidSid(pSid)) return FALSE;
// obtain SidIdentifierAuthority
psia = GetSidIdentifierAuthority(pSid);
// obtain sidsubauthority count
dwSubAuthorities = *GetSidSubAuthorityCount(pSid);
//
// compute buffer length (conservative guess)
// S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
//
dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(WCHAR);
//
// check provided buffer length.
// If not large enough, indicate proper size and setlasterror
//
if(*dwBufferLen < dwSidSize) {
*dwBufferLen = dwSidSize;
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
//
// prepare S-SID_REVISION-
//
dwSidSize = wsprintfW(TextualSid, L"S-%lu-", SID_REVISION );
//
// prepare SidIdentifierAuthority
//
if ( (psia->Value[0] != 0) || (psia->Value[1] != 0) ) {
dwSidSize += wsprintfW(TextualSid + dwSidSize,
L"0x%02hx%02hx%02hx%02hx%02hx%02hx",
(USHORT)psia->Value[0],
(USHORT)psia->Value[1],
(USHORT)psia->Value[2],
(USHORT)psia->Value[3],
(USHORT)psia->Value[4],
(USHORT)psia->Value[5]);
} else {
dwSidSize += wsprintfW(TextualSid + dwSidSize,
L"%lu",
(ULONG)(psia->Value[5] ) +
(ULONG)(psia->Value[4] << 8) +
(ULONG)(psia->Value[3] << 16) +
(ULONG)(psia->Value[2] << 24) );
}
//
// loop through SidSubAuthorities
//
for (dwCounter = 0 ; dwCounter < dwSubAuthorities ; dwCounter++) {
dwSidSize += wsprintfW(TextualSid + dwSidSize,
L"-%lu", *GetSidSubAuthority(pSid, dwCounter) );
}
*dwBufferLen = dwSidSize + 1; // tell caller how many chars (include NULL)
return TRUE;
}
BOOL
GetThreadAuthenticationId(
IN HANDLE hThread,
IN OUT PLUID AuthenticationId
)
/*++
This function retrieves the authentication Id (LUID) from the access token
specified by the calling thread.
The thread specified by hThread must be impersonating a client.
--*/
{
HANDLE hToken;
TOKEN_STATISTICS TokenInfo;
DWORD dwReturnLen;
BOOL bSuccess;
if(!OpenThreadToken(
hThread,
TOKEN_QUERY,
TRUE,
&hToken
)) return FALSE;
bSuccess = GetTokenInformation(
hToken,
TokenStatistics,
&TokenInfo,
sizeof(TokenInfo),
&dwReturnLen
);
CloseHandle(hToken);
if(!bSuccess) return FALSE;
memcpy(AuthenticationId, &(TokenInfo.AuthenticationId), sizeof(LUID));
return TRUE;
}
BOOL
GetTokenAuthenticationId(
IN HANDLE hUserToken,
IN OUT PLUID AuthenticationId
)
/*++
This function retrieves the authentication Id (LUID) from the specified
access token.
--*/
{
TOKEN_STATISTICS TokenInfo;
DWORD dwReturnLen;
BOOL bSuccess;
HANDLE hToken = NULL;
if(hUserToken == NULL)
{
if(!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&hToken
))
{
return FALSE;
}
}
else
{
hToken = hUserToken;
}
bSuccess = GetTokenInformation(
hToken,
TokenStatistics,
&TokenInfo,
sizeof(TokenInfo),
&dwReturnLen
);
if(hToken != hUserToken)
{
CloseHandle(hToken);
}
if(!bSuccess) return FALSE;
memcpy(AuthenticationId, &(TokenInfo.AuthenticationId), sizeof(LUID));
return TRUE;
}
BOOL
GetTokenUserSid(
IN HANDLE hUserToken, // token to query
IN OUT PSID *ppUserSid // resultant user sid
)
/*++
This function queries the access token specified by the
hToken parameter, and returns an allocated copy of the
TokenUser information on success.
The access token specified by hToken must be opened for
TOKEN_QUERY access.
On success, the return value is TRUE. The caller is
responsible for freeing the resultant UserSid via a call
to SSFree().
On failure, the return value is FALSE. The caller does
not need to free any buffer.
--*/
{
BYTE FastBuffer[256];
LPBYTE SlowBuffer = NULL;
PTOKEN_USER ptgUser;
DWORD cbBuffer;
BOOL fSuccess = FALSE;
HANDLE hToken = NULL;
*ppUserSid = NULL;
if(hUserToken == NULL)
{
if(!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&hToken
))
{
return FALSE;
}
}
else
{
hToken = hUserToken;
}
//
// try querying based on a fast stack based buffer first.
//
ptgUser = (PTOKEN_USER)FastBuffer;
cbBuffer = sizeof(FastBuffer);
fSuccess = GetTokenInformation(
hToken, // identifies access token
TokenUser, // TokenUser info type
ptgUser, // retrieved info buffer
cbBuffer, // size of buffer passed-in
&cbBuffer // required buffer size
);
if(!fSuccess) {
if(GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
//
// try again with the specified buffer size
//
SlowBuffer = (LPBYTE)SSAlloc(cbBuffer);
if(SlowBuffer != NULL) {
ptgUser = (PTOKEN_USER)SlowBuffer;
fSuccess = GetTokenInformation(
hToken, // identifies access token
TokenUser, // TokenUser info type
ptgUser, // retrieved info buffer
cbBuffer, // size of buffer passed-in
&cbBuffer // required buffer size
);
}
}
}
//
// if we got the token info successfully, copy the
// relevant element for the caller.
//
if(fSuccess) {
DWORD cbSid;
// reset to assume failure
fSuccess = FALSE;
cbSid = GetLengthSid(ptgUser->User.Sid);
*ppUserSid = SSAlloc( cbSid );
if(*ppUserSid != NULL) {
fSuccess = CopySid(cbSid, *ppUserSid, ptgUser->User.Sid);
}
else
{
fSuccess = FALSE;
}
}
if(!fSuccess) {
if(*ppUserSid) {
SSFree(*ppUserSid);
*ppUserSid = NULL;
}
}
if(SlowBuffer)
SSFree(SlowBuffer);
if(hToken != hUserToken)
{
CloseHandle(hToken);
}
return fSuccess;
}
BOOL
SetRegistrySecurity(
IN HKEY hKey
)
/*++
The function applies security to the specifed registry key such that only
Local System has Full Control to the registry key. Note that the owner
is not set, which results in a default owner of Administrators.
The specified hKey must to opened for WRITE_DAC access.
--*/
{
SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
PSID pLocalSystemSid = NULL;
SECURITY_DESCRIPTOR sd;
PACL pDacl = NULL;
PACCESS_ALLOWED_ACE pAce;
DWORD dwAclSize;
LONG lRetCode;
BOOL bSuccess = FALSE; // assume this function fails
//
// prepare a Sid representing the Local System account
//
if(!AllocateAndInitializeSid(
&sia,
1,
SECURITY_LOCAL_SYSTEM_RID,
0, 0, 0, 0, 0, 0, 0,
&pLocalSystemSid
)) goto cleanup;
//
// compute size of new acl
//
dwAclSize = sizeof(ACL) +
1 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
GetLengthSid(pLocalSystemSid) ;
//
// allocate storage for Acl
//
pDacl = (PACL)SSAlloc(dwAclSize);
if(pDacl == NULL) goto cleanup;
if(!InitializeAcl(pDacl, dwAclSize, ACL_REVISION))
goto cleanup;
if(!AddAccessAllowedAce(
pDacl,
ACL_REVISION,
KEY_ALL_ACCESS,
pLocalSystemSid
)) goto cleanup;
//
// make it container inherit.
//
if(!GetAce(pDacl, 0, &pAce))
goto cleanup;
pAce->Header.AceFlags = CONTAINER_INHERIT_ACE;
if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
goto cleanup;
if(!SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE)) {
goto cleanup;
}
//
// apply the security descriptor to the registry key
//
lRetCode = RegSetKeySecurity(
hKey,
(SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
&sd
);
if(lRetCode != ERROR_SUCCESS) {
goto cleanup;
}
bSuccess = TRUE; // indicate success
cleanup:
//
// free allocated resources
//
if(pDacl != NULL)
SSFree(pDacl);
if(pLocalSystemSid != NULL)
FreeSid(pLocalSystemSid);
return bSuccess;
}
BOOL
SetPrivilege(
HANDLE hToken, // token handle
LPCWSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
TOKEN_PRIVILEGES tp;
LUID luid;
TOKEN_PRIVILEGES tpPrevious;
DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES);
if(!LookupPrivilegeValueW( NULL, Privilege, &luid )) return FALSE;
//
// first pass. get current privilege setting
//
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = 0;
AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
&tpPrevious,
&cbPrevious
);
if (GetLastError() != ERROR_SUCCESS) return FALSE;
//
// second pass. set privilege based on previous setting
//
tpPrevious.PrivilegeCount = 1;
tpPrevious.Privileges[0].Luid = luid;
if(bEnablePrivilege) {
tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED);
}
else {
tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED &
tpPrevious.Privileges[0].Attributes);
}
AdjustTokenPrivileges(
hToken,
FALSE,
&tpPrevious,
cbPrevious,
NULL,
NULL
);
if (GetLastError() != ERROR_SUCCESS) return FALSE;
return TRUE;
}
BOOL
SetCurrentPrivilege(
LPCWSTR Privilege, // Privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
)
{
BOOL bSuccess=FALSE; // assume failure
HANDLE hToken;
if(OpenProcessToken(
GetCurrentProcess(),
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
&hToken
))
{
if(SetPrivilege(hToken, Privilege, bEnablePrivilege)) bSuccess=TRUE;
CloseHandle(hToken);
}
return bSuccess;
}
#if 0
BOOL
IsDelegating(
IN HANDLE hToken // token to query, open for at least TOKEN_QUERY access
)
/*++
This function determines if the specified access token represents an
impersonation at the delegation impersonate level.
The access token specified by the hToken parameter must be opened for
at least TOKEN_QUERY access.
If the return value is TRUE, the specified hToken is impersonating at
delegation level.
If the return value is FALSE, hToken does not represent delegation level
impersonation.
--*/
{
DWORD dwImpersonationLevel;
DWORD cbTokenInfo;
if( GetTokenInformation(
hToken,
TokenImpersonationLevel,
&dwImpersonationLevel,
sizeof(dwImpersonationLevel),
&cbTokenInfo
) && dwImpersonationLevel == SecurityDelegation ) {
return TRUE;
}
return FALSE;
}
#endif
BOOL
IsUserSidInDomain(
IN PSID pSidDomain, // domain Sid
IN PSID pSidUser // user Sid
)
/*++
This function determines if the user associated with the
pSidUser parameter exists in the domain specified by pSidDomain.
If the user is in the specified domain, the return value is TRUE.
Otherwise, the return value is FALSE.
--*/
{
DWORD dwSubauthorityCount;
DWORD dwSubauthIndex;
//
// pickup count of subauthorities in domain sid. The domain Sid
// is the prefix associated with user sids, so use that count
// as the basis for our comparison.
//
dwSubauthorityCount = (DWORD)*GetSidSubAuthorityCount( pSidDomain );
if( dwSubauthorityCount >= (DWORD)*GetSidSubAuthorityCount( pSidUser ) )
return FALSE;
//
// compare identifier authority values.
//
if(memcmp( GetSidIdentifierAuthority(pSidDomain),
GetSidIdentifierAuthority(pSidUser),
sizeof(SID_IDENTIFIER_AUTHORITY) ) != 0)
return FALSE;
//
// loop through subauthorities comparing equality.
//
for(dwSubauthIndex = 0 ;
dwSubauthIndex < dwSubauthorityCount ;
dwSubauthIndex++) {
if( *GetSidSubAuthority(pSidDomain, dwSubauthIndex) !=
*GetSidSubAuthority(pSidUser, dwSubauthIndex) )
return FALSE;
}
return TRUE;
}
#if 0
BOOL
IsAdministrator(
VOID
)
/*++
This function determines if the calling user is an Administrator.
On Windows 95, this function always returns TRUE, as there is
no difference between users on that platform.
On Windows NT, the caller of this function must be impersonating
the user which is to be queried. If the caller is not impersonating,
this function will always return FALSE.
--*/
{
HANDLE hAccessToken;
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
PSID psidAdministrators = NULL;
BOOL bSuccess;
if(!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&hAccessToken
)) return FALSE;
bSuccess = AllocateAndInitializeSid(
&siaNtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&psidAdministrators
);
if( bSuccess ) {
BOOL fIsMember = FALSE;
bSuccess = CheckTokenMembership( hAccessToken, psidAdministrators, &fIsMember );
if( bSuccess && !fIsMember )
bSuccess = FALSE;
}
CloseHandle( hAccessToken );
if(psidAdministrators)
FreeSid(psidAdministrators);
return bSuccess;
}
#endif
BOOL
IsLocal(
VOID
)
/*++
This function determines if the calling user is logged on locally
On Windows NT, the caller of this function must be impersonating
the user which is to be queried. If the caller is not impersonating,
this function will always return FALSE.
--*/
{
HANDLE hAccessToken;
SID_IDENTIFIER_AUTHORITY siaLocalAuthority = SECURITY_LOCAL_SID_AUTHORITY;
PSID psidLocal = NULL;
BOOL bSuccess;
if(!OpenThreadToken(
GetCurrentThread(),
TOKEN_QUERY,
TRUE,
&hAccessToken
)) return FALSE;
bSuccess = AllocateAndInitializeSid(
&siaLocalAuthority,
1,
SECURITY_LOCAL_RID,
0, 0, 0, 0, 0, 0, 0,
&psidLocal
);
if( bSuccess ) {
BOOL fIsMember = FALSE;
bSuccess = CheckTokenMembership( hAccessToken, psidLocal, &fIsMember );
if( bSuccess && !fIsMember )
bSuccess = FALSE;
}
CloseHandle( hAccessToken );
if(psidLocal)
FreeSid(psidLocal);
return bSuccess;
}
BOOL
IsDomainController(
VOID
)
/*++
This function returns TRUE if the current machine is a Windows NT
domain controller.
The function returns FALSE if the current machine is not a Windows NT
domain controller.
--*/
{
HMODULE hNtDll;
typedef BOOLEAN (NTAPI *RTLGETNTPRODUCTTYPE)(
OUT PNT_PRODUCT_TYPE NtProductType
);
RTLGETNTPRODUCTTYPE _RtlGetNtProductType;
NT_PRODUCT_TYPE NtProductType;
hNtDll = GetModuleHandleW(L"ntdll.dll");
if( hNtDll == NULL )
return FALSE;
_RtlGetNtProductType = (RTLGETNTPRODUCTTYPE)GetProcAddress( hNtDll, "RtlGetNtProductType" );
if( _RtlGetNtProductType == NULL )
return FALSE;
if(_RtlGetNtProductType( &NtProductType )) {
if( NtProductType == NtProductLanManNt )
return TRUE;
}
return FALSE;
}
#if 0
LONG
SecureRegDeleteValueU(
IN HKEY hKey, // handle of key
IN LPCWSTR lpValueName // address of value name
)
/*++
This function securely deletes a value from the registry. This approach
avoids leaving a copy of the old data in the registry backing file after
the delete has occurred.
The specified registry handle hKey must be opened for
REG_QUERY_VALUE | REG_SET_VALUE | DELETE access.
On success, the return value is ERROR_SUCCESS.
On error, the return value is a Win32 error code.
--*/
{
DWORD dwType;
DWORD cbData;
BYTE FastBuffer[ 256 ];
LPBYTE lpData;
LPBYTE SlowBuffer = NULL;
LONG lRet;
cbData = 0; // query size of value data.
//
// query the current size of the registry data.
// zero the current registry data of current size.
// delete the registry data.
// flush the change to disk.
// If errors occur, just do a regular delete.
//
lRet = RegQueryValueExU(
hKey,
lpValueName,
NULL,
&dwType,
NULL,
&cbData
);
if( lRet == ERROR_MORE_DATA ) {
BOOL fSet = TRUE; // assume ok to set
//
// select fast buffer if large enough. otherwise, allocate a buffer
//
if(cbData <= sizeof(FastBuffer)) {
lpData = FastBuffer;
} else {
SlowBuffer = (LPBYTE)SSAlloc( cbData );
if(SlowBuffer == NULL) {
fSet = FALSE; // failure.
} else {
lpData = SlowBuffer;
}
}
if( fSet ) {
ZeroMemory( lpData, cbData );
RegSetValueExU(
hKey,
lpValueName,
0,
dwType,
lpData,
cbData
);
}
}
lRet = RegDeleteValueU( hKey, lpValueName );
RegFlushKey( hKey );
if( SlowBuffer )
SSFree( SlowBuffer );
return lRet;
}
#endif