263 lines
10 KiB
C#
263 lines
10 KiB
C#
|
// <copyright>
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
|
||
|
namespace System.ServiceModel.Channels
|
||
|
{
|
||
|
using System.ComponentModel;
|
||
|
using System.Runtime;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Security;
|
||
|
using System.Security.Principal;
|
||
|
using System.Text;
|
||
|
|
||
|
using SafeCloseHandle = System.ServiceModel.Activation.SafeCloseHandle;
|
||
|
|
||
|
/// <summary>
|
||
|
/// This class provides the entry points into Application Container related functionality.
|
||
|
/// Callers are expected to check if the application is running in an AppContainer before
|
||
|
/// invoking any of the methods in this class.
|
||
|
/// </summary>
|
||
|
class AppContainerInfo
|
||
|
{
|
||
|
static object thisLock = new object();
|
||
|
static bool isAppContainerSupported;
|
||
|
static bool isRunningInAppContainer;
|
||
|
static volatile bool isRunningInAppContainerSet;
|
||
|
static object isRunningInAppContainerLock = new object();
|
||
|
static int? currentSessionId;
|
||
|
static volatile SecurityIdentifier currentAppContainerSid;
|
||
|
|
||
|
static AppContainerInfo()
|
||
|
{
|
||
|
// AppContainers are supported starting in Win8
|
||
|
isAppContainerSupported = OSEnvironmentHelper.IsAtLeast(OSVersion.Win8);
|
||
|
|
||
|
if (!isAppContainerSupported)
|
||
|
{
|
||
|
isRunningInAppContainerSet = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
AppContainerInfo(int sessionId, string namedObjectPath)
|
||
|
{
|
||
|
this.SessionId = sessionId;
|
||
|
this.NamedObjectPath = namedObjectPath;
|
||
|
}
|
||
|
|
||
|
internal static bool IsAppContainerSupported
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return isAppContainerSupported;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal static bool IsRunningInAppContainer
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
// The AppContainerInfo.RunningInAppContainer() API may throw security exceptions,
|
||
|
// so cannot be used inside the static constructor of the class.
|
||
|
if (!isRunningInAppContainerSet)
|
||
|
{
|
||
|
lock (isRunningInAppContainerLock)
|
||
|
{
|
||
|
if (!isRunningInAppContainerSet)
|
||
|
{
|
||
|
isRunningInAppContainer = AppContainerInfo.RunningInAppContainer();
|
||
|
isRunningInAppContainerSet = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return isRunningInAppContainer;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal int SessionId { get; private set; }
|
||
|
|
||
|
internal string NamedObjectPath { get; private set; }
|
||
|
|
||
|
internal static AppContainerInfo CreateAppContainerInfo(string fullName, int sessionId)
|
||
|
{
|
||
|
Fx.Assert(IsAppContainerSupported, "AppContainers are not supported.");
|
||
|
Fx.Assert(!string.IsNullOrEmpty(fullName), "fullName should be provided to initialize an AppContainerInfo.");
|
||
|
|
||
|
int appSession = sessionId;
|
||
|
if (appSession == ApplicationContainerSettings.CurrentSession)
|
||
|
{
|
||
|
lock (thisLock)
|
||
|
{
|
||
|
if (currentSessionId == null)
|
||
|
{
|
||
|
currentSessionId = AppContainerInfo.GetCurrentSessionId();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
appSession = currentSessionId.Value;
|
||
|
}
|
||
|
|
||
|
string namedObjectPath = AppContainerInfo.GetAppContainerNamedObjectPath(fullName);
|
||
|
return new AppContainerInfo(appSession, namedObjectPath);
|
||
|
}
|
||
|
|
||
|
[SecuritySafeCritical]
|
||
|
[Fx.Tag.SecurityNote(Critical = "This calls into the SecurityCritical method GetCurrentProcessToken and unsafe GetAppContainerSid.",
|
||
|
Safe = "All critical token access and dispose is ensured. " +
|
||
|
" The Sid is a non protected resource and can be obtained only if we have the appropriate process token permissions.")]
|
||
|
internal static SecurityIdentifier GetCurrentAppContainerSid()
|
||
|
{
|
||
|
Fx.Assert(AppContainerInfo.IsAppContainerSupported, "AppContainers are not supported.");
|
||
|
if (currentAppContainerSid == null)
|
||
|
{
|
||
|
lock (thisLock)
|
||
|
{
|
||
|
if (currentAppContainerSid == null)
|
||
|
{
|
||
|
SafeCloseHandle tokenHandle = null;
|
||
|
try
|
||
|
{
|
||
|
tokenHandle = AppContainerInfo.GetCurrentProcessToken();
|
||
|
currentAppContainerSid = UnsafeNativeMethods.GetAppContainerSid(tokenHandle);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (tokenHandle != null)
|
||
|
{
|
||
|
tokenHandle.Dispose();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return currentAppContainerSid;
|
||
|
}
|
||
|
|
||
|
[SecuritySafeCritical]
|
||
|
[Fx.Tag.SecurityNote(Safe = "Process token handle access and dispose is ensured here and we only return a non-critical flag.")]
|
||
|
static bool RunningInAppContainer()
|
||
|
{
|
||
|
Fx.Assert(AppContainerInfo.IsAppContainerSupported, "AppContainers are not supported.");
|
||
|
SafeCloseHandle tokenHandle = null;
|
||
|
try
|
||
|
{
|
||
|
tokenHandle = AppContainerInfo.GetCurrentProcessToken();
|
||
|
return UnsafeNativeMethods.RunningInAppContainer(tokenHandle);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (tokenHandle != null)
|
||
|
{
|
||
|
tokenHandle.Dispose();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SecuritySafeCritical]
|
||
|
[Fx.Tag.SecurityNote(Critical = "Calls into unsafe native AppContainer package resolution methods.",
|
||
|
Safe = "Wraps all access and disposal of securityDescriptors and returns a NamedObjectPath, " +
|
||
|
"which is a non protected resource.")]
|
||
|
static string GetAppContainerNamedObjectPath(string name)
|
||
|
{
|
||
|
// 1. Derive the PackageFamilyName(PFN) from the PackageFullName
|
||
|
// 2. Get the AppContainerSID from the PFN
|
||
|
// 3. Get the NamedObjectPath from the AppContainerSID
|
||
|
Fx.Assert(AppContainerInfo.IsAppContainerSupported, "AppContainers are not supported.");
|
||
|
IntPtr appContainerSid = IntPtr.Zero;
|
||
|
|
||
|
// Package Full Name => Package family name
|
||
|
uint packageFamilyNameLength = UnsafeNativeMethods.MAX_PATH;
|
||
|
StringBuilder packageFamilyNameBuilder = new StringBuilder((int)UnsafeNativeMethods.MAX_PATH);
|
||
|
string packageFamilyName;
|
||
|
int errorCode = UnsafeNativeMethods.PackageFamilyNameFromFullName(name, ref packageFamilyNameLength, packageFamilyNameBuilder);
|
||
|
if (errorCode != UnsafeNativeMethods.ERROR_SUCCESS)
|
||
|
{
|
||
|
throw FxTrace.Exception.AsError(new Win32Exception(errorCode, SR.GetString(SR.PackageFullNameInvalid, name)));
|
||
|
}
|
||
|
|
||
|
packageFamilyName = packageFamilyNameBuilder.ToString();
|
||
|
|
||
|
try
|
||
|
{
|
||
|
// PackageFamilyName => AppContainerSID
|
||
|
int hresult = UnsafeNativeMethods.DeriveAppContainerSidFromAppContainerName(
|
||
|
packageFamilyName,
|
||
|
out appContainerSid);
|
||
|
if (hresult != 0)
|
||
|
{
|
||
|
errorCode = Marshal.GetLastWin32Error();
|
||
|
throw FxTrace.Exception.AsError(new Win32Exception(errorCode));
|
||
|
}
|
||
|
|
||
|
// AppContainerSID => NamedObjectPath
|
||
|
StringBuilder namedObjectPath = new StringBuilder((int)UnsafeNativeMethods.MAX_PATH);
|
||
|
uint returnLength = 0;
|
||
|
if (!UnsafeNativeMethods.GetAppContainerNamedObjectPath(
|
||
|
IntPtr.Zero,
|
||
|
appContainerSid,
|
||
|
UnsafeNativeMethods.MAX_PATH,
|
||
|
namedObjectPath,
|
||
|
ref returnLength))
|
||
|
{
|
||
|
errorCode = Marshal.GetLastWin32Error();
|
||
|
throw FxTrace.Exception.AsError(new Win32Exception(errorCode));
|
||
|
}
|
||
|
|
||
|
return namedObjectPath.ToString();
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (appContainerSid != IntPtr.Zero)
|
||
|
{
|
||
|
UnsafeNativeMethods.FreeSid(appContainerSid);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[SecuritySafeCritical]
|
||
|
[Fx.Tag.SecurityNote(Critical = "Accesses the native current process token.",
|
||
|
Safe = "The session id returned is a non-critical resource and " +
|
||
|
" we ensure that the current process token handle created is disposed here.")]
|
||
|
static int GetCurrentSessionId()
|
||
|
{
|
||
|
Fx.Assert(AppContainerInfo.IsAppContainerSupported, "AppContainers are not supported.");
|
||
|
SafeCloseHandle tokenHandle = null;
|
||
|
try
|
||
|
{
|
||
|
tokenHandle = AppContainerInfo.GetCurrentProcessToken();
|
||
|
return UnsafeNativeMethods.GetSessionId(tokenHandle);
|
||
|
}
|
||
|
finally
|
||
|
{
|
||
|
if (tokenHandle != null)
|
||
|
{
|
||
|
tokenHandle.Dispose();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the process token using TokenAccessLevels.Query
|
||
|
/// </summary>
|
||
|
/// <returns>ProcessToken as a SafeCloseHandle.</returns>
|
||
|
[SecurityCritical]
|
||
|
[Fx.Tag.SecurityNote(Critical = "Returns a process token. The caller is responsible for disposing the SafeCloseHandle.")]
|
||
|
static SafeCloseHandle GetCurrentProcessToken()
|
||
|
{
|
||
|
SafeCloseHandle tokenHandle = null;
|
||
|
if (!UnsafeNativeMethods.OpenProcessToken(
|
||
|
UnsafeNativeMethods.GetCurrentProcess(),
|
||
|
TokenAccessLevels.Query,
|
||
|
out tokenHandle))
|
||
|
{
|
||
|
int error = Marshal.GetLastWin32Error();
|
||
|
throw FxTrace.Exception.AsError(new Win32Exception(error));
|
||
|
}
|
||
|
|
||
|
return tokenHandle;
|
||
|
}
|
||
|
}
|
||
|
}
|