diff --git a/Contexts/ClientContext.cs b/Contexts/ClientContext.cs index c4270c2..437052d 100644 --- a/Contexts/ClientContext.cs +++ b/Contexts/ClientContext.cs @@ -25,8 +25,6 @@ namespace NSspi.Contexts public SecurityStatus Init( byte[] serverToken, out byte[] outToken ) { - long credHandle = base.Credential.CredentialHandle; - long prevContextHandle = base.ContextHandle; long newContextHandle = 0; @@ -62,7 +60,7 @@ namespace NSspi.Contexts if ( prevContextHandle == 0 ) { status = ContextNativeMethods.InitializeSecurityContext_1( - ref credHandle, + ref this.Credential.Handle.rawHandle, IntPtr.Zero, this.serverPrinc, this.requestedAttribs, @@ -81,7 +79,7 @@ namespace NSspi.Contexts using ( serverAdapter = new SecureBufferAdapter( serverBuffer ) ) { status = ContextNativeMethods.InitializeSecurityContext_2( - ref credHandle, + ref this.Credential.Handle.rawHandle, ref prevContextHandle, this.serverPrinc, this.requestedAttribs, diff --git a/Contexts/ContextNativeMethods.cs b/Contexts/ContextNativeMethods.cs index bf9bb93..9b5b86d 100644 --- a/Contexts/ContextNativeMethods.cs +++ b/Contexts/ContextNativeMethods.cs @@ -31,7 +31,7 @@ namespace NSspi.Contexts SetLastError = true )] public static extern SecurityStatus AcceptSecurityContext_1( - ref long credHandle, + ref RawSspiHandle credHandle, IntPtr oldContextHandle, IntPtr inputBuffer, ContextAttrib requestedAttribs, @@ -51,7 +51,7 @@ namespace NSspi.Contexts SetLastError = true )] public static extern SecurityStatus AcceptSecurityContext_2( - ref long credHandle, + ref RawSspiHandle credHandle, ref long oldContextHandle, IntPtr inputBuffer, ContextAttrib requestedAttribs, @@ -104,7 +104,7 @@ namespace NSspi.Contexts SetLastError = true )] public static extern SecurityStatus InitializeSecurityContext_1( - ref long credentialHandle, + ref RawSspiHandle credentialHandle, IntPtr zero, string serverPrincipleName, ContextAttrib requiredAttribs, @@ -126,7 +126,7 @@ namespace NSspi.Contexts SetLastError = true )] public static extern SecurityStatus InitializeSecurityContext_2( - ref long credentialHandle, + ref RawSspiHandle credentialHandle, ref long previousHandle, string serverPrincipleName, ContextAttrib requiredAttribs, diff --git a/Contexts/ServerContext.cs b/Contexts/ServerContext.cs index e3108c9..3cf0847 100644 --- a/Contexts/ServerContext.cs +++ b/Contexts/ServerContext.cs @@ -24,7 +24,6 @@ namespace NSspi.Contexts SecureBuffer clientBuffer = new SecureBuffer( clientToken, BufferType.Token ); SecureBuffer outBuffer = new SecureBuffer( new byte[12288], BufferType.Token ); - long credHandle = this.Credential.CredentialHandle; long oldContextHandle = base.ContextHandle; long newContextHandle = 0; @@ -42,7 +41,7 @@ namespace NSspi.Contexts if ( oldContextHandle == 0 ) { status = ContextNativeMethods.AcceptSecurityContext_1( - ref credHandle, + ref this.Credential.Handle.rawHandle, IntPtr.Zero, clientAdapter.Handle, requestedAttribs, @@ -56,7 +55,7 @@ namespace NSspi.Contexts else { status = ContextNativeMethods.AcceptSecurityContext_2( - ref credHandle, + ref this.Credential.Handle.rawHandle, ref oldContextHandle, clientAdapter.Handle, requestedAttribs, diff --git a/Credentials/Credential.cs b/Credentials/Credential.cs index eb3e042..67f847b 100644 --- a/Credentials/Credential.cs +++ b/Credentials/Credential.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.DirectoryServices.AccountManagement; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; @@ -14,7 +15,7 @@ namespace NSspi private SecurityPackage securityPackage; - private long credHandle; + private SafeCredentialHandle safeCredHandle; private long expiry; public Credential(SecurityPackage package, CredentialType credentialType) @@ -22,7 +23,6 @@ namespace NSspi this.disposed = false; this.securityPackage = package; - this.credHandle = 0; this.expiry = 0; Init( package, credentialType ); @@ -66,17 +66,30 @@ namespace NSspi } // -- Invoke -- - SecurityStatus status = NativeMethods.AcquireCredentialsHandle( - null, - packageName, - use, - IntPtr.Zero, - IntPtr.Zero, - IntPtr.Zero, - IntPtr.Zero, - ref this.credHandle, - ref this.expiry - ); + + SecurityStatus status = SecurityStatus.InternalError; + + this.safeCredHandle = new SafeCredentialHandle(); + + // The finally clause is the actual constrained region. The VM pre-allocates any stack space, + // performs any allocations it needs to prepare methods for execution, and postpones any + // instances of the 'uncatchable' exceptions (ThreadAbort, StackOverflow, OutOfMemory). + RuntimeHelpers.PrepareConstrainedRegions(); + try { } + finally + { + status = NativeMethods.AcquireCredentialsHandle( + null, + packageName, + use, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero, + IntPtr.Zero, + ref this.safeCredHandle.rawHandle, + ref this.expiry + ); + } if ( status != SecurityStatus.OK ) { @@ -112,7 +125,7 @@ namespace NSspi string name = null; status = NativeMethods.QueryCredentialsAttribute_Name( - ref this.credHandle, + ref this.safeCredHandle.rawHandle, CredentialQueryAttrib.Names, ref carrier ); @@ -131,11 +144,11 @@ namespace NSspi } } - public long CredentialHandle + public SafeCredentialHandle Handle { get { - return this.credHandle; + return this.safeCredHandle; } } @@ -149,16 +162,12 @@ namespace NSspi { if ( this.disposed == false ) { - SecurityStatus result; - - result = NativeMethods.FreeCredentialsHandle( ref this.credHandle ); + if ( disposing ) + { + this.safeCredHandle.Dispose(); + } this.disposed = true; - - if ( disposing && result != SecurityStatus.OK ) - { - throw new SSPIException( "Failed to release credentials handle", result ); - } } } } diff --git a/NSspi.csproj b/NSspi.csproj index a089124..ffb953d 100644 --- a/NSspi.csproj +++ b/NSspi.csproj @@ -72,6 +72,7 @@ + diff --git a/NativeMethods.cs b/NativeMethods.cs index 64b7f4e..438bb21 100644 --- a/NativeMethods.cs +++ b/NativeMethods.cs @@ -58,7 +58,7 @@ namespace NSspi IntPtr packageData, IntPtr getKeyFunc, IntPtr getKeyData, - ref long credentialHandle, + ref RawSspiHandle credentialHandle, ref long expiry ); @@ -75,7 +75,7 @@ namespace NSspi SetLastError = true )] public static extern SecurityStatus FreeCredentialsHandle( - ref long credentialHandle + ref RawSspiHandle credentialHandle ); /* @@ -118,7 +118,7 @@ namespace NSspi SetLastError = true )] public static extern SecurityStatus QueryCredentialsAttribute_Name( - ref long credentialHandle, + ref RawSspiHandle credentialHandle, CredentialQueryAttrib attributeName, ref QueryNameAttribCarrier name ); diff --git a/SspiHandle.cs b/SspiHandle.cs new file mode 100644 index 0000000..ed5601a --- /dev/null +++ b/SspiHandle.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace NSspi +{ + /// + /// Represents any SSPI handle created for credential handles, context handles, and security package + /// handles. Any SSPI handle is always the size of two native pointers. + /// + /// + /// The documentation for SSPI handles can be found here: + /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380495(v=vs.85).aspx + /// + /// This class is not reference safe - if used directly, or referenced directly, it may be leaked, + /// or subject to finalizer races, or any of the hundred of things SafeHandles were designed to fix. + /// Do not directly use this class - use only though SafeHandle wrapper objects. Any reference needed + /// to this handle for performing work (InitializeSecurityContext, eg), should be done through + /// a second class SafeSspiHandleReference so that reference counting is properly executed. + /// + [StructLayout( LayoutKind.Sequential, Pack = 1 ) ] + public struct RawSspiHandle + { + private IntPtr lowPart; + private IntPtr highPart; + + public bool IsZero() + { + return this.lowPart == IntPtr.Zero && this.highPart == IntPtr.Zero; + } + + // This guy has to be executed in a CER. + [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success)] + public void SetInvalid() + { + this.lowPart = IntPtr.Zero; + this.highPart = IntPtr.Zero; + } + } + + + public abstract class SafeSspiHandle : SafeHandle + { + internal RawSspiHandle rawHandle; + + protected SafeSspiHandle() + : base( IntPtr.Zero, true ) + { + this.rawHandle = new RawSspiHandle(); + } + + public override bool IsInvalid + { + get { return IsClosed || this.rawHandle.IsZero(); } + } + } + + public class SafeCredentialHandle : SafeSspiHandle + { + public SafeCredentialHandle() + : base() + { } + + protected override bool ReleaseHandle() + { + SecurityStatus status = NativeMethods.FreeCredentialsHandle( + ref base.rawHandle + ); + + return status == SecurityStatus.OK; + } + } + +}