/mcs/class/referencesource/System.ServiceModel/System/ServiceModel/ComIntegration/ComPlusAuthorization.cs
C# | 618 lines | 528 code | 70 blank | 20 comment | 82 complexity | d2dd39af189550930e971cdb6dedce93 MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
- //----------------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //----------------------------------------------------------------------------
- namespace System.ServiceModel.ComIntegration
- {
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Runtime;
- using System.Runtime.InteropServices;
- using System.Security;
- using System.Security.AccessControl;
- using System.Security.Permissions;
- using System.Security.Principal;
- using System.ServiceModel;
- using System.ServiceModel.Diagnostics;
- using SafeCloseHandle = System.IdentityModel.SafeCloseHandle;
- using SafeHGlobalHandle = System.IdentityModel.SafeHGlobalHandle;
- static class SecurityUtils
- {
- static WindowsIdentity anonymousIdentity;
- static WindowsIdentity processIdentity;
- static object lockObject = new object();
- [Fx.Tag.SecurityNote(Critical = "Uses critical type SafeHGlobalHandle.",
- Safe = "Performs a Demand for full trust.")]
- [SecuritySafeCritical]
- [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
- public static SafeHandle GetTokenInformation(SafeCloseHandle token, TOKEN_INFORMATION_CLASS infoClass)
- {
- uint length;
- if (!SafeNativeMethods.GetTokenInformation(token, infoClass, SafeHGlobalHandle.InvalidHandle, 0, out length))
- {
- int error = Marshal.GetLastWin32Error();
- if (error != (int)Win32Error.ERROR_INSUFFICIENT_BUFFER)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.GetTokenInfoFailed, error)));
- }
- }
- SafeHandle buffer = SafeHGlobalHandle.AllocHGlobal(length);
- try
- {
- if (!SafeNativeMethods.GetTokenInformation(token, infoClass, buffer, length, out length))
- {
- int error = Marshal.GetLastWin32Error();
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.GetTokenInfoFailed, error)));
- }
- }
- catch
- {
- buffer.Dispose();
- throw;
- }
- return buffer;
- }
- internal static bool IsAtleastImpersonationToken(SafeCloseHandle token)
- {
- using (SafeHandle buffer =
- GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenImpersonationLevel))
- {
- int level = Marshal.ReadInt32(buffer.DangerousGetHandle());
- if (level < (int)SecurityImpersonationLevel.Impersonation)
- return false;
- else
- return true;
- }
- }
- internal static bool IsPrimaryToken(SafeCloseHandle token)
- {
- using (SafeHandle buffer =
- GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenType))
- {
- int level = Marshal.ReadInt32(buffer.DangerousGetHandle());
- return (level == (int)TokenType.TokenPrimary);
- }
- }
- internal static LUID GetModifiedIDLUID(SafeCloseHandle token)
- {
- using (SafeHandle buffer =
- GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenStatistics))
- {
- TOKEN_STATISTICS tokenStats = (TOKEN_STATISTICS)
- Marshal.PtrToStructure(buffer.DangerousGetHandle(), typeof(TOKEN_STATISTICS));
- return tokenStats.ModifiedId;
- }
- }
- public static WindowsIdentity GetAnonymousIdentity()
- {
- SafeCloseHandle tokenHandle = null;
- bool isImpersonating = false;
- lock (lockObject)
- {
- if (anonymousIdentity == null)
- {
- try
- {
- try
- {
- if (!SafeNativeMethods.ImpersonateAnonymousUserOnCurrentThread(SafeNativeMethods.GetCurrentThread()))
- {
- int error = Marshal.GetLastWin32Error();
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.ImpersonateAnonymousTokenFailed, error)));
- }
- isImpersonating = true;
- bool revertSuccess;
- bool isSuccess = SafeNativeMethods.OpenCurrentThreadToken(SafeNativeMethods.GetCurrentThread(), TokenAccessLevels.Query, true, out tokenHandle);
- if (!isSuccess)
- {
- int error = Marshal.GetLastWin32Error();
- revertSuccess = SafeNativeMethods.RevertToSelf();
- if (false == revertSuccess)
- {
- error = Marshal.GetLastWin32Error();
- //this requires a failfast since failure to revert impersonation compromises security
- DiagnosticUtility.FailFast("RevertToSelf() failed with " + error);
- }
- isImpersonating = false;
- Utility.CloseInvalidOutSafeHandle(tokenHandle);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.OpenThreadTokenFailed, error)));
- }
- revertSuccess = SafeNativeMethods.RevertToSelf();
- if (false == revertSuccess)
- {
- int error = Marshal.GetLastWin32Error();
- //this requires a failfast since failure to revert impersonation compromises security
- DiagnosticUtility.FailFast("RevertToSelf() failed with " + error);
- }
- isImpersonating = false;
- using (tokenHandle)
- {
- anonymousIdentity = new WindowsIdentity(tokenHandle.DangerousGetHandle());
- }
- }
- finally
- {
- if (isImpersonating)
- {
- bool revertSuccess = SafeNativeMethods.RevertToSelf();
- if (false == revertSuccess)
- {
- int error = Marshal.GetLastWin32Error();
- //this requires a failfast since failure to revert impersonation compromises security
- DiagnosticUtility.FailFast("RevertToSelf() failed with " + error);
- }
- }
- }
- }
- catch
- {
- // Force the finally to run before leaving the method.
- throw;
- }
- }
- }
- return anonymousIdentity;
- }
- public static WindowsIdentity GetProcessIdentity()
- {
- SafeCloseHandle tokenHandle = null;
- lock (lockObject)
- {
- try
- {
- bool isSuccess = SafeNativeMethods.GetCurrentProcessToken(SafeNativeMethods.GetCurrentProcess(), TokenAccessLevels.Query, out tokenHandle);
- if (!isSuccess)
- {
- int error = Marshal.GetLastWin32Error();
- Utility.CloseInvalidOutSafeHandle(tokenHandle);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.OpenProcessTokenFailed, error)));
- }
- processIdentity = new WindowsIdentity(tokenHandle.DangerousGetHandle());
- }
- finally
- {
- if (tokenHandle != null)
- tokenHandle.Dispose();
- }
- }
- return processIdentity;
- }
- }
- internal sealed class ComPlusAuthorization
- {
- string[] serviceRoleMembers = null;
- string[] contractRoleMembers = null;
- string[] operationRoleMembers = null;
- CommonSecurityDescriptor securityDescriptor = null;
- static SecurityIdentifier sidAdministrators = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
- Dictionary<LUID, bool> accessCheckCache = new Dictionary<LUID, bool>();
- public ComPlusAuthorization(string[] serviceRoleMembers, string[] contractRoleMembers, string[] operationRoleMembers)
- {
- this.serviceRoleMembers = serviceRoleMembers;
- this.contractRoleMembers = contractRoleMembers;
- this.operationRoleMembers = operationRoleMembers;
- }
- private void BuildSecurityDescriptor()
- {
- Fx.Assert((null == securityDescriptor), "SecurityDescriptor must be NULL");
- NTAccount name;
- SecurityIdentifier sid;
- CommonAce ace;
- RawAcl acl = new RawAcl(GenericAcl.AclRevision, 1);
- int index = 0;
- if (operationRoleMembers != null)
- {
- foreach (string userName in operationRoleMembers)
- {
- name = new NTAccount(userName);
- sid = (SecurityIdentifier)name.Translate(typeof(SecurityIdentifier));
- ace = new CommonAce(AceFlags.None, AceQualifier.AccessAllowed, (int)ComRights.EXECUTE, sid, false, null);
- acl.InsertAce(index, ace);
- index++;
- }
- }
- if (contractRoleMembers != null)
- {
- foreach (string userName in contractRoleMembers)
- {
- name = new NTAccount(userName);
- sid = (SecurityIdentifier)name.Translate(typeof(SecurityIdentifier));
- ace = new CommonAce(AceFlags.None, AceQualifier.AccessAllowed, (int)ComRights.EXECUTE, sid, false, null);
- acl.InsertAce(index, ace);
- index++;
- }
- }
- if (serviceRoleMembers != null)
- {
- foreach (string userName in serviceRoleMembers)
- {
- name = new NTAccount(userName);
- sid = (SecurityIdentifier)name.Translate(typeof(SecurityIdentifier));
- ace = new CommonAce(AceFlags.None, AceQualifier.AccessAllowed, (int)ComRights.EXECUTE, sid, false, null);
- acl.InsertAce(index, ace);
- index++;
- }
- }
- DiscretionaryAcl dacl = new DiscretionaryAcl(true, false, acl);
- securityDescriptor = new CommonSecurityDescriptor(true, false, ControlFlags.DiscretionaryAclPresent, sidAdministrators, sidAdministrators, null, dacl);
- }
- private bool IsAccessCached(LUID luidModifiedID, out bool isAccessAllowed)
- {
- if (null == accessCheckCache)
- {
- throw Fx.AssertAndThrowFatal("AcessCheckCache must not be NULL");
- }
- bool retValue = false;
- lock (this)
- {
- retValue = accessCheckCache.TryGetValue(luidModifiedID, out isAccessAllowed);
- }
- return retValue;
- }
- private void CacheAccessCheck(LUID luidModifiedID, bool isAccessAllowed)
- {
- if (null == accessCheckCache)
- {
- throw Fx.AssertAndThrowFatal("AcessCheckCache must not be NULL");
- }
- lock (this)
- {
- accessCheckCache[luidModifiedID] = isAccessAllowed;
- }
- }
- private void CheckAccess(WindowsIdentity clientIdentity, out bool IsAccessAllowed)
- {
- if (null == securityDescriptor)
- {
- throw Fx.AssertAndThrowFatal("Security Descriptor must not be NULL");
- }
- IsAccessAllowed = false;
- byte[] BinaryForm = new byte[securityDescriptor.BinaryLength];
- securityDescriptor.GetBinaryForm(BinaryForm, 0);
- SafeCloseHandle ImpersonationToken = null;
- SafeCloseHandle clientIdentityToken = new SafeCloseHandle(clientIdentity.Token, false);
- try
- {
- if (SecurityUtils.IsPrimaryToken(clientIdentityToken))
- {
- if (!SafeNativeMethods.DuplicateTokenEx(clientIdentityToken,
- TokenAccessLevels.Query,
- IntPtr.Zero,
- SecurityImpersonationLevel.Identification,
- TokenType.TokenImpersonation,
- out ImpersonationToken))
- {
- int error = Marshal.GetLastWin32Error();
- Utility.CloseInvalidOutSafeHandle(ImpersonationToken);
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.DuplicateTokenExFailed, error)));
- }
- }
- GENERIC_MAPPING GenericMapping = new GENERIC_MAPPING();
- PRIVILEGE_SET PrivilegeSet = new PRIVILEGE_SET();
- uint PrivilegeSetLength = (uint)Marshal.SizeOf(PrivilegeSet);
- uint GrantedAccess = 0;
- if (!SafeNativeMethods.AccessCheck(BinaryForm, (ImpersonationToken != null) ? ImpersonationToken : clientIdentityToken,
- (int)ComRights.EXECUTE, GenericMapping, out PrivilegeSet,
- ref PrivilegeSetLength, out GrantedAccess, out IsAccessAllowed))
- {
- int error = Marshal.GetLastWin32Error();
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new Win32Exception(error, SR.GetString(SR.AccessCheckFailed, error)));
- }
- }
- finally
- {
- if (ImpersonationToken != null)
- ImpersonationToken.Dispose();
- }
- }
- public string[] ServiceRoleMembers
- {
- get
- {
- return serviceRoleMembers;
- }
- }
- public string[] ContractRoleMembers
- {
- get
- {
- return contractRoleMembers;
- }
- }
- public string[] OperationRoleMembers
- {
- get
- {
- return operationRoleMembers;
- }
- }
- public CommonSecurityDescriptor SecurityDescriptor
- {
- get
- {
- return securityDescriptor;
- }
- }
- public bool IsAuthorizedForOperation(WindowsIdentity clientIdentity)
- {
- bool IsAccessAllowed = false;
- if (null == clientIdentity)
- {
- throw Fx.AssertAndThrow("NULL Identity");
- }
- if (IntPtr.Zero == clientIdentity.Token)
- {
- throw Fx.AssertAndThrow("Token handle cannot be zero");
- }
- lock (this)
- {
- if (securityDescriptor == null)
- {
- BuildSecurityDescriptor();
- }
- }
- LUID luidModified = SecurityUtils.GetModifiedIDLUID(new SafeCloseHandle(clientIdentity.Token, false));
- if (IsAccessCached(luidModified, out IsAccessAllowed))
- return IsAccessAllowed;
- CheckAccess(clientIdentity, out IsAccessAllowed);
- CacheAccessCheck(luidModified, IsAccessAllowed);
- return IsAccessAllowed;
- }
- }
- internal sealed class ComPlusServerSecurity : IContextSecurityPerimeter, IServerSecurity, IDisposable
- {
- WindowsIdentity clientIdentity = null;
- IntPtr oldSecurityObject = IntPtr.Zero;
- WindowsImpersonationContext impersonateContext = null;
- bool isImpersonating = false;
- bool shouldUseCallContext = false;
- const uint RPC_C_AUTHN_GSS_NEGOTIATE = 9;
- const uint RPC_C_AUTHN_WINNT = 10;
- const uint RPC_C_AUTHN_GSS_KERBEROS = 16;
- const uint RPC_C_AUTHN_DEFAULT = unchecked((uint)0xFFFFFFFF);
- const uint RPC_C_AUTHZ_NONE = 0;
- const uint RPC_C_AUTHN_LEVEL_DEFAULT = 0;
- const uint RPC_C_AUTHN_LEVEL_NONE = 1;
- const uint RPC_C_AUTHN_LEVEL_CONNECT = 2;
- const uint RPC_C_AUTHN_LEVEL_CALL = 3;
- const uint RPC_C_AUTHN_LEVEL_PKT = 4;
- const uint RPC_C_AUTHN_LEVEL_PKT_INTEGRITY = 5;
- const uint RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6;
- public ComPlusServerSecurity(WindowsIdentity clientIdentity, bool shouldUseCallContext)
- {
- if (null == clientIdentity)
- {
- throw Fx.AssertAndThrow("NULL Identity");
- }
- if (IntPtr.Zero == clientIdentity.Token)
- {
- throw Fx.AssertAndThrow("Token handle cannot be zero");
- }
- this.shouldUseCallContext = shouldUseCallContext;
- this.clientIdentity = clientIdentity;
- IntPtr secCtx = Marshal.GetIUnknownForObject(this);
- try
- {
- oldSecurityObject = SafeNativeMethods.CoSwitchCallContext(secCtx);
- }
- catch
- {
- Marshal.Release(secCtx);
- throw;
- }
- }
- ~ComPlusServerSecurity()
- {
- Dispose(false);
- }
- public bool GetPerimeterFlag()
- {
- return shouldUseCallContext;
- }
- public void SetPerimeterFlag(bool flag)
- {
- shouldUseCallContext = flag;
- }
- public void QueryBlanket
- (
- IntPtr authnSvc,
- IntPtr authzSvc,
- IntPtr serverPrincipalName,
- IntPtr authnLevel,
- IntPtr impLevel,
- IntPtr clientPrincipalName,
- IntPtr Capabilities
- )
- {
- // Convert to RPC'isms.
- if (authnSvc != IntPtr.Zero)
- {
- uint tempAuthnSvc = RPC_C_AUTHN_DEFAULT;
- // Try to convert the clientIdentity.AuthenticationType to an RPC constant.
- // This is a best case attempt.
- string authenticationType = clientIdentity.AuthenticationType;
- if (authenticationType.ToUpperInvariant() == "NTLM")
- tempAuthnSvc = RPC_C_AUTHN_WINNT;
- else if (authenticationType.ToUpperInvariant() == "KERBEROS")
- tempAuthnSvc = RPC_C_AUTHN_GSS_KERBEROS;
- else if (authenticationType.ToUpperInvariant() == "NEGOTIATE")
- tempAuthnSvc = RPC_C_AUTHN_GSS_NEGOTIATE;
- Marshal.WriteInt32(authnSvc, (int)tempAuthnSvc);
- }
- if (authzSvc != IntPtr.Zero)
- {
- Marshal.WriteInt32(authzSvc, (int)RPC_C_AUTHZ_NONE);
- }
- if (serverPrincipalName != IntPtr.Zero)
- {
- IntPtr str = Marshal.StringToCoTaskMemUni(SecurityUtils.GetProcessIdentity().Name);
- Marshal.WriteIntPtr(serverPrincipalName, str);
- }
- // There is no equivalent for the RPC authn level. It can only be
- // approximated, in the best case. Use default.
- if (authnLevel != IntPtr.Zero)
- {
- Marshal.WriteInt32(authnLevel, (int)RPC_C_AUTHN_LEVEL_DEFAULT);
- }
- if (impLevel != IntPtr.Zero)
- {
- Marshal.WriteInt32(impLevel, 0);
- }
- if (clientPrincipalName != IntPtr.Zero)
- {
- IntPtr str = Marshal.StringToCoTaskMemUni(clientIdentity.Name);
- Marshal.WriteIntPtr(clientPrincipalName, str);
- }
- if (Capabilities != IntPtr.Zero)
- {
- Marshal.WriteInt32(Capabilities, 0);
- }
- }
- public int ImpersonateClient()
- {
- // We want to return known COM hresults here rather than random CLR-Exception mapped HRESULTS. Also,
- // we don't want CLR to set the ErrorInfo object.
- int hresult = HR.E_FAIL;
- try
- {
- impersonateContext = WindowsIdentity.Impersonate(clientIdentity.Token);
- isImpersonating = true;
- hresult = HR.S_OK;
- }
- catch (SecurityException)
- {
- // Special case anonymous impersonation failure.
- // Unmanaged callers to ImpersonateClient expect this hresult.
- hresult = HR.RPC_NT_BINDING_HAS_NO_AUTH;
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e))
- throw;
- }
- return hresult;
- }
- public int RevertToSelf()
- {
- // We want to return known COM hresults here rather than random CLR-Exception mapped HRESULTS. Also,
- // we don't want CLR to set the ErrorInfo object.
- int hresult = HR.E_FAIL;
- if (isImpersonating)
- {
- try
- {
- impersonateContext.Undo();
- isImpersonating = false;
- hresult = HR.S_OK;
- }
- catch (Exception e)
- {
- if (Fx.IsFatal(e))
- throw;
- }
- }
- return hresult;
- }
- public bool IsImpersonating()
- {
- return isImpersonating;
- }
- void IDisposable.Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- public void Dispose(bool disposing)
- {
- RevertToSelf();
- IntPtr secCtx = SafeNativeMethods.CoSwitchCallContext(oldSecurityObject);
- if (IntPtr.Zero == secCtx)
- {
- // this has to be a failfast since not having a security context can compromise security
- DiagnosticUtility.FailFast("Security Context was should not be null");
- }
- if (Marshal.GetObjectForIUnknown(secCtx) != this)
- {
- // this has to be a failfast since being in the wrong security context can compromise security
- DiagnosticUtility.FailFast("Security Context was modified from underneath us");
- }
- Marshal.Release(secCtx);
- if (disposing)
- {
- clientIdentity = null;
- if (impersonateContext != null)
- impersonateContext.Dispose();
- }
- }
- }
- }