diff --git a/NSspi/Contexts/ServerContext.cs b/NSspi/Contexts/ServerContext.cs index 1b3b3b9..b3a2263 100644 --- a/NSspi/Contexts/ServerContext.cs +++ b/NSspi/Contexts/ServerContext.cs @@ -22,8 +22,18 @@ namespace NSspi.Contexts this.finalAttribs = ContextAttrib.Zero; this.impersonating = false; + + this.SupportsImpersonate = this.Credential.PackageInfo.Capabilities.HasFlag( SecPkgCapability.Impersonation ); } + /// + /// Whether or not the server can impersonate an authenticated client. + /// + /// + /// This depends on the security package that was used to create the server and client's credentials. + /// + public bool SupportsImpersonate { get; private set; } + public SecurityStatus AcceptToken( byte[] clientToken, out byte[] nextToken ) { SecureBuffer clientBuffer = new SecureBuffer( clientToken, BufferType.Token ); @@ -93,8 +103,6 @@ namespace NSspi.Contexts } this.Expiry = rawExpiry.ToDateTime(); - - InitProviderCapabilities(); } else if ( status == SecurityStatus.ContinueNeeded ) { @@ -113,7 +121,7 @@ namespace NSspi.Contexts public ImpersonationHandle ImpersonateClient() { - ImpersonationHandle handle = new ImpersonationHandle( this ); + ImpersonationHandle handle; SecurityStatus status = SecurityStatus.InternalError; bool gotRef = false; @@ -125,7 +133,14 @@ namespace NSspi.Contexts { throw new InvalidOperationException( "Cannot impersonate again while already impersonating." ); } + else if( this.SupportsImpersonate == false ) + { + throw new InvalidOperationException( + "The ServerContext is using a security package that does not support impersonation." + ); + } + handle = new ImpersonationHandle( this ); RuntimeHelpers.PrepareConstrainedRegions(); try { @@ -211,8 +226,22 @@ namespace NSspi.Contexts } } - private void InitProviderCapabilities() + protected override void Dispose( bool disposing ) { + // We were disposed while impersonating. This means that the consumer that is currently holding + // the impersonation handle allowed the context to be disposed or finalized while an impersonation handle + // was held. We have to revert impersonation to restore the thread's behavior, since once the context + // goes away, there's nothing left. + // + // When and if the impersonation handle is diposed/finalized, it'll see that the context has already been + // disposed, will assume that we already reverted, and so will do nothing. + + if( this.impersonating ) + { + RevertImpersonate(); + } + + base.Dispose( disposing ); } } } diff --git a/NSspi/Credentials/Credential.cs b/NSspi/Credentials/Credential.cs index 5976d42..b890940 100644 --- a/NSspi/Credentials/Credential.cs +++ b/NSspi/Credentials/Credential.cs @@ -28,6 +28,8 @@ namespace NSspi.Credentials this.securityPackage = package; this.expiry = DateTime.MinValue; + + this.PackageInfo = PackageSupport.GetPackageCapabilities( this.SecurityPackage ); } ~Credential() @@ -35,6 +37,8 @@ namespace NSspi.Credentials Dispose( false ); } + public SecPkgInfo PackageInfo { get; private set; } + public string SecurityPackage { get