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