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.
856 lines
25 KiB
856 lines
25 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1997.
|
|
//
|
|
// File: security.cxx
|
|
//
|
|
// Contents:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "act.hxx"
|
|
|
|
#include <alloca.h>
|
|
#include <ntlsa.h>
|
|
#define SECURITY_WIN32
|
|
#define SECURITY_KERBEROS
|
|
#include <security.h>
|
|
#include <secint.h>
|
|
|
|
// the constant generic mapping structure
|
|
GENERIC_MAPPING sGenericMapping = {
|
|
READ_CONTROL,
|
|
READ_CONTROL,
|
|
READ_CONTROL,
|
|
READ_CONTROL};
|
|
|
|
// Well-known low-privilege service account sids
|
|
const SID sidLocalSystem = {SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SYSTEM_RID };
|
|
const SID sidLocalService = {SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_LOCAL_SERVICE_RID };
|
|
const SID sidNetworkService = {SID_REVISION, 1, SECURITY_NT_AUTHORITY, SECURITY_NETWORK_SERVICE_RID };
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
// CheckForAccess
|
|
//
|
|
// Checks whether the given token has COM_RIGHTS_EXECUTE access in the
|
|
// given security descriptor.
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
BOOL
|
|
CheckForAccess(
|
|
IN CToken * pToken,
|
|
IN SECURITY_DESCRIPTOR * pSD
|
|
)
|
|
{
|
|
// if we have an empty SD, deny everyone
|
|
if (!pSD)
|
|
return FALSE;
|
|
|
|
//
|
|
// Some history here: we used to say, if pToken is NULL (implying an
|
|
// unsecure activation), parse the security descriptor to see if
|
|
// Everyone is granted access, and if so, allow the activation to
|
|
// proceed. This was a DCOM-specific policy decision. In .NET Server
|
|
// we changed instead to mapping an unsecure (unauthenticated) client
|
|
// to the Anonymous identity. (Another DCOM-specific policy decision,
|
|
// but it probably makes more sense than the original one). And so we
|
|
// no longer parse the security descriptor, instead we just do a
|
|
// straight-forward access check.
|
|
//
|
|
HANDLE hToken = pToken->GetToken();
|
|
BOOL fAccess = FALSE;
|
|
BOOL fSuccess = FALSE;
|
|
DWORD dwGrantedAccess;
|
|
PRIVILEGE_SET sPrivilegeSet;
|
|
DWORD dwSetLen = sizeof( sPrivilegeSet );
|
|
|
|
sPrivilegeSet.PrivilegeCount = 1;
|
|
sPrivilegeSet.Control = 0;
|
|
|
|
fSuccess = AccessCheck( (PSECURITY_DESCRIPTOR) pSD,
|
|
hToken,
|
|
COM_RIGHTS_EXECUTE,
|
|
&sGenericMapping,
|
|
&sPrivilegeSet,
|
|
&dwSetLen,
|
|
&dwGrantedAccess,
|
|
&fAccess );
|
|
if (fSuccess && fAccess)
|
|
return TRUE;
|
|
|
|
if (!fSuccess)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "Bad Security Descriptor 0x%08x, Access Check returned 0x%x\n", pSD, GetLastError() ));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
HRESULT IsLowPrivilegeServiceAccount (LPCWSTR pwszDomain, LPCWSTR pwszName, BOOL* pfSvcAccount)
|
|
{
|
|
if (!pwszName || !pfSvcAccount)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*pfSvcAccount = FALSE;
|
|
|
|
HRESULT hr = S_OK;
|
|
BYTE pbSid [sizeof (sidLocalService)] = {0};
|
|
DWORD cbSid = sizeof (pbSid);
|
|
|
|
WCHAR wszDomain [DNLEN + 1] = {0};
|
|
DWORD cchDomain = sizeof (wszDomain) / sizeof (wszDomain[0]);
|
|
SID_NAME_USE eUse;
|
|
|
|
LPCWSTR pwszFullName = pwszName;
|
|
LPWSTR pwszCopyFullName = NULL;
|
|
|
|
// Build the full domain\account string if necessary
|
|
if (pwszDomain)
|
|
{
|
|
SIZE_T cchFullNameLen = lstrlenW (pwszDomain) + 1 + lstrlenW (pwszName) + 1;
|
|
|
|
SafeAllocaAllocate (pwszCopyFullName, cchFullNameLen * sizeof (WCHAR));
|
|
if (!pwszCopyFullName)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
_snwprintf (pwszCopyFullName, cchFullNameLen, L"%s\\%s", pwszDomain, pwszName);
|
|
pwszCopyFullName [cchFullNameLen - 1] = L'\0';
|
|
|
|
pwszFullName = pwszCopyFullName;
|
|
}
|
|
|
|
// Lookup the sid for the account
|
|
if (!LookupAccountName(NULL, pwszFullName, pbSid, &cbSid, wszDomain, &cchDomain, &eUse))
|
|
{
|
|
// Either our sid was too small, or a failure occurred.
|
|
// If the former, returning *pfSvcAccount = FALSE is correct
|
|
// If the latter, the subsequent call to LogonUser will catch it
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Compare to well known service sids
|
|
if (eUse == SidTypeWellKnownGroup &&
|
|
(EqualSid (pbSid, (PSID) &sidLocalService) || EqualSid (pbSid, (PSID) &sidNetworkService)))
|
|
{
|
|
*pfSvcAccount = TRUE;
|
|
}
|
|
}
|
|
|
|
SafeAllocaFree (pwszCopyFullName);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT IsLocalSystemAccount(LPCWSTR pwszDomain, LPCWSTR pwszName, BOOL* pfLocalSystemAccount)
|
|
{
|
|
if (!pwszName || !pfLocalSystemAccount)
|
|
{
|
|
return E_POINTER;
|
|
}
|
|
|
|
*pfLocalSystemAccount = FALSE;
|
|
|
|
HRESULT hr = S_OK;
|
|
BYTE pbSid [sizeof (sidLocalSystem)] = {0};
|
|
DWORD cbSid = sizeof (pbSid);
|
|
|
|
WCHAR wszDomain [DNLEN + 1] = {0};
|
|
DWORD cchDomain = sizeof (wszDomain) / sizeof (wszDomain[0]);
|
|
SID_NAME_USE eUse;
|
|
|
|
LPCWSTR pwszFullName = pwszName;
|
|
LPWSTR pwszCopyFullName = NULL;
|
|
|
|
// Build the full domain\account string if necessary
|
|
if (pwszDomain)
|
|
{
|
|
SIZE_T cchFullNameLen = lstrlenW (pwszDomain) + 1 + lstrlenW (pwszName) + 1;
|
|
|
|
SafeAllocaAllocate (pwszCopyFullName, cchFullNameLen * sizeof (WCHAR));
|
|
if (!pwszCopyFullName)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
_snwprintf (pwszCopyFullName, cchFullNameLen, L"%s\\%s", pwszDomain, pwszName);
|
|
pwszCopyFullName [cchFullNameLen - 1] = L'\0';
|
|
|
|
pwszFullName = pwszCopyFullName;
|
|
}
|
|
|
|
// Lookup the sid for the account
|
|
if (!LookupAccountName(NULL, pwszFullName, pbSid, &cbSid, wszDomain, &cchDomain, &eUse))
|
|
{
|
|
// Either our sid was too small, or a failure occurred.
|
|
// If the former, returning *pfLocalSystemAccount = FALSE is correct
|
|
// If the latter, the subsequent call to LogonUser will catch it
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
{
|
|
// Compare to well known service sids
|
|
if (eUse == SidTypeWellKnownGroup &&
|
|
(EqualSid (pbSid, (PSID) &sidLocalSystem)))
|
|
{
|
|
*pfLocalSystemAccount = TRUE;
|
|
}
|
|
}
|
|
|
|
SafeAllocaFree (pwszCopyFullName);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HANDLE
|
|
GetRunAsToken(
|
|
DWORD clsctx,
|
|
WCHAR *pwszAppID,
|
|
WCHAR *pwszRunAsDomainName,
|
|
WCHAR *pwszRunAsUserName,
|
|
BOOL fForLaunch)
|
|
{
|
|
LSA_OBJECT_ATTRIBUTES sObjAttributes;
|
|
HANDLE hPolicy = NULL;
|
|
LSA_UNICODE_STRING sKey;
|
|
WCHAR wszKey[CLSIDSTR_MAX+5];
|
|
PLSA_UNICODE_STRING psPassword;
|
|
HANDLE hToken;
|
|
NTSTATUS Status;
|
|
PKERB_INTERACTIVE_LOGON LogonInfo;
|
|
ULONG LogonInfoSize = sizeof(KERB_INTERACTIVE_LOGON);
|
|
STRING Name;
|
|
ULONG PackageId;
|
|
TOKEN_SOURCE SourceContext;
|
|
PKERB_INTERACTIVE_PROFILE Profile = NULL;
|
|
ULONG ProfileSize;
|
|
LUID LogonId;
|
|
QUOTA_LIMITS Quotas;
|
|
NTSTATUS SubStatus;
|
|
PUCHAR Where;
|
|
UNICODE_STRING usRunAsUserName, usRunAsDomainName;
|
|
PTOKEN_GROUPS TokenGroups = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if ( !pwszAppID )
|
|
{
|
|
// if we have a RunAs, we'd better have an appid....
|
|
return 0;
|
|
}
|
|
ASSERT(gLSAHandle);
|
|
ASSERT(gSidService);
|
|
ASSERT(gpwszDefaultDomainName[0]);
|
|
|
|
// formulate the access key
|
|
lstrcpyW(wszKey, L"SCM:");
|
|
lstrcatW(wszKey, pwszAppID );
|
|
|
|
// UNICODE_STRING length fields are in bytes and include the NULL
|
|
// terminator
|
|
sKey.Length = (USHORT)((lstrlenW(wszKey) + 1) * sizeof(WCHAR));
|
|
sKey.MaximumLength = (CLSIDSTR_MAX + 5) * sizeof(WCHAR);
|
|
sKey.Buffer = wszKey;
|
|
|
|
// Open the local security policy
|
|
InitializeObjectAttributes(&sObjAttributes, NULL, 0L, NULL, NULL);
|
|
if (!NT_SUCCESS(LsaOpenPolicy(NULL, &sObjAttributes,
|
|
POLICY_GET_PRIVATE_INFORMATION, &hPolicy)))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Read the user's password
|
|
if (!NT_SUCCESS(LsaRetrievePrivateData(hPolicy, &sKey, &psPassword)))
|
|
{
|
|
LsaClose(hPolicy);
|
|
return 0;
|
|
}
|
|
|
|
// Close the policy handle, we're done with it now.
|
|
LsaClose(hPolicy);
|
|
|
|
// Possible for LsaRetrievePrivateData to return success but with a NULL
|
|
// psPassword. If this happens we fail.
|
|
if (!psPassword)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Special case of NT AUTHORITY\System - cannot use LsaLogonUser to obtain such a
|
|
// token, so we use our own process token. Note that we currently only support
|
|
// this on the server registration code-path, and not the server-launch code path.
|
|
//
|
|
if (!fForLaunch)
|
|
{
|
|
BOOL fLocalSystemAccount = FALSE;
|
|
hr = IsLocalSystemAccount(pwszRunAsDomainName, pwszRunAsUserName, &fLocalSystemAccount);
|
|
if (SUCCEEDED(hr)) // keep going here in case of errors, LsaLogonUser will just fail below.
|
|
{
|
|
// If SYSTEM and user has supplied a blank password (for compatibility
|
|
// reasons), use our own token
|
|
if (fLocalSystemAccount && !lstrcmpiW(psPassword->Buffer, L""))
|
|
{
|
|
BOOL fResult = FALSE;
|
|
HANDLE hProcess = NULL;
|
|
HANDLE hProcessToken = NULL;
|
|
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
|
|
if (hProcess)
|
|
{
|
|
fResult = OpenProcessToken(hProcess, TOKEN_ALL_ACCESS, &hProcessToken);
|
|
ASSERT(!fResult || hProcessToken);
|
|
CloseHandle(hProcess);
|
|
}
|
|
return hProcessToken;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN b = RtlCreateUnicodeString(&usRunAsUserName, pwszRunAsUserName);
|
|
if (FALSE == b)
|
|
{
|
|
SecureZeroMemory(psPassword->Buffer, psPassword->Length);
|
|
LsaFreeMemory( psPassword );
|
|
return 0;
|
|
}
|
|
if ( (pwszRunAsDomainName[0] == L'.') && (pwszRunAsDomainName[1] == L'\0'))
|
|
b = RtlCreateUnicodeString(&usRunAsDomainName, gpwszDefaultDomainName);
|
|
else
|
|
b = RtlCreateUnicodeString(&usRunAsDomainName, pwszRunAsDomainName);
|
|
if (FALSE == b)
|
|
{
|
|
RtlFreeUnicodeString(&usRunAsUserName);
|
|
SecureZeroMemory(psPassword->Buffer, psPassword->Length);
|
|
LsaFreeMemory( psPassword );
|
|
return 0;
|
|
}
|
|
LogonInfoSize += usRunAsUserName.MaximumLength + usRunAsDomainName.MaximumLength + psPassword->MaximumLength ;
|
|
LogonInfo = (PKERB_INTERACTIVE_LOGON) LocalAlloc(LMEM_ZEROINIT, LogonInfoSize);
|
|
if (!LogonInfo)
|
|
{
|
|
RtlFreeUnicodeString(&usRunAsUserName);
|
|
RtlFreeUnicodeString(&usRunAsDomainName);
|
|
SecureZeroMemory(psPassword->Buffer, psPassword->Length);
|
|
LsaFreeMemory( psPassword );
|
|
return NULL;
|
|
}
|
|
|
|
LogonInfo->MessageType = KerbInteractiveLogon;
|
|
|
|
Where = (PUCHAR) (LogonInfo + 1);
|
|
|
|
LogonInfo->UserName.Buffer = (LPWSTR) Where;
|
|
LogonInfo->UserName.MaximumLength = usRunAsUserName.MaximumLength ;
|
|
LogonInfo->UserName.Length = usRunAsUserName.Length ;
|
|
|
|
RtlCopyMemory( LogonInfo->UserName.Buffer,
|
|
usRunAsUserName.Buffer,
|
|
usRunAsUserName.MaximumLength );
|
|
|
|
Where += LogonInfo->UserName.Length + sizeof(WCHAR);
|
|
|
|
LogonInfo->LogonDomainName.Buffer = (LPWSTR) Where ;
|
|
LogonInfo->LogonDomainName.MaximumLength = usRunAsDomainName.MaximumLength;
|
|
LogonInfo->LogonDomainName.Length = usRunAsDomainName.Length ;
|
|
|
|
RtlCopyMemory( LogonInfo->LogonDomainName.Buffer,
|
|
usRunAsDomainName.Buffer,
|
|
usRunAsDomainName.MaximumLength );
|
|
|
|
Where += LogonInfo->LogonDomainName.Length + sizeof(WCHAR);
|
|
|
|
LogonInfo->Password.Buffer = (LPWSTR) Where;
|
|
LogonInfo->Password.MaximumLength = psPassword->MaximumLength;
|
|
LogonInfo->Password.Length = psPassword->Length - sizeof(WCHAR); // The LSA API retrives length=maxlength
|
|
|
|
RtlCopyMemory( LogonInfo->Password.Buffer,
|
|
psPassword->Buffer,
|
|
LogonInfo->Password.MaximumLength );
|
|
|
|
// Clear the password
|
|
SecureZeroMemory(psPassword->Buffer, psPassword->Length);
|
|
LsaFreeMemory( psPassword );
|
|
RtlFreeUnicodeString(&usRunAsUserName);
|
|
RtlFreeUnicodeString(&usRunAsDomainName);
|
|
Where += LogonInfo->Password.Length + sizeof(WCHAR);
|
|
strncpy(
|
|
SourceContext.SourceName,
|
|
"DCOMSCM",sizeof(SourceContext.SourceName)
|
|
);
|
|
|
|
Status = NtAllocateLocallyUniqueId(
|
|
&SourceContext.SourceIdentifier
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SecureZeroMemory(LogonInfo, LogonInfoSize);
|
|
LocalFree(LogonInfo);
|
|
return NULL ;
|
|
}
|
|
|
|
RtlInitString( &Name, NEGOSSP_NAME_A);
|
|
|
|
Status = LsaLookupAuthenticationPackage(
|
|
gLSAHandle,
|
|
&Name,
|
|
&PackageId
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SecureZeroMemory(LogonInfo, LogonInfoSize);
|
|
LocalFree(LogonInfo);
|
|
return NULL ;
|
|
}
|
|
//
|
|
// Now call LsaLogonUser
|
|
//
|
|
|
|
RtlInitString(
|
|
&Name,
|
|
"DCOMSCM"
|
|
);
|
|
|
|
BOOL fSvcAccount = FALSE;
|
|
if (FAILED (IsLowPrivilegeServiceAccount (pwszRunAsDomainName, pwszRunAsUserName, &fSvcAccount)))
|
|
{
|
|
SecureZeroMemory(LogonInfo, LogonInfoSize);
|
|
LocalFree(LogonInfo);
|
|
return NULL;
|
|
}
|
|
|
|
// Service accounts should have blank passwords
|
|
ASSERT (!fSvcAccount || (psPassword->Buffer && !psPassword->Buffer[0]));
|
|
|
|
#define TOKEN_GROUP_COUNT 1
|
|
|
|
// if local/network service, no need to add the serivce SID
|
|
if (!fSvcAccount)
|
|
{
|
|
TokenGroups = (PTOKEN_GROUPS)LocalAlloc(LMEM_ZEROINIT, sizeof(TOKEN_GROUPS) +
|
|
(TOKEN_GROUP_COUNT - ANYSIZE_ARRAY) * sizeof(SID_AND_ATTRIBUTES));
|
|
if (TokenGroups == NULL) {
|
|
SecureZeroMemory(LogonInfo, LogonInfoSize);
|
|
LocalFree(LogonInfo);
|
|
return NULL ;
|
|
}
|
|
|
|
// Add the service SID to the token so the resulting
|
|
// process can impersonate
|
|
|
|
TokenGroups->GroupCount = TOKEN_GROUP_COUNT;
|
|
TokenGroups->Groups[0].Sid = gSidService;
|
|
TokenGroups->Groups[0].Attributes =
|
|
SE_GROUP_MANDATORY | SE_GROUP_ENABLED |
|
|
SE_GROUP_ENABLED_BY_DEFAULT;
|
|
}
|
|
Status = LsaLogonUser(
|
|
gLSAHandle,
|
|
&Name,
|
|
fSvcAccount ? Service : Batch,
|
|
PackageId,
|
|
LogonInfo,
|
|
LogonInfoSize,
|
|
TokenGroups, //NULL for service accounts
|
|
&SourceContext,
|
|
(PVOID *) &Profile,
|
|
&ProfileSize,
|
|
&LogonId,
|
|
&hToken,
|
|
&Quotas,
|
|
&SubStatus
|
|
);
|
|
|
|
SecureZeroMemory(LogonInfo, LogonInfoSize);
|
|
LocalFree(LogonInfo);
|
|
LocalFree(TokenGroups);
|
|
// Log the specifed user on
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
// a-sergiv (Sergei O. Ivanov), 6-17-99
|
|
// Fix for com+ 9383/nt 272085
|
|
|
|
// Apply event filters
|
|
DWORD dwActLogLvl = GetActivationFailureLoggingLevel();
|
|
if(dwActLogLvl == 2)
|
|
return 0;
|
|
if(dwActLogLvl != 1 && clsctx & CLSCTX_NO_FAILURE_LOG)
|
|
return 0;
|
|
|
|
// for this message,
|
|
// %1 is the error number string
|
|
// %2 is the domain name
|
|
// %3 is the user name
|
|
// %4 is the CLSID
|
|
HANDLE LogHandle;
|
|
LPWSTR Strings[4]; // array of message strings.
|
|
WCHAR wszErrnum[20];
|
|
WCHAR wszClsid[GUIDSTR_MAX];
|
|
|
|
// Save the error number
|
|
wsprintf(wszErrnum, L"%lu",GetLastError() );
|
|
Strings[0] = wszErrnum;
|
|
|
|
// Put in the RunAs identity
|
|
Strings[1] = pwszRunAsDomainName;
|
|
Strings[2] = pwszRunAsUserName;
|
|
|
|
// Get the clsid
|
|
Strings[3] = pwszAppID;
|
|
|
|
// Get the log handle, then report then event.
|
|
LogHandle = RegisterEventSource( NULL,
|
|
SCM_EVENT_SOURCE );
|
|
|
|
if ( LogHandle )
|
|
{
|
|
ReportEvent( LogHandle,
|
|
EVENTLOG_ERROR_TYPE,
|
|
0, // event category
|
|
EVENT_RPCSS_RUNAS_CANT_LOGIN,
|
|
NULL, // SID
|
|
4, // 4 strings passed
|
|
0, // 0 bytes of binary
|
|
(LPCTSTR *)Strings, // array of strings
|
|
NULL ); // no raw data
|
|
|
|
// clean up the event log handle
|
|
DeregisterEventSource(LogHandle);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
if (Profile)
|
|
{
|
|
LsaFreeReturnBuffer(Profile);
|
|
}
|
|
}
|
|
|
|
return hToken;
|
|
}
|
|
|
|
BOOL
|
|
DuplicateTokenAsPrimary(
|
|
HANDLE hUserToken,
|
|
PSID psidUserSid,
|
|
HANDLE *hPrimaryToken
|
|
)
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PSECURITY_DESCRIPTOR psdNewProcessTokenSD;
|
|
NTSTATUS NtStatus;
|
|
|
|
*hPrimaryToken = NULL;
|
|
|
|
if (hUserToken == NULL)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
//
|
|
// Create the security descriptor that we want to put in the Token.
|
|
//
|
|
CAccessInfo AccessInfo(psidUserSid);
|
|
psdNewProcessTokenSD = AccessInfo.IdentifyAccess(
|
|
FALSE,
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS |
|
|
TOKEN_ADJUST_DEFAULT | TOKEN_QUERY |
|
|
TOKEN_DUPLICATE | TOKEN_IMPERSONATE | READ_CONTROL,
|
|
TOKEN_QUERY
|
|
);
|
|
if (psdNewProcessTokenSD == NULL)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "Failed to create SD for process token\n"));
|
|
return(FALSE);
|
|
}
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
psdNewProcessTokenSD
|
|
);
|
|
NtStatus = NtDuplicateToken(
|
|
hUserToken, // Duplicate this token
|
|
TOKEN_ALL_ACCESS, // Give me this access to the resulting token
|
|
&ObjectAttributes,
|
|
FALSE, // EffectiveOnly
|
|
TokenPrimary, // TokenType
|
|
hPrimaryToken // Duplicate token handle stored here
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "CreateAndSetProcessToken failed to duplicate primary token for new user process, status = 0x%lx\n", NtStatus));
|
|
return(FALSE);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DuplicateTokenForSessionUse(
|
|
HANDLE hUserToken,
|
|
HANDLE *hDuplicate
|
|
)
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PSECURITY_DESCRIPTOR psdNewProcessTokenSD;
|
|
NTSTATUS NtStatus;
|
|
|
|
if (hUserToken == NULL) {
|
|
return(TRUE);
|
|
}
|
|
|
|
*hDuplicate = NULL;
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
NtStatus = NtDuplicateToken(
|
|
hUserToken, // Duplicate this token
|
|
TOKEN_ALL_ACCESS, //Give me this access to the resulting token
|
|
&ObjectAttributes,
|
|
FALSE, // EffectiveOnly
|
|
TokenPrimary, // TokenType
|
|
hDuplicate // Duplicate token handle stored here
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
CairoleDebugOut((DEB_ERROR, "CreateAndSetProcessToken failed to duplicate primary token for new user process, status = 0x%lx\n", NtStatus));
|
|
return(FALSE);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetUserSid
|
|
*
|
|
* Allocs space for the user sid, fills it in and returns a pointer.
|
|
* The sid should be freed by calling DeleteUserSid.
|
|
*
|
|
* Note the sid returned is the user's real sid, not the per-logon sid.
|
|
*
|
|
* Returns pointer to sid or NULL on failure.
|
|
*
|
|
* History:
|
|
* 26-Aug-92 Davidc Created.
|
|
* 31-Mar-94 AndyH Copied from Winlogon, changed arg from pGlobals
|
|
\***************************************************************************/
|
|
PSID
|
|
GetUserSid(
|
|
HANDLE hUserToken
|
|
)
|
|
{
|
|
BYTE achBuffer[100];
|
|
PTOKEN_USER pUser = (PTOKEN_USER) &achBuffer;
|
|
PSID pSid;
|
|
DWORD dwBytesRequired;
|
|
NTSTATUS NtStatus;
|
|
BOOL fAllocatedBuffer = FALSE;
|
|
|
|
NtStatus = NtQueryInformationToken(
|
|
hUserToken, // Handle
|
|
TokenUser, // TokenInformationClass
|
|
pUser, // TokenInformation
|
|
sizeof(achBuffer), // TokenInformationLength
|
|
&dwBytesRequired // ReturnLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
if (NtStatus != STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
ASSERT(NtStatus == STATUS_BUFFER_TOO_SMALL);
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Allocate space for the user info
|
|
//
|
|
|
|
pUser = (PTOKEN_USER) PrivMemAlloc(dwBytesRequired);
|
|
if (pUser == NULL)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "Failed to allocate %d bytes\n", dwBytesRequired));
|
|
ASSERT(pUser != NULL);
|
|
return NULL;
|
|
}
|
|
|
|
fAllocatedBuffer = TRUE;
|
|
|
|
//
|
|
// Read in the UserInfo
|
|
//
|
|
|
|
NtStatus = NtQueryInformationToken(
|
|
hUserToken, // Handle
|
|
TokenUser, // TokenInformationClass
|
|
pUser, // TokenInformation
|
|
dwBytesRequired, // TokenInformationLength
|
|
&dwBytesRequired // ReturnLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "Failed to query user info from user token, status = 0x%lx\n", NtStatus));
|
|
ASSERT(NtStatus == STATUS_SUCCESS);
|
|
PrivMemFree((HANDLE)pUser);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
// Alloc buffer for copy of SID
|
|
|
|
dwBytesRequired = RtlLengthSid(pUser->User.Sid);
|
|
pSid = (PSID) PrivMemAlloc(dwBytesRequired);
|
|
if (pSid == NULL)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "Failed to allocate %d bytes\n", dwBytesRequired));
|
|
if (fAllocatedBuffer == TRUE)
|
|
{
|
|
PrivMemFree((HANDLE)pUser);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Copy SID
|
|
|
|
NtStatus = RtlCopySid(dwBytesRequired, pSid, pUser->User.Sid);
|
|
if (fAllocatedBuffer == TRUE)
|
|
{
|
|
PrivMemFree((HANDLE)pUser);
|
|
}
|
|
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "RtlCopySid failed, status = 0x%lx\n", NtStatus));
|
|
ASSERT(NtStatus != STATUS_SUCCESS);
|
|
PrivMemFree(pSid);
|
|
pSid = NULL;
|
|
}
|
|
|
|
|
|
return pSid;
|
|
}
|
|
|
|
|
|
HANDLE GetUserTokenForSession(
|
|
ULONG ulSessionId
|
|
)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
HANDLE hToken = NULL;
|
|
|
|
//
|
|
// We used to have a lot of complicated code for doing this
|
|
// logic. The WTSQueryUserToken api replaces all of that
|
|
// quite nicely. Only downside is that wtsapi32.dll has a
|
|
// moderately large dependency list. Since under normal
|
|
// conditions we won't need to use this api until after someone
|
|
// logs on, we delay-load link to wtsapi32 to avoid the load
|
|
// hit during boot.
|
|
//
|
|
fRet = WTSQueryUserToken(ulSessionId, &hToken);
|
|
if (!fRet)
|
|
{
|
|
return NULL;
|
|
}
|
|
ASSERT(hToken);
|
|
return hToken;
|
|
}
|
|
|
|
// Global default launch permissions
|
|
CSecDescriptor* gpDefaultLaunchPermissions;
|
|
|
|
|
|
CSecDescriptor*
|
|
GetDefaultLaunchPermissions()
|
|
{
|
|
CSecDescriptor* pSD = NULL;
|
|
|
|
gpClientLock->LockShared();
|
|
|
|
pSD = gpDefaultLaunchPermissions;
|
|
if (pSD)
|
|
pSD->IncRefCount();
|
|
|
|
gpClientLock->UnlockShared();
|
|
|
|
return pSD;
|
|
}
|
|
|
|
void
|
|
SetDefaultLaunchPermissions(CSecDescriptor* pNewLaunchPerms)
|
|
{
|
|
CSecDescriptor* pOldSD = NULL;
|
|
|
|
gpClientLock->LockExclusive();
|
|
|
|
pOldSD = gpDefaultLaunchPermissions;
|
|
gpDefaultLaunchPermissions = pNewLaunchPerms;
|
|
if (gpDefaultLaunchPermissions)
|
|
gpDefaultLaunchPermissions->IncRefCount();
|
|
|
|
gpClientLock->UnlockExclusive();
|
|
|
|
if (pOldSD)
|
|
pOldSD->DecRefCount();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
CSecDescriptor::CSecDescriptor(SECURITY_DESCRIPTOR* pSD) : _lRefs(1)
|
|
{
|
|
ASSERT(pSD);
|
|
_pSD = pSD; // we own it now
|
|
}
|
|
|
|
CSecDescriptor::~CSecDescriptor()
|
|
{
|
|
ASSERT(_lRefs == 0);
|
|
ASSERT(_pSD);
|
|
PrivMemFree(_pSD);
|
|
}
|
|
|
|
void CSecDescriptor::IncRefCount()
|
|
{
|
|
ASSERT(_lRefs > 0);
|
|
LONG lRefs = InterlockedIncrement(&_lRefs);
|
|
}
|
|
|
|
void CSecDescriptor::DecRefCount()
|
|
{
|
|
ASSERT(_lRefs > 0);
|
|
LONG lRefs = InterlockedDecrement(&_lRefs);
|
|
if (lRefs == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
SECURITY_DESCRIPTOR* CSecDescriptor::GetSD()
|
|
{
|
|
ASSERT(_pSD);
|
|
ASSERT(_lRefs > 0);
|
|
return _pSD;
|
|
}
|