14 Commits
0.1.3 ... 0.2.0

Author SHA1 Message Date
Kevin Thompson
4d79f1b5db Announced v0.2.0 release 2017-10-20 01:46:33 -04:00
Kevin Thompson
aef74fe8aa Default to VS2015. 2017-10-20 01:25:49 -04:00
Kevin Thompson
37a7881d25 Updated version declarations to v0.2.0, to reflect the new support for username/password authentication plus the minor incompatibility introduced with the updated design. 2017-10-20 01:25:36 -04:00
Kevin Thompson
9350412fb6 Updated the svn-ignore properties. 2017-09-24 02:41:00 -04:00
Kevin Thompson
6a5e6056a7 Added password authentication support.
Added the ability to acquire a valid credential by providing a username/password combination.
2017-09-24 02:39:49 -04:00
Kevin Thompson
c6c0ac623e Code cleanup.
Fixed comment and code style; removed unnecessary initialization of variable.
2017-09-24 02:10:00 -04:00
Kevin Thompson
c07abdaa62 Comments. 2017-09-24 02:02:10 -04:00
Kevin Thompson
f2d4541bab Renamed Client/ServerCredential.
The credential implementations should indicate in their name that they are implementations of the 'current' credential (acquired from the login running the process).
2017-09-24 01:59:34 -04:00
Kevin Thompson
0eb8ff49f2 Fixed ClientContext and ServerContext constructors.
Changed the contexts to take in any Credential implementation, instead of requiring client- or server-specific credentials.
2017-09-24 01:56:36 -04:00
Kevin Thompson
3b189d2865 Code cleanup
Fixed code style using CodeMaid.
2017-09-24 01:52:45 -04:00
Kevin Thompson
6ff68d8812 Merge pull request #8 from mcgov/master
Adding QuerySessionKey, as well as QueryContextAttributes and SafeQue…
2017-09-23 15:00:00 -04:00
Matthew McGovern
e631d77f01 Adding QuerySessionKey, as well as QueryContextAttributes and SafeQueryContextAttributes 2017-09-22 09:21:48 -07:00
Kevin Thompson
4e629a71b2 Changed the csproj to output the documentation xml file 2017-05-01 10:10:32 -04:00
Kevin Thompson
8db4b6d8a5 Documented release 0.1.3 in the readme 2017-05-01 10:06:00 -04:00
58 changed files with 707 additions and 648 deletions

View File

@@ -1,6 +1,8 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012 # Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClient", "TestClient\TestClient.csproj", "{E93FBF1A-5198-44D6-BDF0-880D17F2B81A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestClient", "TestClient\TestClient.csproj", "{E93FBF1A-5198-44D6-BDF0-880D17F2B81A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProtocol", "TestProtocol\TestProtocol.csproj", "{9BFD94E1-D9FB-44D7-A6E7-8BAC2620E535}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProtocol", "TestProtocol\TestProtocol.csproj", "{9BFD94E1-D9FB-44D7-A6E7-8BAC2620E535}"

View File

@@ -1,9 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi namespace NSspi
{ {
@@ -13,7 +8,7 @@ namespace NSspi
public static class ByteWriter public static class ByteWriter
{ {
// Big endian: Most significant byte at lowest address in memory. // Big endian: Most significant byte at lowest address in memory.
/// <summary> /// <summary>
/// Writes a 2-byte signed integer to the buffer in big-endian format. /// Writes a 2-byte signed integer to the buffer in big-endian format.
/// </summary> /// </summary>
@@ -37,8 +32,7 @@ namespace NSspi
buffer[position + 0] = (byte)( value >> 24 ); buffer[position + 0] = (byte)( value >> 24 );
buffer[position + 1] = (byte)( value >> 16 ); buffer[position + 1] = (byte)( value >> 16 );
buffer[position + 2] = (byte)( value >> 8 ); buffer[position + 2] = (byte)( value >> 8 );
buffer[position + 3] = (byte)( value); buffer[position + 3] = (byte)( value );
} }
/// <summary> /// <summary>
@@ -77,4 +71,4 @@ namespace NSspi
return value; return value;
} }
} }
} }

View File

@@ -1,9 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using NSspi.Buffers; using NSspi.Buffers;
using NSspi.Credentials; using NSspi.Credentials;
@@ -14,7 +9,7 @@ namespace NSspi.Contexts
/// with the server and to encrypt, decrypt, sign and verify messages to and from the server. /// with the server and to encrypt, decrypt, sign and verify messages to and from the server.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// A client and server establish a shared security context by exchanging authentication tokens. Once /// A client and server establish a shared security context by exchanging authentication tokens. Once
/// the shared context is established, the client and server can pass messages to each other, encrypted, /// the shared context is established, the client and server can pass messages to each other, encrypted,
/// signed, etc, using the established parameters of the shared context. /// signed, etc, using the established parameters of the shared context.
/// </remarks> /// </remarks>
@@ -33,7 +28,7 @@ namespace NSspi.Contexts
/// <param name="requestedAttribs">Requested attributes that describe the desired properties of the /// <param name="requestedAttribs">Requested attributes that describe the desired properties of the
/// context once it is established. If a context cannot be established that satisfies the indicated /// context once it is established. If a context cannot be established that satisfies the indicated
/// properties, the context initialization is aborted.</param> /// properties, the context initialization is aborted.</param>
public ClientContext( ClientCredential cred, string serverPrinc, ContextAttrib requestedAttribs ) public ClientContext( Credential cred, string serverPrinc, ContextAttrib requestedAttribs )
: base( cred ) : base( cred )
{ {
this.serverPrinc = serverPrinc; this.serverPrinc = serverPrinc;
@@ -47,11 +42,11 @@ namespace NSspi.Contexts
/// This method is performed iteratively to start, continue, and end the authentication cycle with the /// This method is performed iteratively to start, continue, and end the authentication cycle with the
/// server. Each stage works by acquiring a token from one side, presenting it to the other side /// server. Each stage works by acquiring a token from one side, presenting it to the other side
/// which in turn may generate a new token. /// which in turn may generate a new token.
/// ///
/// The cycle typically starts and ends with the client. On the first invocation on the client, /// The cycle typically starts and ends with the client. On the first invocation on the client,
/// no server token exists, and null is provided in its place. The client returns its status, providing /// no server token exists, and null is provided in its place. The client returns its status, providing
/// its output token for the server. The server accepts the clients token as input and provides a /// its output token for the server. The server accepts the clients token as input and provides a
/// token as output to send back to the client. This cycle continues until the server and client /// token as output to send back to the client. This cycle continues until the server and client
/// both indicate, typically, a SecurityStatus of 'OK'. /// both indicate, typically, a SecurityStatus of 'OK'.
/// </remarks> /// </remarks>
/// <param name="serverToken">The most recently received token from the server, or null if beginning /// <param name="serverToken">The most recently received token from the server, or null if beginning
@@ -60,7 +55,7 @@ namespace NSspi.Contexts
/// <returns>A status message indicating the progression of the authentication cycle. /// <returns>A status message indicating the progression of the authentication cycle.
/// A status of 'OK' indicates that the cycle is complete, from the client's perspective. If the outToken /// A status of 'OK' indicates that the cycle is complete, from the client's perspective. If the outToken
/// is not null, it must be sent to the server. /// is not null, it must be sent to the server.
/// A status of 'Continue' indicates that the output token should be sent to the server and /// A status of 'Continue' indicates that the output token should be sent to the server and
/// a response should be anticipated.</returns> /// a response should be anticipated.</returns>
public SecurityStatus Init( byte[] serverToken, out byte[] outToken ) public SecurityStatus Init( byte[] serverToken, out byte[] outToken )
{ {
@@ -73,7 +68,7 @@ namespace NSspi.Contexts
SecureBuffer serverBuffer; SecureBuffer serverBuffer;
SecureBufferAdapter serverAdapter; SecureBufferAdapter serverAdapter;
if( this.Disposed ) if( this.Disposed )
{ {
throw new ObjectDisposedException( "ClientContext" ); throw new ObjectDisposedException( "ClientContext" );
@@ -86,24 +81,24 @@ namespace NSspi.Contexts
{ {
throw new InvalidOperationException( "Must provide the server's response when continuing the init process." ); throw new InvalidOperationException( "Must provide the server's response when continuing the init process." );
} }
// The security package tells us how big its biggest token will be. We'll allocate a buffer // The security package tells us how big its biggest token will be. We'll allocate a buffer
// that size, and it'll tell us how much it used. // that size, and it'll tell us how much it used.
outTokenBuffer = new SecureBuffer( outTokenBuffer = new SecureBuffer(
new byte[ this.Credential.PackageInfo.MaxTokenLength ], new byte[this.Credential.PackageInfo.MaxTokenLength],
BufferType.Token BufferType.Token
); );
serverBuffer = null; serverBuffer = null;
if ( serverToken != null ) if( serverToken != null )
{ {
serverBuffer = new SecureBuffer( serverToken, BufferType.Token ); serverBuffer = new SecureBuffer( serverToken, BufferType.Token );
} }
// Some notes on handles and invoking InitializeSecurityContext // Some notes on handles and invoking InitializeSecurityContext
// - The first time around, the phContext parameter (the 'old' handle) is a null pointer to what // - The first time around, the phContext parameter (the 'old' handle) is a null pointer to what
// would be an RawSspiHandle, to indicate this is the first time it's being called. // would be an RawSspiHandle, to indicate this is the first time it's being called.
// The phNewContext is a pointer (reference) to an RawSspiHandle struct of where to write the // The phNewContext is a pointer (reference) to an RawSspiHandle struct of where to write the
// new handle's values. // new handle's values.
// - The next time you invoke ISC, it takes a pointer to the handle it gave you last time in phContext, // - The next time you invoke ISC, it takes a pointer to the handle it gave you last time in phContext,
// and takes a pointer to where it should write the new handle's values in phNewContext. // and takes a pointer to where it should write the new handle's values in phNewContext.
@@ -111,13 +106,13 @@ namespace NSspi.Contexts
// "On the second call, phNewContext can be the same as the handle specified in the phContext // "On the second call, phNewContext can be the same as the handle specified in the phContext
// parameter." // parameter."
// It will overwrite the handle you gave it with the new handle value. // It will overwrite the handle you gave it with the new handle value.
// - All handle structures themselves are actually *two* pointer variables, eg, 64 bits on 32-bit // - All handle structures themselves are actually *two* pointer variables, eg, 64 bits on 32-bit
// Windows, 128 bits on 64-bit Windows. // Windows, 128 bits on 64-bit Windows.
// - So in the end, on a 64-bit machine, we're passing a 64-bit value (the pointer to the struct) that // - So in the end, on a 64-bit machine, we're passing a 64-bit value (the pointer to the struct) that
// points to 128 bits of memory (the struct itself) for where to write the handle numbers. // points to 128 bits of memory (the struct itself) for where to write the handle numbers.
using ( outAdapter = new SecureBufferAdapter( outTokenBuffer ) ) using( outAdapter = new SecureBufferAdapter( outTokenBuffer ) )
{ {
if ( this.ContextHandle.IsInvalid ) if( this.ContextHandle.IsInvalid )
{ {
status = ContextNativeMethods.InitializeSecurityContext_1( status = ContextNativeMethods.InitializeSecurityContext_1(
ref this.Credential.Handle.rawHandle, ref this.Credential.Handle.rawHandle,
@@ -136,7 +131,7 @@ namespace NSspi.Contexts
} }
else else
{ {
using ( serverAdapter = new SecureBufferAdapter( serverBuffer ) ) using( serverAdapter = new SecureBufferAdapter( serverBuffer ) )
{ {
status = ContextNativeMethods.InitializeSecurityContext_2( status = ContextNativeMethods.InitializeSecurityContext_2(
ref this.Credential.Handle.rawHandle, ref this.Credential.Handle.rawHandle,
@@ -179,4 +174,4 @@ namespace NSspi.Contexts
return status; return status;
} }
} }
} }

View File

@@ -1,23 +1,18 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using NSspi.Buffers; using NSspi.Buffers;
using NSspi.Contexts;
using NSspi.Credentials; using NSspi.Credentials;
namespace NSspi.Contexts namespace NSspi.Contexts
{ {
/// <summary> /// <summary>
/// Represents a security context and provides common functionality required for all security /// Represents a security context and provides common functionality required for all security
/// contexts. /// contexts.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This class is abstract and has a protected constructor and Initialize method. The exact /// This class is abstract and has a protected constructor and Initialize method. The exact
/// initialization implementation is provided by a subclasses, which may perform initialization /// initialization implementation is provided by a subclasses, which may perform initialization
/// in a variety of manners. /// in a variety of manners.
/// </remarks> /// </remarks>
public abstract class Context : IDisposable public abstract class Context : IDisposable
@@ -200,7 +195,7 @@ namespace NSspi.Contexts
Array.Copy( paddingBuffer.Buffer, 0, result, position, paddingBuffer.Length ); Array.Copy( paddingBuffer.Buffer, 0, result, position, paddingBuffer.Length );
position += paddingBuffer.Length; position += paddingBuffer.Length;
return result; return result;
} }
@@ -257,7 +252,7 @@ namespace NSspi.Contexts
paddingLength = ByteWriter.ReadInt16_BE( input, position ); paddingLength = ByteWriter.ReadInt16_BE( input, position );
position += 2; position += 2;
if ( trailerLength + dataLength + paddingLength + 2 + 4 + 2 > input.Length ) if( trailerLength + dataLength + paddingLength + 2 + 4 + 2 > input.Length )
{ {
throw new ArgumentException( "The buffer contains invalid data - the embedded length data does not add up." ); throw new ArgumentException( "The buffer contains invalid data - the embedded length data does not add up." );
} }
@@ -278,7 +273,7 @@ namespace NSspi.Contexts
{ {
throw new ArgumentException( "Input is missing data - it is not long enough to contain a fully encrypted message" ); throw new ArgumentException( "Input is missing data - it is not long enough to contain a fully encrypted message" );
} }
if( dataBuffer.Length <= remaining ) if( dataBuffer.Length <= remaining )
{ {
Array.Copy( input, position, dataBuffer.Buffer, 0, dataBuffer.Length ); Array.Copy( input, position, dataBuffer.Buffer, 0, dataBuffer.Length );
@@ -295,9 +290,8 @@ namespace NSspi.Contexts
Array.Copy( input, position, paddingBuffer.Buffer, 0, paddingBuffer.Length ); Array.Copy( input, position, paddingBuffer.Buffer, 0, paddingBuffer.Length );
} }
// else there was no padding. // else there was no padding.
using( adapter = new SecureBufferAdapter( new [] { trailerBuffer, dataBuffer, paddingBuffer } ) ) using( adapter = new SecureBufferAdapter( new[] { trailerBuffer, dataBuffer, paddingBuffer } ) )
{ {
status = ContextNativeMethods.SafeDecryptMessage( status = ContextNativeMethods.SafeDecryptMessage(
this.ContextHandle, this.ContextHandle,
@@ -348,7 +342,7 @@ namespace NSspi.Contexts
Array.Copy( message, dataBuffer.Buffer, message.Length ); Array.Copy( message, dataBuffer.Buffer, message.Length );
using ( adapter = new SecureBufferAdapter( new[] { dataBuffer, signatureBuffer } ) ) using( adapter = new SecureBufferAdapter( new[] { dataBuffer, signatureBuffer } ) )
{ {
status = ContextNativeMethods.SafeMakeSignature( status = ContextNativeMethods.SafeMakeSignature(
this.ContextHandle, this.ContextHandle,
@@ -358,7 +352,7 @@ namespace NSspi.Contexts
); );
} }
if ( status != SecurityStatus.OK ) if( status != SecurityStatus.OK )
{ {
throw new SSPIException( "Failed to create message signature.", status ); throw new SSPIException( "Failed to create message signature.", status );
} }
@@ -366,7 +360,7 @@ namespace NSspi.Contexts
byte[] outMessage; byte[] outMessage;
int position = 0; int position = 0;
// Enough room for // Enough room for
// - original message length (4 bytes) // - original message length (4 bytes)
// - signature length (2 bytes) // - signature length (2 bytes)
// - original message // - original message
@@ -389,6 +383,35 @@ namespace NSspi.Contexts
return outMessage; return outMessage;
} }
/// <summary>
/// Returns the Session Key from a context or null on failure.
/// </summary>
/// <remarks>
/// Session keys are sometimes needed for other purposes or HMAC functions. This function
/// will run QueryAttribute to get the session key struct, and read and return the key from
/// that struct.
/// </remarks>
/// <returns>byte[] with the session key data or null on failure</returns>
public byte[] QuerySessionKey()
{
SecurityStatus status;
byte[] SessionKey = null;
status = ContextNativeMethods.SafeQueryContextAttribute(
this.ContextHandle,
ContextQueryAttrib.SessionKey,
ref SessionKey
);
if( status != SecurityStatus.OK )
{
throw new SSPIException( "Failed to query session key.", status );
}
return SessionKey;
}
/// <summary> /// <summary>
/// Verifies the signature of a signed message /// Verifies the signature of a signed message
/// </summary> /// </summary>
@@ -414,8 +437,8 @@ namespace NSspi.Contexts
CheckLifecycle(); CheckLifecycle();
sizes = QueryBufferSizes(); sizes = QueryBufferSizes();
if ( signedMessage.Length < 2 + 4 + sizes.MaxSignature ) if( signedMessage.Length < 2 + 4 + sizes.MaxSignature )
{ {
throw new ArgumentException( "Input message is too small to possibly fit a valid message" ); throw new ArgumentException( "Input message is too small to possibly fit a valid message" );
} }
@@ -430,7 +453,7 @@ namespace NSspi.Contexts
sigLen = ByteWriter.ReadInt16_BE( signedMessage, position ); sigLen = ByteWriter.ReadInt16_BE( signedMessage, position );
position += 2; position += 2;
if ( messageLen + sigLen + 2 + 4 > signedMessage.Length ) if( messageLen + sigLen + 2 + 4 > signedMessage.Length )
{ {
throw new ArgumentException( "The buffer contains invalid data - the embedded length data does not add up." ); throw new ArgumentException( "The buffer contains invalid data - the embedded length data does not add up." );
} }
@@ -443,7 +466,7 @@ namespace NSspi.Contexts
Array.Copy( signedMessage, position, signatureBuffer.Buffer, 0, sigLen ); Array.Copy( signedMessage, position, signatureBuffer.Buffer, 0, sigLen );
position += sigLen; position += sigLen;
using ( adapter = new SecureBufferAdapter( new[] { dataBuffer, signatureBuffer } ) ) using( adapter = new SecureBufferAdapter( new[] { dataBuffer, signatureBuffer } ) )
{ {
status = ContextNativeMethods.SafeVerifySignature( status = ContextNativeMethods.SafeVerifySignature(
this.ContextHandle, this.ContextHandle,
@@ -453,12 +476,12 @@ namespace NSspi.Contexts
); );
} }
if ( status == SecurityStatus.OK ) if( status == SecurityStatus.OK )
{ {
origMessage = dataBuffer.Buffer; origMessage = dataBuffer.Buffer;
return true; return true;
} }
else if ( status == SecurityStatus.MessageAltered || else if( status == SecurityStatus.MessageAltered ||
status == SecurityStatus.OutOfSequence ) status == SecurityStatus.OutOfSequence )
{ {
origMessage = null; origMessage = null;
@@ -485,9 +508,9 @@ namespace NSspi.Contexts
{ {
this.ContextHandle.DangerousAddRef( ref gotRef ); this.ContextHandle.DangerousAddRef( ref gotRef );
} }
catch ( Exception ) catch( Exception )
{ {
if ( gotRef ) if( gotRef )
{ {
this.ContextHandle.DangerousRelease(); this.ContextHandle.DangerousRelease();
gotRef = false; gotRef = false;
@@ -497,7 +520,7 @@ namespace NSspi.Contexts
} }
finally finally
{ {
if ( gotRef ) if( gotRef )
{ {
status = ContextNativeMethods.QueryContextAttributes_Sizes( status = ContextNativeMethods.QueryContextAttributes_Sizes(
ref this.ContextHandle.rawHandle, ref this.ContextHandle.rawHandle,
@@ -521,7 +544,7 @@ namespace NSspi.Contexts
/// </summary> /// </summary>
/// <param name="attrib">The string-valued attribute to query.</param> /// <param name="attrib">The string-valued attribute to query.</param>
/// <returns></returns> /// <returns></returns>
private string QueryContextString(ContextQueryAttrib attrib) private string QueryContextString( ContextQueryAttrib attrib )
{ {
SecPkgContext_String stringAttrib; SecPkgContext_String stringAttrib;
SecurityStatus status = SecurityStatus.InternalError; SecurityStatus status = SecurityStatus.InternalError;
@@ -540,9 +563,9 @@ namespace NSspi.Contexts
{ {
this.ContextHandle.DangerousAddRef( ref gotRef ); this.ContextHandle.DangerousAddRef( ref gotRef );
} }
catch ( Exception ) catch( Exception )
{ {
if ( gotRef ) if( gotRef )
{ {
this.ContextHandle.DangerousRelease(); this.ContextHandle.DangerousRelease();
gotRef = false; gotRef = false;
@@ -551,7 +574,7 @@ namespace NSspi.Contexts
} }
finally finally
{ {
if ( gotRef ) if( gotRef )
{ {
status = ContextNativeMethods.QueryContextAttributes_String( status = ContextNativeMethods.QueryContextAttributes_String(
ref this.ContextHandle.rawHandle, ref this.ContextHandle.rawHandle,
@@ -561,14 +584,14 @@ namespace NSspi.Contexts
this.ContextHandle.DangerousRelease(); this.ContextHandle.DangerousRelease();
if ( status == SecurityStatus.OK ) if( status == SecurityStatus.OK )
{ {
result = Marshal.PtrToStringUni( stringAttrib.StringResult ); result = Marshal.PtrToStringUni( stringAttrib.StringResult );
ContextNativeMethods.FreeContextBuffer( stringAttrib.StringResult ); ContextNativeMethods.FreeContextBuffer( stringAttrib.StringResult );
} }
} }
} }
if( status == SecurityStatus.Unsupported ) if( status == SecurityStatus.Unsupported )
{ {
return null; return null;
@@ -582,7 +605,7 @@ namespace NSspi.Contexts
} }
/// <summary> /// <summary>
/// Verifies that the object's lifecycle (initialization / disposition) state is suitable for using the /// Verifies that the object's lifecycle (initialization / disposition) state is suitable for using the
/// object. /// object.
/// </summary> /// </summary>
private void CheckLifecycle() private void CheckLifecycle()
@@ -597,4 +620,4 @@ namespace NSspi.Contexts
} }
} }
} }
} }

View File

@@ -1,29 +1,25 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Contexts namespace NSspi.Contexts
{ {
/// <summary> /// <summary>
/// Defines options for creating a security context via win32 InitializeSecurityContext /// Defines options for creating a security context via win32 InitializeSecurityContext
/// (used by clients) and AcceptSecurityContext (used by servers). /// (used by clients) and AcceptSecurityContext (used by servers).
/// Required attribute flags are specified when creating the context. InitializeSecurityContext /// Required attribute flags are specified when creating the context. InitializeSecurityContext
/// and AcceptSecurityContext returns a value indicating what final attributes the created context /// and AcceptSecurityContext returns a value indicating what final attributes the created context
/// actually has. /// actually has.
/// </summary> /// </summary>
[Flags] [Flags]
public enum ContextAttrib : int public enum ContextAttrib : int
{ {
/// <summary> /// <summary>
/// No additional attributes are provided. /// No additional attributes are provided.
/// </summary> /// </summary>
Zero = 0, Zero = 0,
/// <summary> /// <summary>
/// The server can use the context to authenticate to other servers as the client. The /// The server can use the context to authenticate to other servers as the client. The
/// MutualAuth flag must be set for this flag to work. Valid for Kerberos. Ignore this flag for /// MutualAuth flag must be set for this flag to work. Valid for Kerberos. Ignore this flag for
/// constrained delegation. /// constrained delegation.
/// </summary> /// </summary>
Delegate = 0x00000001, Delegate = 0x00000001,
@@ -36,17 +32,16 @@ namespace NSspi.Contexts
/// </summary> /// </summary>
MutualAuth = 0x00000002, MutualAuth = 0x00000002,
/// <summary> /// <summary>
/// Detect replayed messages that have been encoded by using the EncryptMessage or MakeSignature /// Detect replayed messages that have been encoded by using the EncryptMessage or MakeSignature
/// message support functionality. /// message support functionality.
/// </summary> /// </summary>
ReplayDetect = 0x00000004, ReplayDetect = 0x00000004,
/// <summary> /// <summary>
/// Detect messages received out of sequence when using the message support functionality. /// Detect messages received out of sequence when using the message support functionality.
/// This flag implies all of the conditions specified by the Integrity flag - out-of-order sequence /// This flag implies all of the conditions specified by the Integrity flag - out-of-order sequence
/// detection can only be trusted if the integrity of any underlying sequence detection mechanism /// detection can only be trusted if the integrity of any underlying sequence detection mechanism
/// in transmitted data can be trusted. /// in transmitted data can be trusted.
/// </summary> /// </summary>
SequenceDetect = 0x00000008, SequenceDetect = 0x00000008,
@@ -55,12 +50,12 @@ namespace NSspi.Contexts
// Confidentiality is supported for NTLM with Microsoft // Confidentiality is supported for NTLM with Microsoft
// Windows NT version 4.0, SP4 and later and with the // Windows NT version 4.0, SP4 and later and with the
// Kerberos protocol in Microsoft Windows 2000 and later. // Kerberos protocol in Microsoft Windows 2000 and later.
/// <summary> /// <summary>
/// The context must protect data while in transit. Encrypt messages by using the EncryptMessage function. /// The context must protect data while in transit. Encrypt messages by using the EncryptMessage function.
/// </summary> /// </summary>
Confidentiality = 0x00000010, Confidentiality = 0x00000010,
/// <summary> /// <summary>
/// A new session key must be negotiated. /// A new session key must be negotiated.
/// This value is supported only by the Kerberos security package. /// This value is supported only by the Kerberos security package.
@@ -68,13 +63,13 @@ namespace NSspi.Contexts
UseSessionKey = 0x00000020, UseSessionKey = 0x00000020,
/// <summary> /// <summary>
/// The security package allocates output buffers for you. Buffers allocated by the security package have /// The security package allocates output buffers for you. Buffers allocated by the security package have
/// to be released by the context memory management functions. /// to be released by the context memory management functions.
/// </summary> /// </summary>
AllocateMemory = 0x00000100, AllocateMemory = 0x00000100,
/// <summary> /// <summary>
/// The security context will not handle formatting messages. This value is the default for the Kerberos, /// The security context will not handle formatting messages. This value is the default for the Kerberos,
/// Negotiate, and NTLM security packages. /// Negotiate, and NTLM security packages.
/// </summary> /// </summary>
Connection = 0x00000800, Connection = 0x00000800,
@@ -84,7 +79,7 @@ namespace NSspi.Contexts
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// A client specifies InitExtendedError in InitializeSecurityContext /// A client specifies InitExtendedError in InitializeSecurityContext
/// and the server specifies AcceptExtendedError in AcceptSecurityContext. /// and the server specifies AcceptExtendedError in AcceptSecurityContext.
/// </remarks> /// </remarks>
InitExtendedError = 0x00004000, InitExtendedError = 0x00004000,
@@ -93,7 +88,7 @@ namespace NSspi.Contexts
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// A client specifies InitExtendedError in InitializeSecurityContext /// A client specifies InitExtendedError in InitializeSecurityContext
/// and the server specifies AcceptExtendedError in AcceptSecurityContext. /// and the server specifies AcceptExtendedError in AcceptSecurityContext.
/// </remarks> /// </remarks>
AcceptExtendedError = 0x00008000, AcceptExtendedError = 0x00008000,
@@ -112,7 +107,7 @@ namespace NSspi.Contexts
/// Replayed and out-of-sequence messages will not be detected with the setting of this attribute. /// Replayed and out-of-sequence messages will not be detected with the setting of this attribute.
/// Set ReplayDetect and SequenceDetect also if these behaviors are desired. /// Set ReplayDetect and SequenceDetect also if these behaviors are desired.
/// </summary> /// </summary>
InitIntegrity = 0x00010000, InitIntegrity = 0x00010000,
/// <summary> /// <summary>
/// Sign messages and verify signatures by using the EncryptMessage and MakeSignature functions. /// Sign messages and verify signatures by using the EncryptMessage and MakeSignature functions.
@@ -134,4 +129,4 @@ namespace NSspi.Contexts
/// </summary> /// </summary>
InitUseSuppliedCreds = 0x00000080, InitUseSuppliedCreds = 0x00000080,
} }
} }

View File

@@ -1,13 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using NSspi.Buffers; using NSspi.Buffers;
using NSspi.Contexts;
namespace NSspi.Contexts namespace NSspi.Contexts
{ {
@@ -28,7 +23,7 @@ namespace NSspi.Contexts
_Out_ PULONG pfContextAttr, _Out_ PULONG pfContextAttr,
_Out_opt_ PTimeStamp ptsTimeStamp _Out_opt_ PTimeStamp ptsTimeStamp
); );
SECURITY_STATUS SEC_Entry InitializeSecurityContext( SECURITY_STATUS SEC_Entry InitializeSecurityContext(
_In_opt_ PCredHandle phCredential, // [in] handle to the credentials _In_opt_ PCredHandle phCredential, // [in] handle to the credentials
_In_opt_ PCtxtHandle phContext, // [in/out] handle of partially formed context. Always NULL the first time through _In_opt_ PCtxtHandle phContext, // [in/out] handle of partially formed context. Always NULL the first time through
@@ -45,7 +40,7 @@ namespace NSspi.Contexts
); );
*/ */
[DllImport( "Secur32.dll", EntryPoint = "AcceptSecurityContext",CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "AcceptSecurityContext", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus AcceptSecurityContext_1( internal static extern SecurityStatus AcceptSecurityContext_1(
ref RawSspiHandle credHandle, ref RawSspiHandle credHandle,
IntPtr oldContextHandle, IntPtr oldContextHandle,
@@ -58,7 +53,6 @@ namespace NSspi.Contexts
ref TimeStamp expiry ref TimeStamp expiry
); );
[DllImport( "Secur32.dll", EntryPoint = "AcceptSecurityContext", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "AcceptSecurityContext", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus AcceptSecurityContext_2( internal static extern SecurityStatus AcceptSecurityContext_2(
ref RawSspiHandle credHandle, ref RawSspiHandle credHandle,
@@ -72,7 +66,6 @@ namespace NSspi.Contexts
ref TimeStamp expiry ref TimeStamp expiry
); );
[DllImport( "Secur32.dll", EntryPoint = "InitializeSecurityContext", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "InitializeSecurityContext", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus InitializeSecurityContext_1( internal static extern SecurityStatus InitializeSecurityContext_1(
ref RawSspiHandle credentialHandle, ref RawSspiHandle credentialHandle,
@@ -89,7 +82,6 @@ namespace NSspi.Contexts
ref TimeStamp expiry ref TimeStamp expiry
); );
[DllImport( "Secur32.dll", EntryPoint = "InitializeSecurityContext", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "InitializeSecurityContext", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus InitializeSecurityContext_2( internal static extern SecurityStatus InitializeSecurityContext_2(
ref RawSspiHandle credentialHandle, ref RawSspiHandle credentialHandle,
@@ -106,12 +98,11 @@ namespace NSspi.Contexts
ref TimeStamp expiry ref TimeStamp expiry
); );
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "DeleteSecurityContext", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "DeleteSecurityContext", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus DeleteSecurityContext( ref RawSspiHandle contextHandle ); internal static extern SecurityStatus DeleteSecurityContext( ref RawSspiHandle contextHandle );
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail )]
[DllImport( "Secur32.dll", EntryPoint = "EncryptMessage", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "EncryptMessage", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus EncryptMessage( internal static extern SecurityStatus EncryptMessage(
ref RawSspiHandle contextHandle, ref RawSspiHandle contextHandle,
@@ -155,7 +146,7 @@ namespace NSspi.Contexts
ref SecPkgContext_Sizes sizes ref SecPkgContext_Sizes sizes
); );
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success)] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "QueryContextAttributes", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "QueryContextAttributes", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus QueryContextAttributes_String( internal static extern SecurityStatus QueryContextAttributes_String(
ref RawSspiHandle contextHandle, ref RawSspiHandle contextHandle,
@@ -163,11 +154,18 @@ namespace NSspi.Contexts
ref SecPkgContext_String names ref SecPkgContext_String names
); );
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "QueryContextAttributes", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus QueryContextAttributes(
ref RawSspiHandle contextHandle,
ContextQueryAttrib attrib,
IntPtr attribute
);
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "FreeContextBuffer", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "FreeContextBuffer", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus FreeContextBuffer( IntPtr handle ); internal static extern SecurityStatus FreeContextBuffer( IntPtr handle );
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "ImpersonateSecurityContext", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "ImpersonateSecurityContext", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus ImpersonateSecurityContext( ref RawSspiHandle contextHandle ); internal static extern SecurityStatus ImpersonateSecurityContext( ref RawSspiHandle contextHandle );
@@ -176,8 +174,74 @@ namespace NSspi.Contexts
[DllImport( "Secur32.dll", EntryPoint = "RevertSecurityContext", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "RevertSecurityContext", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus RevertSecurityContext( ref RawSspiHandle contextHandle ); internal static extern SecurityStatus RevertSecurityContext( ref RawSspiHandle contextHandle );
[StructLayout( LayoutKind.Sequential )]
private class KeyStruct
{
public int size;
public IntPtr data;
}
internal static SecurityStatus SafeQueryContextAttribute(
SafeContextHandle handle,
ContextQueryAttrib attribute,
ref byte[] buffer
)
{
bool gotRef = false;
SecurityStatus status = SecurityStatus.InternalError;
RuntimeHelpers.PrepareConstrainedRegions();
int pointerSize = System.Environment.Is64BitOperatingSystem ? 8 : 4; //NOTE: update this when 128 bit processors exist
IntPtr alloc_buffer = Marshal.AllocHGlobal( sizeof( uint ) + pointerSize ); //NOTE: this is at most 4 + sizeof(void*) bytes
//see struct SecPkgContext_SessionKey
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa380096(v=vs.85).aspx
try
{
handle.DangerousAddRef( ref gotRef );
}
catch( Exception )
{
if( gotRef )
{
handle.DangerousRelease();
gotRef = false;
buffer = null;
}
throw;
}
finally
{
if( gotRef )
{
status = ContextNativeMethods.QueryContextAttributes(
ref handle.rawHandle,
attribute,
alloc_buffer
);
if( status == SecurityStatus.OK )
{
KeyStruct key = new KeyStruct();
Marshal.PtrToStructure( alloc_buffer, key ); // fit to the proper size, read a byte[]
byte[] sizedBuffer = new byte[key.size];
for( int i = 0; i < key.size; i++ )
sizedBuffer[i] = Marshal.ReadByte( key.data, i );
buffer = sizedBuffer;
}
handle.DangerousRelease();
}
}
Marshal.FreeHGlobal( alloc_buffer );
return status;
}
/// <summary> /// <summary>
/// Safely invokes the native EncryptMessage function, making sure that handle ref counting is /// Safely invokes the native EncryptMessage function, making sure that handle ref counting is
/// performed in a proper CER. /// performed in a proper CER.
/// </summary> /// </summary>
/// <param name="handle"></param> /// <param name="handle"></param>
@@ -199,9 +263,9 @@ namespace NSspi.Contexts
{ {
handle.DangerousAddRef( ref gotRef ); handle.DangerousAddRef( ref gotRef );
} }
catch ( Exception ) catch( Exception )
{ {
if ( gotRef ) if( gotRef )
{ {
handle.DangerousRelease(); handle.DangerousRelease();
gotRef = false; gotRef = false;
@@ -211,7 +275,7 @@ namespace NSspi.Contexts
} }
finally finally
{ {
if ( gotRef ) if( gotRef )
{ {
status = ContextNativeMethods.EncryptMessage( status = ContextNativeMethods.EncryptMessage(
ref handle.rawHandle, ref handle.rawHandle,
@@ -228,7 +292,7 @@ namespace NSspi.Contexts
} }
/// <summary> /// <summary>
/// Safely invokes the native DecryptMessage function, making sure that handle ref counting is /// Safely invokes the native DecryptMessage function, making sure that handle ref counting is
/// performed in a proper CER. /// performed in a proper CER.
/// </summary> /// </summary>
/// <param name="handle"></param> /// <param name="handle"></param>
@@ -236,10 +300,10 @@ namespace NSspi.Contexts
/// <param name="bufferAdapter"></param> /// <param name="bufferAdapter"></param>
/// <param name="sequenceNumber"></param> /// <param name="sequenceNumber"></param>
/// <returns></returns> /// <returns></returns>
internal static SecurityStatus SafeDecryptMessage( internal static SecurityStatus SafeDecryptMessage(
SafeContextHandle handle, SafeContextHandle handle,
int qualityOfProtection, int qualityOfProtection,
SecureBufferAdapter bufferAdapter, SecureBufferAdapter bufferAdapter,
int sequenceNumber ) int sequenceNumber )
{ {
SecurityStatus status = SecurityStatus.InvalidHandle; SecurityStatus status = SecurityStatus.InvalidHandle;
@@ -279,7 +343,7 @@ namespace NSspi.Contexts
} }
/// <summary> /// <summary>
/// Safely invokes the native MakeSignature function, making sure that handle ref counting is /// Safely invokes the native MakeSignature function, making sure that handle ref counting is
/// performed in a proper CER. /// performed in a proper CER.
/// </summary> /// </summary>
/// <param name="handle"></param> /// <param name="handle"></param>
@@ -301,9 +365,9 @@ namespace NSspi.Contexts
{ {
handle.DangerousAddRef( ref gotRef ); handle.DangerousAddRef( ref gotRef );
} }
catch ( Exception ) catch( Exception )
{ {
if ( gotRef ) if( gotRef )
{ {
handle.DangerousRelease(); handle.DangerousRelease();
gotRef = false; gotRef = false;
@@ -313,7 +377,7 @@ namespace NSspi.Contexts
} }
finally finally
{ {
if ( gotRef ) if( gotRef )
{ {
status = ContextNativeMethods.MakeSignature( status = ContextNativeMethods.MakeSignature(
ref handle.rawHandle, ref handle.rawHandle,
@@ -330,7 +394,7 @@ namespace NSspi.Contexts
} }
/// <summary> /// <summary>
/// Safely invokes the native VerifySignature function, making sure that handle ref counting is /// Safely invokes the native VerifySignature function, making sure that handle ref counting is
/// performed in a proper CER. /// performed in a proper CER.
/// </summary> /// </summary>
/// <param name="handle"></param> /// <param name="handle"></param>
@@ -352,9 +416,9 @@ namespace NSspi.Contexts
{ {
handle.DangerousAddRef( ref gotRef ); handle.DangerousAddRef( ref gotRef );
} }
catch ( Exception ) catch( Exception )
{ {
if ( gotRef ) if( gotRef )
{ {
handle.DangerousRelease(); handle.DangerousRelease();
gotRef = false; gotRef = false;
@@ -364,7 +428,7 @@ namespace NSspi.Contexts
} }
finally finally
{ {
if ( gotRef ) if( gotRef )
{ {
status = ContextNativeMethods.VerifySignature( status = ContextNativeMethods.VerifySignature(
ref handle.rawHandle, ref handle.rawHandle,
@@ -380,4 +444,4 @@ namespace NSspi.Contexts
return status; return status;
} }
} }
} }

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Contexts namespace NSspi.Contexts
{ {
@@ -27,4 +23,4 @@ namespace NSspi.Contexts
{ {
public IntPtr StringResult; public IntPtr StringResult;
} }
} }

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Contexts namespace NSspi.Contexts
{ {
@@ -36,5 +32,15 @@ namespace NSspi.Contexts
/// Results for a query of this type are stored in a Win32 SecPkgContext_Authority structure. /// Results for a query of this type are stored in a Win32 SecPkgContext_Authority structure.
/// </remarks> /// </remarks>
Authority = 6, Authority = 6,
/// <summary>
/// Queries the context for it's neogtiated SessionKey
/// </summary>
/// <remarks>
/// Results for a query of this type are stored in a Win32 SecPkgContext_SessionKey structure
/// </remarks>
SessionKey = 9,
AccessToken = 13, //not implemented yet but this would be cool
} }
} }

View File

@@ -1,21 +1,17 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Contexts namespace NSspi.Contexts
{ {
/// <summary> /// <summary>
/// Represents impersonation performed on a server on behalf of a client. /// Represents impersonation performed on a server on behalf of a client.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The handle controls the lifetime of impersonation, and will revert the impersonation /// The handle controls the lifetime of impersonation, and will revert the impersonation
/// if it is disposed, or if it is finalized ie by being leaked and garbage collected. /// if it is disposed, or if it is finalized ie by being leaked and garbage collected.
/// ///
/// If the handle is accidentally leaked while operations are performed on behalf of the user, /// If the handle is accidentally leaked while operations are performed on behalf of the user,
/// impersonation may be reverted at any arbitrary time, perhaps during those operations. /// impersonation may be reverted at any arbitrary time, perhaps during those operations.
/// This may lead to operations being performed in the security context of the server, /// This may lead to operations being performed in the security context of the server,
/// potentially leading to security vulnerabilities. /// potentially leading to security vulnerabilities.
/// </remarks> /// </remarks>
public class ImpersonationHandle : IDisposable public class ImpersonationHandle : IDisposable
@@ -27,7 +23,7 @@ namespace NSspi.Contexts
/// Initializes a new instance of the ImpersonationHandle. Does not perform impersonation. /// Initializes a new instance of the ImpersonationHandle. Does not perform impersonation.
/// </summary> /// </summary>
/// <param name="server">The server context that is performing impersonation.</param> /// <param name="server">The server context that is performing impersonation.</param>
internal ImpersonationHandle(ServerContext server) internal ImpersonationHandle( ServerContext server )
{ {
this.server = server; this.server = server;
this.disposed = false; this.disposed = false;
@@ -54,6 +50,5 @@ namespace NSspi.Contexts
this.server.RevertImpersonate(); this.server.RevertImpersonate();
} }
} }
} }
} }

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Contexts namespace NSspi.Contexts
{ {
@@ -28,4 +24,4 @@ namespace NSspi.Contexts
return status == SecurityStatus.OK; return status == SecurityStatus.OK;
} }
} }
} }

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using NSspi.Buffers; using NSspi.Buffers;
using NSspi.Credentials; using NSspi.Credentials;
@@ -25,7 +21,7 @@ namespace NSspi.Contexts
/// </summary> /// </summary>
/// <param name="cred"></param> /// <param name="cred"></param>
/// <param name="requestedAttribs"></param> /// <param name="requestedAttribs"></param>
public ServerContext(ServerCredential cred, ContextAttrib requestedAttribs) : base ( cred ) public ServerContext( Credential cred, ContextAttrib requestedAttribs ) : base( cred )
{ {
this.requestedAttribs = requestedAttribs; this.requestedAttribs = requestedAttribs;
this.finalAttribs = ContextAttrib.Zero; this.finalAttribs = ContextAttrib.Zero;
@@ -50,11 +46,11 @@ namespace NSspi.Contexts
/// This method is performed iteratively to continue and end the authentication cycle with the /// This method is performed iteratively to continue and end the authentication cycle with the
/// client. Each stage works by acquiring a token from one side, presenting it to the other side /// client. Each stage works by acquiring a token from one side, presenting it to the other side
/// which in turn may generate a new token. /// which in turn may generate a new token.
/// ///
/// The cycle typically starts and ends with the client. On the first invocation on the client, /// The cycle typically starts and ends with the client. On the first invocation on the client,
/// no server token exists, and null is provided in its place. The client returns its status, providing /// no server token exists, and null is provided in its place. The client returns its status, providing
/// its output token for the server. The server accepts the clients token as input and provides a /// its output token for the server. The server accepts the clients token as input and provides a
/// token as output to send back to the client. This cycle continues until the server and client /// token as output to send back to the client. This cycle continues until the server and client
/// both indicate, typically, a SecurityStatus of 'OK'. /// both indicate, typically, a SecurityStatus of 'OK'.
/// </remarks> /// </remarks>
/// <param name="clientToken">The most recently received token from the client.</param> /// <param name="clientToken">The most recently received token from the client.</param>
@@ -63,7 +59,7 @@ namespace NSspi.Contexts
/// <returns>A status message indicating the progression of the authentication cycle. /// <returns>A status message indicating the progression of the authentication cycle.
/// A status of 'OK' indicates that the cycle is complete, from the servers's perspective. If the nextToken /// A status of 'OK' indicates that the cycle is complete, from the servers's perspective. If the nextToken
/// is not null, it must be sent to the client. /// is not null, it must be sent to the client.
/// A status of 'Continue' indicates that the output token should be sent to the client and /// A status of 'Continue' indicates that the output token should be sent to the client and
/// a response should be anticipated.</returns> /// a response should be anticipated.</returns>
public SecurityStatus AcceptToken( byte[] clientToken, out byte[] nextToken ) public SecurityStatus AcceptToken( byte[] clientToken, out byte[] nextToken )
{ {
@@ -82,21 +78,21 @@ namespace NSspi.Contexts
} }
else if( this.Initialized ) else if( this.Initialized )
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
"Attempted to continue initialization of a ServerContext after initialization had completed." "Attempted to continue initialization of a ServerContext after initialization had completed."
); );
} }
clientBuffer = new SecureBuffer( clientToken, BufferType.Token ); clientBuffer = new SecureBuffer( clientToken, BufferType.Token );
outBuffer = new SecureBuffer( outBuffer = new SecureBuffer(
new byte[ this.Credential.PackageInfo.MaxTokenLength ], new byte[this.Credential.PackageInfo.MaxTokenLength],
BufferType.Token BufferType.Token
); );
using ( clientAdapter = new SecureBufferAdapter( clientBuffer ) ) using( clientAdapter = new SecureBufferAdapter( clientBuffer ) )
{ {
using ( outAdapter = new SecureBufferAdapter( outBuffer ) ) using( outAdapter = new SecureBufferAdapter( outBuffer ) )
{ {
if( this.ContextHandle.IsInvalid ) if( this.ContextHandle.IsInvalid )
{ {
@@ -125,19 +121,17 @@ namespace NSspi.Contexts
ref this.finalAttribs, ref this.finalAttribs,
ref rawExpiry ref rawExpiry
); );
} }
} }
} }
if ( status == SecurityStatus.OK ) if( status == SecurityStatus.OK )
{ {
nextToken = null; nextToken = null;
base.Initialize( rawExpiry.ToDateTime() ); base.Initialize( rawExpiry.ToDateTime() );
if ( outBuffer.Length != 0 ) if( outBuffer.Length != 0 )
{ {
nextToken = new byte[outBuffer.Length]; nextToken = new byte[outBuffer.Length];
Array.Copy( outBuffer.Buffer, nextToken, nextToken.Length ); Array.Copy( outBuffer.Buffer, nextToken, nextToken.Length );
@@ -147,7 +141,7 @@ namespace NSspi.Contexts
nextToken = null; nextToken = null;
} }
} }
else if ( status == SecurityStatus.ContinueNeeded ) else if( status == SecurityStatus.ContinueNeeded )
{ {
nextToken = new byte[outBuffer.Length]; nextToken = new byte[outBuffer.Length];
Array.Copy( outBuffer.Buffer, nextToken, nextToken.Length ); Array.Copy( outBuffer.Buffer, nextToken, nextToken.Length );
@@ -161,17 +155,17 @@ namespace NSspi.Contexts
} }
/// <summary> /// <summary>
/// Changes the current thread's security context to impersonate the user of the client. /// Changes the current thread's security context to impersonate the user of the client.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Requires that the security package provided with the server's credentials, as well as the /// Requires that the security package provided with the server's credentials, as well as the
/// client's credentials, support impersonation. /// client's credentials, support impersonation.
/// ///
/// Currently, only one thread may initiate impersonation per security context. Impersonation may /// Currently, only one thread may initiate impersonation per security context. Impersonation may
/// follow threads created by the initial impersonation thread, however. /// follow threads created by the initial impersonation thread, however.
/// </remarks> /// </remarks>
/// <returns>A handle to capture the lifetime of the impersonation. Dispose the handle to revert /// <returns>A handle to capture the lifetime of the impersonation. Dispose the handle to revert
/// impersonation. If the handle is leaked, the impersonation will automatically revert at a /// impersonation. If the handle is leaked, the impersonation will automatically revert at a
/// non-deterministic time when the handle is finalized by the Garbage Collector.</returns> /// non-deterministic time when the handle is finalized by the Garbage Collector.</returns>
public ImpersonationHandle ImpersonateClient() public ImpersonationHandle ImpersonateClient()
{ {
@@ -242,7 +236,7 @@ namespace NSspi.Contexts
{ {
throw new SSPIException( "Failed to impersonate the client", status ); throw new SSPIException( "Failed to impersonate the client", status );
} }
return handle; return handle;
} }
@@ -306,4 +300,4 @@ namespace NSspi.Contexts
base.Dispose( disposing ); base.Dispose( disposing );
} }
} }
} }

View File

@@ -0,0 +1,55 @@
using System;
using System.Runtime.InteropServices;
namespace NSspi.Credentials
{
/// <summary>
/// Provides authentication data in native method calls.
/// </summary>
/// <remarks>
/// Implements the 'SEC_WINNT_AUTH_IDENTITY' structure. See:
///
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa380131(v=vs.85).aspx
/// </remarks>
[StructLayout( LayoutKind.Sequential )]
internal struct NativeAuthData
{
public NativeAuthData( string domain, string username, string password, NativeAuthDataFlag flag )
{
this.Domain = domain;
this.DomainLength = domain.Length;
this.User = username;
this.UserLength = username.Length;
this.Password = password;
this.PasswordLength = password.Length;
this.Flags = flag;
}
[MarshalAs( UnmanagedType.LPWStr )]
public string User;
public int UserLength;
[MarshalAs( UnmanagedType.LPWStr )]
public string Domain;
public int DomainLength;
[MarshalAs( UnmanagedType.LPWStr )]
public string Password;
public int PasswordLength;
public NativeAuthDataFlag Flags;
}
internal enum NativeAuthDataFlag : int
{
Ansi = 1,
Unicode = 1
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Credentials
{
/// <summary>
/// Represents the credentials of the user running the current process, for use as an SSPI client.
/// </summary>
public class ClientCredential : CurrentCredential
{
/// <summary>
/// Initializes a new instance of the ClientCredential class.
/// </summary>
/// <param name="package">The security package to acquire the credential handle from.</param>
public ClientCredential( string package )
: base( package, CredentialUse.Outbound )
{
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
namespace NSspi.Credentials
{
/// <summary>
/// Represents a handle to the credentials of the user running the current process, to be used to
/// authenticate as a client.
/// </summary>
public class ClientCurrentCredential : CurrentCredential
{
/// <summary>
/// Initializes a new instance of the ClientCurrentCredential class.
/// </summary>
/// <param name="package">The security package to acquire the credential handle from.</param>
public ClientCurrentCredential( string package )
: base( package, CredentialUse.Outbound )
{
}
}
}

View File

@@ -1,13 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using NSspi.Credentials;
namespace NSspi.Credentials namespace NSspi.Credentials
{ {
@@ -49,7 +42,7 @@ namespace NSspi.Credentials
this.PackageInfo = PackageSupport.GetPackageCapabilities( this.SecurityPackage ); this.PackageInfo = PackageSupport.GetPackageCapabilities( this.SecurityPackage );
} }
/// <summary> /// <summary>
/// Gets metadata for the security package associated with the credential. /// Gets metadata for the security package associated with the credential.
/// </summary> /// </summary>
@@ -151,7 +144,7 @@ namespace NSspi.Credentials
protected set protected set
{ {
CheckLifecycle(); CheckLifecycle();
this.expiry = value; this.expiry = value;
} }
} }
@@ -187,9 +180,9 @@ namespace NSspi.Credentials
protected virtual void Dispose( bool disposing ) protected virtual void Dispose( bool disposing )
{ {
if ( this.disposed == false ) if( this.disposed == false )
{ {
if ( disposing ) if( disposing )
{ {
this.safeCredHandle.Dispose(); this.safeCredHandle.Dispose();
} }
@@ -206,4 +199,4 @@ namespace NSspi.Credentials
} }
} }
} }
} }

View File

@@ -1,17 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using NSspi.Credentials;
namespace NSspi.Credentials namespace NSspi.Credentials
{ {
internal static class CredentialNativeMethods internal static class CredentialNativeMethods
{ {
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail)] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
[DllImport( "Secur32.dll", EntryPoint = "AcquireCredentialsHandle", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "AcquireCredentialsHandle", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus AcquireCredentialsHandle( internal static extern SecurityStatus AcquireCredentialsHandle(
string principleName, string principleName,
@@ -25,13 +20,27 @@ namespace NSspi.Credentials
ref TimeStamp expiry ref TimeStamp expiry
); );
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.MayFail )]
[DllImport( "Secur32.dll", EntryPoint = "AcquireCredentialsHandle", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus AcquireCredentialsHandle_AuthData(
string principleName,
string packageName,
CredentialUse credentialUse,
IntPtr loginId,
ref NativeAuthData authData,
IntPtr getKeyFunc,
IntPtr getKeyData,
ref RawSspiHandle credentialHandle,
ref TimeStamp expiry
);
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "FreeCredentialsHandle", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "FreeCredentialsHandle", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus FreeCredentialsHandle( internal static extern SecurityStatus FreeCredentialsHandle(
ref RawSspiHandle credentialHandle ref RawSspiHandle credentialHandle
); );
/// <summary> /// <summary>
/// The overload of the QueryCredentialsAttribute method that is used for querying the name attribute. /// The overload of the QueryCredentialsAttribute method that is used for querying the name attribute.
/// In this call, it takes a void* to a structure that contains a wide char pointer. The wide character /// In this call, it takes a void* to a structure that contains a wide char pointer. The wide character
@@ -49,4 +58,4 @@ namespace NSspi.Credentials
ref QueryNameAttribCarrier name ref QueryNameAttribCarrier name
); );
} }
} }

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Credentials namespace NSspi.Credentials
{ {
@@ -16,4 +12,4 @@ namespace NSspi.Credentials
/// </summary> /// </summary>
Names = 1, Names = 1,
} }
} }

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Credentials namespace NSspi.Credentials
{ {
@@ -29,4 +25,4 @@ namespace NSspi.Credentials
/// </summary> /// </summary>
Both = 3, Both = 3,
} }
} }

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Credentials namespace NSspi.Credentials
{ {
@@ -38,7 +34,7 @@ namespace NSspi.Credentials
this.Handle = new SafeCredentialHandle(); this.Handle = new SafeCredentialHandle();
// The finally clause is the actual constrained region. The VM pre-allocates any stack space, // 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 // performs any allocations it needs to prepare methods for execution, and postpones any
// instances of the 'uncatchable' exceptions (ThreadAbort, StackOverflow, OutOfMemory). // instances of the 'uncatchable' exceptions (ThreadAbort, StackOverflow, OutOfMemory).
RuntimeHelpers.PrepareConstrainedRegions(); RuntimeHelpers.PrepareConstrainedRegions();
try { } try { }
@@ -57,13 +53,12 @@ namespace NSspi.Credentials
); );
} }
if ( status != SecurityStatus.OK ) if( status != SecurityStatus.OK )
{ {
throw new SSPIException( "Failed to call AcquireCredentialHandle", status ); throw new SSPIException( "Failed to call AcquireCredentialHandle", status );
} }
this.Expiry = rawExpiry.ToDateTime(); this.Expiry = rawExpiry.ToDateTime();
} }
} }
} }

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
namespace NSspi.Credentials
{
/// <summary>
/// Represents credentials acquired by providing a username, password, and domain.
/// </summary>
public class PasswordCredential : Credential
{
/// <summary>
/// Initializes a new instance of the PasswordCredential class.
/// </summary>
/// <remarks>
/// It is possible to acquire a valid handle to credentials that do not provide a valid
/// username-password combination. The username and password are not validation until the
/// authentication cycle begins.
/// </remarks>
/// <param name="domain">The domain to authenticate to.</param>
/// <param name="username">The username of the user to authenticate as.</param>
/// <param name="password">The user's password.</param>
/// <param name="secPackage">The SSPI security package to create credentials for.</param>
/// <param name="use">
/// Specify inbound when acquiring credentials for a server; outbound for a client.
/// </param>
public PasswordCredential( string domain, string username, string password, string secPackage, CredentialUse use )
: base( secPackage )
{
NativeAuthData authData = new NativeAuthData( domain, username, password, NativeAuthDataFlag.Unicode );
Init( authData, secPackage, use );
}
private void Init( NativeAuthData authData, string secPackage, CredentialUse use )
{
string packageName;
TimeStamp rawExpiry = new TimeStamp();
SecurityStatus status = SecurityStatus.InternalError;
// -- Package --
// Copy off for the call, since this.SecurityPackage is a property.
packageName = this.SecurityPackage;
this.Handle = 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 = CredentialNativeMethods.AcquireCredentialsHandle_AuthData(
null,
packageName,
use,
IntPtr.Zero,
ref authData,
IntPtr.Zero,
IntPtr.Zero,
ref this.Handle.rawHandle,
ref rawExpiry
);
}
if( status != SecurityStatus.OK )
{
throw new SSPIException( "Failed to call AcquireCredentialHandle", status );
}
this.Expiry = rawExpiry.ToDateTime();
}
}
}

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Credentials namespace NSspi.Credentials
{ {
@@ -14,9 +10,9 @@ namespace NSspi.Credentials
internal struct QueryNameAttribCarrier internal struct QueryNameAttribCarrier
{ {
/// <summary> /// <summary>
/// A pointer to a null-terminated ascii-encoded containing the principle name /// A pointer to a null-terminated ascii-encoded containing the principle name
/// associated with a credential /// associated with a credential
/// </summary> /// </summary>
public IntPtr Name; public IntPtr Name;
} }
} }

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Credentials namespace NSspi.Credentials
{ {
@@ -28,5 +24,4 @@ namespace NSspi.Credentials
return status == SecurityStatus.OK; return status == SecurityStatus.OK;
} }
} }
}
}

View File

@@ -1,25 +1,21 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Credentials namespace NSspi.Credentials
{ {
/// <summary> /// <summary>
/// Represents the credentials of the user running the current process, for use as an SSPI server. /// Represents a handle to the credentials of the user running the current process, to be used to
/// authenticate as a server.
/// </summary> /// </summary>
public class ServerCredential : CurrentCredential public class ServerCurrentCredential : CurrentCredential
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the ServerCredential class, acquiring credentials from /// Initializes a new instance of the ServerCredential class, acquiring credentials from
/// the current thread's security context. /// the current thread's security context.
/// </summary> /// </summary>
/// <param name="package">The name of the security package to obtain credentials from.</param> /// <param name="package">The name of the security package to obtain credentials from.</param>
public ServerCredential( string package ) public ServerCurrentCredential( string package )
: base( package, CredentialUse.Inbound ) : base( package, CredentialUse.Inbound )
{ {
} }
} }
} }

View File

@@ -1,8 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
namespace NSspi namespace NSspi
{ {
@@ -57,4 +54,4 @@ namespace NSspi
throw new ArgumentException( "Could not find a matching enumeration value for the text '" + text + "'." ); throw new ArgumentException( "Could not find a matching enumeration value for the text '" + text + "'." );
} }
} }
} }

View File

@@ -23,6 +23,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks> <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<Prefer32Bit>false</Prefer32Bit> <Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\NSspi.XML</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
@@ -54,18 +55,20 @@
<Compile Include="Contexts\ContextQueryAttrib.cs" /> <Compile Include="Contexts\ContextQueryAttrib.cs" />
<Compile Include="Contexts\ImpersonationHandle.cs" /> <Compile Include="Contexts\ImpersonationHandle.cs" />
<Compile Include="Contexts\SafeContextHandle.cs" /> <Compile Include="Contexts\SafeContextHandle.cs" />
<Compile Include="Credentials\AuthData.cs" />
<Compile Include="Credentials\ClientCurrentCredential.cs" />
<Compile Include="Credentials\CurrentCredential.cs" /> <Compile Include="Credentials\CurrentCredential.cs" />
<Compile Include="Credentials\PasswordCredential.cs" />
<Compile Include="Credentials\ServerCurrentCredential.cs" />
<Compile Include="EnumMgr.cs" /> <Compile Include="EnumMgr.cs" />
<Compile Include="SecPkgInfo.cs" /> <Compile Include="SecPkgInfo.cs" />
<Compile Include="Contexts\ServerContext.cs" /> <Compile Include="Contexts\ServerContext.cs" />
<Compile Include="Credentials\ClientCredential.cs" />
<Compile Include="Credentials\Credential.cs" /> <Compile Include="Credentials\Credential.cs" />
<Compile Include="Credentials\CredentialNativeMethods.cs" /> <Compile Include="Credentials\CredentialNativeMethods.cs" />
<Compile Include="Credentials\CredentialQueryAttrib.cs" /> <Compile Include="Credentials\CredentialQueryAttrib.cs" />
<Compile Include="Credentials\CredentialUse.cs" /> <Compile Include="Credentials\CredentialUse.cs" />
<Compile Include="Credentials\QueryNameSupport.cs" /> <Compile Include="Credentials\QueryNameSupport.cs" />
<Compile Include="Credentials\SafeCredentialHandle.cs" /> <Compile Include="Credentials\SafeCredentialHandle.cs" />
<Compile Include="Credentials\ServerCredential.cs" />
<Compile Include="NativeMethods.cs" /> <Compile Include="NativeMethods.cs" />
<Compile Include="PackageSupport.cs" /> <Compile Include="PackageSupport.cs" />
<Compile Include="PackageNames.cs" /> <Compile Include="PackageNames.cs" />

View File

@@ -2,7 +2,7 @@
<package > <package >
<metadata> <metadata>
<id>NSspi</id> <id>NSspi</id>
<version>0.1.3.0</version> <version>0.2.0.0</version>
<authors>Kevin Thompson</authors> <authors>Kevin Thompson</authors>
<owners>Kevin Thompson</owners> <owners>Kevin Thompson</owners>
<projectUrl>https://github.com/antiduh/nsspi</projectUrl> <projectUrl>https://github.com/antiduh/nsspi</projectUrl>
@@ -12,7 +12,7 @@
better known as Windows Integrated Authentication. better known as Windows Integrated Authentication.
</description> </description>
<language>C#</language> <language>C#</language>
<releaseNotes>First release of NSspi.</releaseNotes> <releaseNotes>Adds support for username/password credentials, but introduces a minor change in the interface that breaks existing code.</releaseNotes>
<copyright>Copyright 2017</copyright> <copyright>Copyright 2017</copyright>
</metadata> </metadata>
</package> </package>

View File

@@ -1,21 +1,15 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using NSspi.Contexts;
namespace NSspi namespace NSspi
{ {
internal static class NativeMethods internal static class NativeMethods
{ {
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success)] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "FreeContextBuffer", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "FreeContextBuffer", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus FreeContextBuffer( IntPtr buffer ); internal static extern SecurityStatus FreeContextBuffer( IntPtr buffer );
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "QuerySecurityPackageInfo", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "QuerySecurityPackageInfo", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus QuerySecurityPackageInfo( string packageName, ref IntPtr pkgInfo ); internal static extern SecurityStatus QuerySecurityPackageInfo( string packageName, ref IntPtr pkgInfo );
@@ -23,6 +17,5 @@ namespace NSspi
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
[DllImport( "Secur32.dll", EntryPoint = "EnumerateSecurityPackages", CharSet = CharSet.Unicode )] [DllImport( "Secur32.dll", EntryPoint = "EnumerateSecurityPackages", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus EnumerateSecurityPackages( ref int numPackages, ref IntPtr pkgInfoArry ); internal static extern SecurityStatus EnumerateSecurityPackages( ref int numPackages, ref IntPtr pkgInfoArry );
} }
} }

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi namespace NSspi
{ {
@@ -26,4 +22,4 @@ namespace NSspi
/// </summary> /// </summary>
public const string Ntlm = "NTLM"; public const string Ntlm = "NTLM";
} }
} }

View File

@@ -1,10 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi namespace NSspi
{ {
@@ -24,7 +20,7 @@ namespace NSspi
SecurityStatus status = SecurityStatus.InternalError; SecurityStatus status = SecurityStatus.InternalError;
IntPtr rawInfoPtr; IntPtr rawInfoPtr;
rawInfoPtr = new IntPtr(); rawInfoPtr = new IntPtr();
info = new SecPkgInfo(); info = new SecPkgInfo();
@@ -35,11 +31,11 @@ namespace NSspi
{ {
status = NativeMethods.QuerySecurityPackageInfo( packageName, ref rawInfoPtr ); status = NativeMethods.QuerySecurityPackageInfo( packageName, ref rawInfoPtr );
if ( rawInfoPtr != IntPtr.Zero ) if( rawInfoPtr != IntPtr.Zero )
{ {
try try
{ {
if ( status == SecurityStatus.OK ) if( status == SecurityStatus.OK )
{ {
// This performs allocations as it makes room for the strings contained in the SecPkgInfo class. // This performs allocations as it makes room for the strings contained in the SecPkgInfo class.
Marshal.PtrToStructure( rawInfoPtr, info ); Marshal.PtrToStructure( rawInfoPtr, info );
@@ -71,7 +67,7 @@ namespace NSspi
IntPtr pkgArrayPtr; IntPtr pkgArrayPtr;
IntPtr pkgPtr; IntPtr pkgPtr;
int numPackages = 0; int numPackages = 0;
int pkgSize = Marshal.SizeOf( typeof(SecPkgInfo) ); int pkgSize = Marshal.SizeOf( typeof( SecPkgInfo ) );
pkgArrayPtr = new IntPtr(); pkgArrayPtr = new IntPtr();
@@ -90,7 +86,7 @@ namespace NSspi
// Bwooop Bwooop Alocation Alert // Bwooop Bwooop Alocation Alert
// 1) We allocate the array // 1) We allocate the array
// 2) We allocate the individual elements in the array (they're class objects). // 2) We allocate the individual elements in the array (they're class objects).
// 3) We allocate the strings in the individual elements in the array when we // 3) We allocate the strings in the individual elements in the array when we
// call Marshal.PtrToStructure() // call Marshal.PtrToStructure()
packages = new SecPkgInfo[numPackages]; packages = new SecPkgInfo[numPackages];
@@ -99,7 +95,7 @@ namespace NSspi
{ {
packages[i] = new SecPkgInfo(); packages[i] = new SecPkgInfo();
} }
for( int i = 0; i < numPackages; i++ ) for( int i = 0; i < numPackages; i++ )
{ {
pkgPtr = IntPtr.Add( pkgArrayPtr, i * pkgSize ); pkgPtr = IntPtr.Add( pkgArrayPtr, i * pkgSize );
@@ -123,4 +119,4 @@ namespace NSspi
return packages; return packages;
} }
} }
} }

View File

@@ -1,36 +1,35 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("NSspi")] [assembly: AssemblyTitle( "NSspi" )]
[assembly: AssemblyDescription("")] [assembly: AssemblyDescription( "" )]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration( "" )]
[assembly: AssemblyCompany("Kevin Thompson")] [assembly: AssemblyCompany( "Kevin Thompson" )]
[assembly: AssemblyProduct("NSspi")] [assembly: AssemblyProduct( "NSspi" )]
[assembly: AssemblyCopyright("Copyright © Kevin Thompson 2014")] [assembly: AssemblyCopyright( "Copyright © Kevin Thompson 2017" )]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark( "" )]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture( "" )]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. // COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)] [assembly: ComVisible( false )]
// The following GUID is for the ID of the typelib if this project is exposed to COM // The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("9abf710c-c646-42aa-8183-76bfa141a07b")] [assembly: Guid( "9abf710c-c646-42aa-8183-76bfa141a07b" )]
// Version information for an assembly consists of the following four values: // Version information for an assembly consists of the following four values:
// //
// Major Version // Major Version
// Minor Version // Minor Version
// Build Number // Build Number
// Revision // Revision
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.1.3.0")] [assembly: AssemblyVersion( "0.2.0.0" )]
[assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyFileVersion( "0.2.0.0" )]

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace NSspi namespace NSspi
{ {
@@ -26,7 +22,7 @@ namespace NSspi
this.message = message; this.message = message;
this.errorCode = errorCode; this.errorCode = errorCode;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the SSPIException class from serialization data. /// Initializes a new instance of the SSPIException class from serialization data.
/// </summary> /// </summary>
@@ -35,7 +31,7 @@ namespace NSspi
protected SSPIException( SerializationInfo info, StreamingContext context ) protected SSPIException( SerializationInfo info, StreamingContext context )
: base( info, context ) : base( info, context )
{ {
this.message = info.GetString("message"); this.message = info.GetString( "message" );
this.errorCode = (SecurityStatus)info.GetUInt32( "errorCode" ); this.errorCode = (SecurityStatus)info.GetUInt32( "errorCode" );
} }
@@ -70,13 +66,13 @@ namespace NSspi
{ {
get get
{ {
return string.Format( return string.Format(
"{0}. Error Code = '0x{1:X}' - \"{2}\".", "{0}. Error Code = '0x{1:X}' - \"{2}\".",
this.message, this.message,
this.errorCode, this.errorCode,
EnumMgr.ToText(this.errorCode) EnumMgr.ToText( this.errorCode )
); );
} }
} }
} }
} }

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi namespace NSspi
{ {
@@ -47,55 +43,55 @@ namespace NSspi
} }
/// <summary> /// <summary>
/// Describes the capabilities of a security package. /// Describes the capabilities of a security package.
/// </summary> /// </summary>
[Flags] [Flags]
public enum SecPkgCapability : uint public enum SecPkgCapability : uint
{ {
/// <summary> /// <summary>
/// Whether the package supports generating messages with integrity information. Required for MakeSignature and VerifySignature. /// Whether the package supports generating messages with integrity information. Required for MakeSignature and VerifySignature.
/// </summary> /// </summary>
Integrity = 0x1, Integrity = 0x1,
/// <summary> /// <summary>
/// Whether the package supports generating encrypted messages. Required for EncryptMessage and DecryptMessage. /// Whether the package supports generating encrypted messages. Required for EncryptMessage and DecryptMessage.
/// </summary> /// </summary>
Privacy = 0x2, Privacy = 0x2,
/// <summary> /// <summary>
/// Whether the package uses any other buffer information than token buffers. /// Whether the package uses any other buffer information than token buffers.
/// </summary> /// </summary>
TokenOnly = 0x4, TokenOnly = 0x4,
/// <summary> /// <summary>
/// Whether the package supports datagram-style authentication. /// Whether the package supports datagram-style authentication.
/// </summary> /// </summary>
Datagram = 0x8, Datagram = 0x8,
/// <summary> /// <summary>
/// Whether the package supports creating contexts with connection semantics /// Whether the package supports creating contexts with connection semantics
/// </summary> /// </summary>
Connection = 0x10, Connection = 0x10,
/// <summary> /// <summary>
/// Multiple legs are neccessary for authentication. /// Multiple legs are neccessary for authentication.
/// </summary> /// </summary>
MultiLeg = 0x20, MultiLeg = 0x20,
/// <summary> /// <summary>
/// Server authentication is not supported. /// Server authentication is not supported.
/// </summary> /// </summary>
ClientOnly = 0x40, ClientOnly = 0x40,
/// <summary> /// <summary>
/// Supports extended error handling facilities. /// Supports extended error handling facilities.
/// </summary> /// </summary>
ExtendedError = 0x80, ExtendedError = 0x80,
/// <summary> /// <summary>
/// Supports client impersonation on the server. /// Supports client impersonation on the server.
/// </summary> /// </summary>
Impersonation = 0x100, Impersonation = 0x100,
/// <summary> /// <summary>
/// Understands Windows princple and target names. /// Understands Windows princple and target names.
@@ -105,45 +101,45 @@ namespace NSspi
/// <summary> /// <summary>
/// Supports stream semantics /// Supports stream semantics
/// </summary> /// </summary>
Stream = 0x400, Stream = 0x400,
/// <summary> /// <summary>
/// Package may be used by the Negiotiate meta-package. /// Package may be used by the Negiotiate meta-package.
/// </summary> /// </summary>
Negotiable = 0x800, Negotiable = 0x800,
/// <summary> /// <summary>
/// Compatible with GSS. /// Compatible with GSS.
/// </summary> /// </summary>
GssCompatible = 0x1000, GssCompatible = 0x1000,
/// <summary> /// <summary>
/// Supports LsaLogonUser /// Supports LsaLogonUser
/// </summary> /// </summary>
Logon = 0x2000, Logon = 0x2000,
/// <summary> /// <summary>
/// Token buffers are in Ascii format. /// Token buffers are in Ascii format.
/// </summary> /// </summary>
AsciiBuffers = 0x4000, AsciiBuffers = 0x4000,
/// <summary> /// <summary>
/// Supports separating large tokens into multiple buffers. /// Supports separating large tokens into multiple buffers.
/// </summary> /// </summary>
Fragment = 0x8000, Fragment = 0x8000,
/// <summary> /// <summary>
/// Supports mutual authentication between a client and server. /// Supports mutual authentication between a client and server.
/// </summary> /// </summary>
MutualAuth = 0x10000, MutualAuth = 0x10000,
/// <summary> /// <summary>
/// Supports credential delegation from the server to a third context. /// Supports credential delegation from the server to a third context.
/// </summary> /// </summary>
Delegation = 0x20000, Delegation = 0x20000,
/// <summary> /// <summary>
/// Supports calling EncryptMessage with the read-only-checksum flag, which protects data only /// Supports calling EncryptMessage with the read-only-checksum flag, which protects data only
/// with a checksum and does not encrypt it. /// with a checksum and does not encrypt it.
/// </summary> /// </summary>
ReadOnlyChecksum = 0x40000, ReadOnlyChecksum = 0x40000,
@@ -157,11 +153,11 @@ namespace NSspi
/// <summary> /// <summary>
/// Extends the negotiate package; only one such package may be registered at any time. /// Extends the negotiate package; only one such package may be registered at any time.
/// </summary> /// </summary>
ExtendsNego = 0x00100000, ExtendsNego = 0x00100000,
/// <summary> /// <summary>
/// This package is negotiated by the package of type ExtendsNego. /// This package is negotiated by the package of type ExtendsNego.
/// </summary> /// </summary>
Negotiable2 = 0x00200000, Negotiable2 = 0x00200000,
} }
} }

View File

@@ -1,9 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Buffers namespace NSspi.Buffers
{ {
@@ -37,10 +33,10 @@ namespace NSspi.Buffers
/// </summary> /// </summary>
/// <remarks>The buffer is translated into a SecureBufferInternal for the actual call. /// <remarks>The buffer is translated into a SecureBufferInternal for the actual call.
/// To keep the call setup code simple, and to centralize the buffer pinning code, /// To keep the call setup code simple, and to centralize the buffer pinning code,
/// this class stores and returns buffers as regular byte arrays. The buffer /// this class stores and returns buffers as regular byte arrays. The buffer
/// pinning support code in SecureBufferAdapter handles conversion to SecureBufferInternal /// pinning support code in SecureBufferAdapter handles conversion to SecureBufferInternal
/// for pass to the managed api, as well as pinning relevant chunks of memory. /// for pass to the managed api, as well as pinning relevant chunks of memory.
/// ///
/// Furthermore, the native API may not use the entire buffer, and so a mechanism /// Furthermore, the native API may not use the entire buffer, and so a mechanism
/// is needed to communicate the usage of the buffer separate from the length /// is needed to communicate the usage of the buffer separate from the length
/// of the buffer.</remarks> /// of the buffer.</remarks>
@@ -50,7 +46,7 @@ namespace NSspi.Buffers
/// Initializes a new instance of the SecureBuffer class. /// Initializes a new instance of the SecureBuffer class.
/// </summary> /// </summary>
/// <param name="buffer">The buffer to wrap.</param> /// <param name="buffer">The buffer to wrap.</param>
/// <param name="type">The type or purpose of the buffer, for purposes of /// <param name="type">The type or purpose of the buffer, for purposes of
/// invoking the native API.</param> /// invoking the native API.</param>
public SecureBuffer( byte[] buffer, BufferType type ) public SecureBuffer( byte[] buffer, BufferType type )
{ {
@@ -75,4 +71,4 @@ namespace NSspi.Buffers
/// </summary> /// </summary>
public int Length { get; internal set; } public int Length { get; internal set; }
} }
} }

View File

@@ -1,10 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Buffers namespace NSspi.Buffers
{ {
@@ -13,52 +10,52 @@ namespace NSspi.Buffers
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The native APIs consume lists of buffers, with each buffer indicating its type or purpose. /// The native APIs consume lists of buffers, with each buffer indicating its type or purpose.
/// ///
/// The buffers themselves are simple byte arrays, and the native APIs consume arrays of buffers. /// The buffers themselves are simple byte arrays, and the native APIs consume arrays of buffers.
/// ///
/// Since winapi calling convention, perhaps as an extension of C calling convention, does not /// Since winapi calling convention, perhaps as an extension of C calling convention, does not
/// provide a standard convention means of communicating the length of any array, custom structures /// provide a standard convention means of communicating the length of any array, custom structures
/// must be created to carry the buffer length and usage. /// must be created to carry the buffer length and usage.
/// ///
/// Not only does the API need to know how long each buffer is, and how long the array of buffers is, /// Not only does the API need to know how long each buffer is, and how long the array of buffers is,
/// it needs to communicate back how much of each buffer was filled; we may provide it a token buffer /// it needs to communicate back how much of each buffer was filled; we may provide it a token buffer
/// that is 12288 bytes long, but it might only use 125 bytes of that, which we need a way of knowing. /// that is 12288 bytes long, but it might only use 125 bytes of that, which we need a way of knowing.
/// ///
/// As a result of this, the API requires byte arrays to be carried in structs that are natively known as /// As a result of this, the API requires byte arrays to be carried in structs that are natively known as
/// SecureBuffers (known as SecureBufferInternal in this project), and then arrays of SecureBuffers are /// SecureBuffers (known as SecureBufferInternal in this project), and then arrays of SecureBuffers are
/// carried in a SecureBufferDescriptor structure. /// carried in a SecureBufferDescriptor structure.
/// ///
/// As such, this class has to do a significant amount of marshaling work just to get the buffers back and /// As such, this class has to do a significant amount of marshaling work just to get the buffers back and
/// forth to the native APIs. /// forth to the native APIs.
/// * We have to pin all buffers /// * We have to pin all buffers
/// * We have to pin the array of buffers /// * We have to pin the array of buffers
/// * We have to obtain IntPtr handles to each of the buffers and to the array of buffers. /// * We have to obtain IntPtr handles to each of the buffers and to the array of buffers.
/// * Since we provide EasyToUse SecureBuffer classes from the rest of the project, but we /// * Since we provide EasyToUse SecureBuffer classes from the rest of the project, but we
/// provide SecureBufferInternal structures from the native API, we have to copy back values /// provide SecureBufferInternal structures from the native API, we have to copy back values
/// from the SecureBufferInternal structs to our SecureBuffer class. /// from the SecureBufferInternal structs to our SecureBuffer class.
/// ///
/// To make this class easy to use, it accepts either one or many buffers as its constructor; and /// To make this class easy to use, it accepts either one or many buffers as its constructor; and
/// implements IDisposable to know when to marshal values back from the unmanaged structures and to /// implements IDisposable to know when to marshal values back from the unmanaged structures and to
/// release pinned handles. /// release pinned handles.
/// ///
/// Additionally, in case the adapter is leaked without disposing, the adapter implements a Critical /// Additionally, in case the adapter is leaked without disposing, the adapter implements a Critical
/// Finalizer, to ensure that the GCHandles are released, else we will permanently pin handles. /// Finalizer, to ensure that the GCHandles are released, else we will permanently pin handles.
/// ///
/// The typical flow is to take one or many buffers; create and fill the neccessary unmanaged structures; /// The typical flow is to take one or many buffers; create and fill the neccessary unmanaged structures;
/// pin memory; acquire the IntPtr handles; let the caller access the top-level IntPtr representing /// pin memory; acquire the IntPtr handles; let the caller access the top-level IntPtr representing
/// the SecureBufferDescriptor, to provide to the native APIs; wait for the caller to invoke the native /// the SecureBufferDescriptor, to provide to the native APIs; wait for the caller to invoke the native
/// API; wait for the caller to invoke our Dispose; marshal back any data from the native structures /// API; wait for the caller to invoke our Dispose; marshal back any data from the native structures
/// (buffer write counts); release all GCHandles to unpin memory. /// (buffer write counts); release all GCHandles to unpin memory.
/// ///
/// The total descriptor structure is as follows: /// The total descriptor structure is as follows:
/// |-- Descriptor handle /// |-- Descriptor handle
/// |-- Array of buffers /// |-- Array of buffers
/// |-- Buffer 1 /// |-- Buffer 1
/// |-- Buffer 2 /// |-- Buffer 2
/// ... /// ...
/// |-- Buffer N. /// |-- Buffer N.
/// ///
/// Each object in that structure must be pinned and passed as an IntPtr to the native APIs. /// Each object in that structure must be pinned and passed as an IntPtr to the native APIs.
/// All this to pass what boils down to a List of byte arrays.. /// All this to pass what boils down to a List of byte arrays..
/// </remarks> /// </remarks>
internal sealed class SecureBufferAdapter : CriticalFinalizerObject, IDisposable internal sealed class SecureBufferAdapter : CriticalFinalizerObject, IDisposable
@@ -120,7 +117,7 @@ namespace NSspi.Buffers
this.bufferHandles = new GCHandle[this.buffers.Count]; this.bufferHandles = new GCHandle[this.buffers.Count];
this.bufferCarrier = new SecureBufferInternal[this.buffers.Count]; this.bufferCarrier = new SecureBufferInternal[this.buffers.Count];
for ( int i = 0; i < this.buffers.Count; i++ ) for( int i = 0; i < this.buffers.Count; i++ )
{ {
this.bufferHandles[i] = GCHandle.Alloc( this.buffers[i].Buffer, GCHandleType.Pinned ); this.bufferHandles[i] = GCHandle.Alloc( this.buffers[i].Buffer, GCHandleType.Pinned );
@@ -156,7 +153,7 @@ namespace NSspi.Buffers
{ {
get get
{ {
if ( this.disposed ) if( this.disposed )
{ {
throw new ObjectDisposedException( "Cannot use SecureBufferListHandle after it has been disposed" ); throw new ObjectDisposedException( "Cannot use SecureBufferListHandle after it has been disposed" );
} }
@@ -184,11 +181,11 @@ namespace NSspi.Buffers
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
private void Dispose( bool disposing ) private void Dispose( bool disposing )
{ {
if ( this.disposed == true ) { return; } if( this.disposed == true ) { return; }
if ( disposing ) if( disposing )
{ {
// When this class is actually being used for its original purpose - to convey buffers // When this class is actually being used for its original purpose - to convey buffers
// back and forth to SSPI calls - we need to copy the potentially modified structure members // back and forth to SSPI calls - we need to copy the potentially modified structure members
// back to our caller's buffer. // back to our caller's buffer.
for( int i = 0; i < this.buffers.Count; i++ ) for( int i = 0; i < this.buffers.Count; i++ )
@@ -218,4 +215,4 @@ namespace NSspi.Buffers
this.disposed = true; this.disposed = true;
} }
} }
} }

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Buffers namespace NSspi.Buffers
{ {
@@ -22,4 +18,4 @@ namespace NSspi.Buffers
/// </summary> /// </summary>
Network = 0x00 Network = 0x00
} }
} }

View File

@@ -1,10 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Buffers namespace NSspi.Buffers
{ {
@@ -12,7 +7,7 @@ namespace NSspi.Buffers
/// Represents the native layout of the secure buffer descriptor that is provided directly /// Represents the native layout of the secure buffer descriptor that is provided directly
/// to native API calls. /// to native API calls.
/// </summary> /// </summary>
[StructLayout( LayoutKind.Sequential)] [StructLayout( LayoutKind.Sequential )]
internal struct SecureBufferDescInternal internal struct SecureBufferDescInternal
{ {
/// <summary> /// <summary>
@@ -35,4 +30,4 @@ namespace NSspi.Buffers
/// </summary> /// </summary>
public const int ApiVersion = 0; public const int ApiVersion = 0;
} }
} }

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi.Buffers namespace NSspi.Buffers
{ {
@@ -38,7 +34,7 @@ namespace NSspi.Buffers
/// <summary> /// <summary>
/// The buffer passed to an API call contained more data than was necessary for completing the action, /// The buffer passed to an API call contained more data than was necessary for completing the action,
/// such as the case when a streaming-mode connection that does not preserve message bounders, such as TCP /// such as the case when a streaming-mode connection that does not preserve message bounders, such as TCP
/// is used as the transport. The extra data is returned back to the caller in a buffer of this type. /// is used as the transport. The extra data is returned back to the caller in a buffer of this type.
/// </summary> /// </summary>
Extra = 0x05, Extra = 0x05,
@@ -57,7 +53,7 @@ namespace NSspi.Buffers
Stream = 0x0A, Stream = 0x0A,
ChannelBindings = 0x0E, ChannelBindings = 0x0E,
TargetHost = 0x10, TargetHost = 0x10,
ReadOnlyFlag = unchecked( (int)0x80000000 ), ReadOnlyFlag = unchecked((int)0x80000000),
ReadOnlyWithChecksum = 0x10000000 ReadOnlyWithChecksum = 0x10000000
} }
} }

View File

@@ -1,13 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NSspi namespace NSspi
{ {
/* /*
// From winerror.h // From winerror.h
#define SEC_E_OK ((HRESULT)0x00000000L) #define SEC_E_OK ((HRESULT)0x00000000L)
#define SEC_E_INSUFFICIENT_MEMORY _HRESULT_TYPEDEF_(0x80090300L) #define SEC_E_INSUFFICIENT_MEMORY _HRESULT_TYPEDEF_(0x80090300L)
#define SEC_E_INVALID_HANDLE _HRESULT_TYPEDEF_(0x80090301L) #define SEC_E_INVALID_HANDLE _HRESULT_TYPEDEF_(0x80090301L)
@@ -31,8 +27,8 @@ namespace NSspi
/// The request completed successfully /// The request completed successfully
/// </summary> /// </summary>
[EnumString( "No error" )] [EnumString( "No error" )]
OK = 0x00000000, OK = 0x00000000,
/// <summary> /// <summary>
/// The token returned by the context needs to be provided to the cooperating party /// The token returned by the context needs to be provided to the cooperating party
/// to continue construction of the context. /// to continue construction of the context.
@@ -45,7 +41,7 @@ namespace NSspi
/// must call CompleteAuthToken. /// must call CompleteAuthToken.
/// </summary> /// </summary>
[EnumString( "Authentication cycle needs to perform a 'complete'." )] [EnumString( "Authentication cycle needs to perform a 'complete'." )]
CompleteNeeded = 0x00090313, CompleteNeeded = 0x00090313,
/// <summary> /// <summary>
/// Occurs after a client calls InitializeSecurityContext to indicate that the client /// Occurs after a client calls InitializeSecurityContext to indicate that the client
@@ -64,66 +60,65 @@ namespace NSspi
CredentialsNeeded = 0x00090320, CredentialsNeeded = 0x00090320,
[EnumString( "The context data must be re-negotiated with the peer" )] [EnumString( "The context data must be re-negotiated with the peer" )]
Renegotiate = 0x00090321, Renegotiate = 0x00090321,
// Errors // Errors
[EnumString( "Not enough memory.")] [EnumString( "Not enough memory." )]
OutOfMemory = 0x80090300, OutOfMemory = 0x80090300,
[EnumString( "The handle provided to the API was invalid.")] [EnumString( "The handle provided to the API was invalid." )]
InvalidHandle = 0x80090301, InvalidHandle = 0x80090301,
[EnumString( "The attempted operation is not supported")] [EnumString( "The attempted operation is not supported" )]
Unsupported = 0x80090302, Unsupported = 0x80090302,
[EnumString( "The specified principle is not known in the authentication system." )]
TargetUnknown = 0x80090303,
[EnumString( "The specified principle is not known in the authentication system.")]
TargetUnknown = 0x80090303,
[EnumString( "An internal error occurred" )] [EnumString( "An internal error occurred" )]
InternalError = 0x80090304, InternalError = 0x80090304,
/// <summary> /// <summary>
/// No security provider package was found with the given name. /// No security provider package was found with the given name.
/// </summary> /// </summary>
[EnumString( "The requested security package was not found.")] [EnumString( "The requested security package was not found." )]
PackageNotFound = 0x80090305, PackageNotFound = 0x80090305,
NotOwner = 0x80090306, NotOwner = 0x80090306,
CannotInstall = 0x80090307, CannotInstall = 0x80090307,
/// <summary> /// <summary>
/// A token was provided that contained incorrect or corrupted data. /// A token was provided that contained incorrect or corrupted data.
/// </summary> /// </summary>
[EnumString("The provided authentication token is invalid or corrupted.")] [EnumString( "The provided authentication token is invalid or corrupted." )]
InvalidToken = 0x80090308, InvalidToken = 0x80090308,
CannotPack = 0x80090309, CannotPack = 0x80090309,
QopNotSupported = 0x8009030A, QopNotSupported = 0x8009030A,
/// <summary> /// <summary>
/// Impersonation is not supported. /// Impersonation is not supported.
/// </summary> /// </summary>
[EnumString("Impersonation is not supported with the current security package.")] [EnumString( "Impersonation is not supported with the current security package." )]
NoImpersonation = 0x8009030B, NoImpersonation = 0x8009030B,
[EnumString("The logon was denied, perhaps because the provided credentials were incorrect.")] [EnumString( "The logon was denied, perhaps because the provided credentials were incorrect." )]
LogonDenied = 0x8009030C, LogonDenied = 0x8009030C,
[EnumString( "The credentials provided are not recognized by the selected security package." )]
UnknownCredentials = 0x8009030D,
[EnumString( "The credentials provided are not recognized by the selected security package.")] [EnumString( "No credentials are available in the selected security package." )]
UnknownCredentials = 0x8009030D, NoCredentials = 0x8009030E,
[EnumString( "No credentials are available in the selected security package.")]
NoCredentials = 0x8009030E,
[EnumString( "A message that was provided to the Decrypt or VerifySignature functions was altered " + [EnumString( "A message that was provided to the Decrypt or VerifySignature functions was altered " +
"after it was created.")] "after it was created." )]
MessageAltered = 0x8009030F, MessageAltered = 0x8009030F,
[EnumString( "A message was received out of the expected order.")] [EnumString( "A message was received out of the expected order." )]
OutOfSequence = 0x80090310, OutOfSequence = 0x80090310,
[EnumString( "The current security package cannot contact an authenticating authority.")] [EnumString( "The current security package cannot contact an authenticating authority." )]
NoAuthenticatingAuthority = 0x80090311, NoAuthenticatingAuthority = 0x80090311,
/// <summary> /// <summary>
@@ -132,25 +127,26 @@ namespace NSspi
/// <remarks> /// <remarks>
/// This occurs regularly with SSPI contexts that exchange data using a streaming context, /// This occurs regularly with SSPI contexts that exchange data using a streaming context,
/// where the data returned from the streaming communications channel, such as a TCP socket, /// where the data returned from the streaming communications channel, such as a TCP socket,
/// did not contain the complete message. /// did not contain the complete message.
/// Similarly, a streaming channel may return too much data, in which case the API function /// Similarly, a streaming channel may return too much data, in which case the API function
/// will indicate success, but will save off the extra, unrelated data in a buffer of /// will indicate success, but will save off the extra, unrelated data in a buffer of
/// type 'extra'. /// type 'extra'.
/// </remarks> /// </remarks>
IncompleteMessage = 0x80090318, IncompleteMessage = 0x80090318,
IncompleteCredentials = 0x80090320, IncompleteCredentials = 0x80090320,
BufferNotEnough = 0x80090321, BufferNotEnough = 0x80090321,
WrongPrincipal = 0x80090322, WrongPrincipal = 0x80090322,
TimeSkew = 0x80090324, TimeSkew = 0x80090324,
UntrustedRoot = 0x80090325, UntrustedRoot = 0x80090325,
IllegalMessage = 0x80090326, IllegalMessage = 0x80090326,
CertUnknown = 0x80090327, CertUnknown = 0x80090327,
CertExpired = 0x80090328, CertExpired = 0x80090328,
AlgorithmMismatch = 0x80090331, AlgorithmMismatch = 0x80090331,
SecurityQosFailed = 0x80090332, SecurityQosFailed = 0x80090332,
SmartcardLogonRequired = 0x8009033E, SmartcardLogonRequired = 0x8009033E,
UnsupportedPreauth = 0x80090343, UnsupportedPreauth = 0x80090343,
BadBinding = 0x80090346 BadBinding = 0x80090346
} }
/// <summary> /// <summary>
@@ -168,5 +164,4 @@ namespace NSspi
return (uint)status > 0x80000000u; return (uint)status > 0x80000000u;
} }
} }
}
}

View File

@@ -1,31 +1,25 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution; using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using NSspi.Contexts;
namespace NSspi namespace NSspi
{ {
/// <summary> /// <summary>
/// Represents the raw structure for any handle created for the SSPI API, for example, credential /// Represents the raw structure for any handle created for the SSPI API, for example, credential
/// handles, context handles, and security package handles. Any SSPI handle is always the size /// handles, context handles, and security package handles. Any SSPI handle is always the size
/// of two native pointers. /// of two native pointers.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// The documentation for SSPI handles can be found here: /// The documentation for SSPI handles can be found here:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa380495(v=vs.85).aspx /// 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, /// 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. /// 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 /// 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 performed a CER /// to this handle for performing work (InitializeSecurityContext, eg) should be performed a CER
/// that employs handle reference counting across the native API invocation. /// that employs handle reference counting across the native API invocation.
/// </remarks> /// </remarks>
[StructLayout( LayoutKind.Sequential, Pack = 1 ) ] [StructLayout( LayoutKind.Sequential, Pack = 1 )]
internal struct RawSspiHandle internal struct RawSspiHandle
{ {
private IntPtr lowPart; private IntPtr lowPart;
@@ -46,7 +40,7 @@ namespace NSspi
/// <remarks> /// <remarks>
/// This method is executed in a CER during handle release. /// This method is executed in a CER during handle release.
/// </remarks> /// </remarks>
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success)] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
public void SetInvalid() public void SetInvalid()
{ {
this.lowPart = IntPtr.Zero; this.lowPart = IntPtr.Zero;
@@ -69,7 +63,7 @@ namespace NSspi
public override bool IsInvalid public override bool IsInvalid
{ {
get { return IsClosed || this.rawHandle.IsZero(); } get { return IsClosed || this.rawHandle.IsZero(); }
} }
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )] [ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
@@ -79,4 +73,4 @@ namespace NSspi
return true; return true;
} }
} }
} }

View File

@@ -1,14 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace NSspi namespace NSspi
{ {
/// <summary> /// <summary>
/// Represents a Windows API Timestamp structure, which stores time in units of 100 nanosecond /// Represents a Windows API Timestamp structure, which stores time in units of 100 nanosecond
/// ticks, counting from January 1st, year 1601 at 00:00 UTC. Time is stored as a 64-bit value. /// ticks, counting from January 1st, year 1601 at 00:00 UTC. Time is stored as a 64-bit value.
/// </summary> /// </summary>
[StructLayout( LayoutKind.Sequential )] [StructLayout( LayoutKind.Sequential )]
@@ -29,13 +25,13 @@ namespace NSspi
/// <returns></returns> /// <returns></returns>
public DateTime ToDateTime() public DateTime ToDateTime()
{ {
ulong test = (ulong)this.time + (ulong)(Epoch.Ticks); ulong test = (ulong)this.time + (ulong)( Epoch.Ticks );
// Sometimes the value returned is massive, eg, 0x7fffff154e84ffff, which is a value // Sometimes the value returned is massive, eg, 0x7fffff154e84ffff, which is a value
// somewhere in the year 30848. This would overflow DateTime, since it peaks at 31-Dec-9999. // somewhere in the year 30848. This would overflow DateTime, since it peaks at 31-Dec-9999.
// It turns out that this value corresponds to a TimeStamp's maximum value, reduced by my local timezone // It turns out that this value corresponds to a TimeStamp's maximum value, reduced by my local timezone
// http://stackoverflow.com/questions/24478056/ // http://stackoverflow.com/questions/24478056/
if ( test > (ulong)DateTime.MaxValue.Ticks ) if( test > (ulong)DateTime.MaxValue.Ticks )
{ {
return DateTime.MaxValue; return DateTime.MaxValue;
} }
@@ -45,4 +41,4 @@ namespace NSspi
} }
} }
} }
} }

View File

@@ -1,6 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup> </startup>
</configuration> </configuration>

View File

@@ -16,10 +16,10 @@ namespace NSspi
private static void CredTest( string packageName ) private static void CredTest( string packageName )
{ {
ClientCredential clientCred = null; ClientCurrentCredential clientCred = null;
ClientContext client = null; ClientContext client = null;
ServerCredential serverCred = null; ServerCurrentCredential serverCred = null;
ServerContext server = null; ServerContext server = null;
byte[] clientToken; byte[] clientToken;
@@ -30,24 +30,23 @@ namespace NSspi
try try
{ {
clientCred = new ClientCredential( packageName ); clientCred = new ClientCurrentCredential( packageName );
serverCred = new ServerCredential( packageName ); serverCred = new ServerCurrentCredential( packageName );
Console.Out.WriteLine( clientCred.PrincipleName ); Console.Out.WriteLine( clientCred.PrincipleName );
client = new ClientContext( client = new ClientContext(
clientCred, clientCred,
serverCred.PrincipleName, serverCred.PrincipleName,
ContextAttrib.MutualAuth | ContextAttrib.MutualAuth |
ContextAttrib.InitIdentify | ContextAttrib.InitIdentify |
ContextAttrib.Confidentiality | ContextAttrib.Confidentiality |
ContextAttrib.ReplayDetect | ContextAttrib.ReplayDetect |
ContextAttrib.SequenceDetect | ContextAttrib.SequenceDetect |
ContextAttrib.Connection | ContextAttrib.Connection |
ContextAttrib.Delegate ContextAttrib.Delegate
); );
server = new ServerContext( server = new ServerContext(
serverCred, serverCred,
ContextAttrib.MutualAuth | ContextAttrib.MutualAuth |
@@ -64,18 +63,17 @@ namespace NSspi
clientStatus = client.Init( serverToken, out clientToken ); clientStatus = client.Init( serverToken, out clientToken );
while ( true ) while( true )
{ {
serverStatus = server.AcceptToken( clientToken, out serverToken ); serverStatus = server.AcceptToken( clientToken, out serverToken );
if ( serverStatus != SecurityStatus.ContinueNeeded && clientStatus != SecurityStatus.ContinueNeeded ) { break; } if( serverStatus != SecurityStatus.ContinueNeeded && clientStatus != SecurityStatus.ContinueNeeded ) { break; }
clientStatus = client.Init( serverToken, out clientToken ); clientStatus = client.Init( serverToken, out clientToken );
if ( serverStatus != SecurityStatus.ContinueNeeded && clientStatus != SecurityStatus.ContinueNeeded ) { break; } if( serverStatus != SecurityStatus.ContinueNeeded && clientStatus != SecurityStatus.ContinueNeeded ) { break; }
} }
Console.Out.WriteLine( "Server authority: " + server.AuthorityName ); Console.Out.WriteLine( "Server authority: " + server.AuthorityName );
Console.Out.WriteLine( "Server context user: " + server.ContextUserName ); Console.Out.WriteLine( "Server context user: " + server.ContextUserName );
@@ -102,7 +100,7 @@ namespace NSspi
throw new Exception(); throw new Exception();
} }
for( int i= 0; i < plainText.Length; i++ ) for( int i = 0; i < plainText.Length; i++ )
{ {
if( plainText[i] != roundTripPlaintext[i] ) if( plainText[i] != roundTripPlaintext[i] )
{ {
@@ -117,25 +115,23 @@ namespace NSspi
throw new Exception(); throw new Exception();
} }
using( server.ImpersonateClient() ) using( server.ImpersonateClient() )
{ {
} }
cipherText = client.MakeSignature( plainText ); cipherText = client.MakeSignature( plainText );
bool goodSig = server.VerifySignature( cipherText, out roundTripPlaintext ); bool goodSig = server.VerifySignature( cipherText, out roundTripPlaintext );
if ( goodSig == false || if( goodSig == false ||
roundTripPlaintext.Length != plainText.Length ) roundTripPlaintext.Length != plainText.Length )
{ {
throw new Exception(); throw new Exception();
} }
for ( int i = 0; i < plainText.Length; i++ ) for( int i = 0; i < plainText.Length; i++ )
{ {
if ( plainText[i] != roundTripPlaintext[i] ) if( plainText[i] != roundTripPlaintext[i] )
{ {
throw new Exception(); throw new Exception();
} }
@@ -145,12 +141,12 @@ namespace NSspi
} }
finally finally
{ {
if ( server != null ) if( server != null )
{ {
server.Dispose(); server.Dispose();
} }
if ( client != null ) if( client != null )
{ {
client.Dispose(); client.Dispose();
} }
@@ -160,11 +156,11 @@ namespace NSspi
clientCred.Dispose(); clientCred.Dispose();
} }
if ( serverCred != null ) if( serverCred != null )
{ {
serverCred.Dispose(); serverCred.Dispose();
} }
} }
} }
} }
} }

View File

@@ -1,8 +1,7 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle( "NsspiDemo" )] [assembly: AssemblyTitle( "NsspiDemo" )]
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark( "" )] [assembly: AssemblyTrademark( "" )]
[assembly: AssemblyCulture( "" )] [assembly: AssemblyCulture( "" )]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. // COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible( false )] [assembly: ComVisible( false )]
@@ -25,12 +24,12 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values: // Version information for an assembly consists of the following four values:
// //
// Major Version // Major Version
// Minor Version // Minor Version
// Build Number // Build Number
// Revision // Revision
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion( "1.0.0.0" )] [assembly: AssemblyVersion( "1.0.0.0" )]
[assembly: AssemblyFileVersion( "1.0.0.0" )] [assembly: AssemblyFileVersion( "1.0.0.0" )]

View File

@@ -1,6 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup> </startup>
</configuration> </configuration>

View File

@@ -1,12 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using NSspi; using NSspi;
using NSspi.Contexts; using NSspi.Contexts;
@@ -20,7 +14,7 @@ namespace TestClient
public partial class ClientForm : Form public partial class ClientForm : Form
{ {
private ClientContext context; private ClientContext context;
private ClientCredential cred; private ClientCurrentCredential cred;
private CustomConnection connection; private CustomConnection connection;
@@ -48,16 +42,16 @@ namespace TestClient
this.FormClosing += Form1_FormClosing; this.FormClosing += Form1_FormClosing;
// --- SSPI --- // --- SSPI ---
this.cred = new ClientCredential( PackageNames.Negotiate ); this.cred = new ClientCurrentCredential( PackageNames.Negotiate );
this.context = new ClientContext( this.context = new ClientContext(
cred, cred,
"", "",
ContextAttrib.InitIntegrity | ContextAttrib.InitIntegrity |
ContextAttrib.ReplayDetect | ContextAttrib.ReplayDetect |
ContextAttrib.SequenceDetect | ContextAttrib.SequenceDetect |
ContextAttrib.MutualAuth | ContextAttrib.MutualAuth |
ContextAttrib.Delegate | ContextAttrib.Delegate |
ContextAttrib.Confidentiality ContextAttrib.Confidentiality
); );
@@ -107,7 +101,6 @@ namespace TestClient
throw; throw;
} }
} }
} }
} }
@@ -116,7 +109,6 @@ namespace TestClient
this.connection.Stop(); this.connection.Stop();
} }
private void encryptButton_Click( object sender, EventArgs e ) private void encryptButton_Click( object sender, EventArgs e )
{ {
byte[] plaintext; byte[] plaintext;
@@ -149,7 +141,7 @@ namespace TestClient
private void connection_Received( Message message ) private void connection_Received( Message message )
{ {
this.Invoke( (Action)delegate() this.Invoke( (Action)delegate ()
{ {
if( message.Operation == ProtocolOp.ServerToken ) if( message.Operation == ProtocolOp.ServerToken )
{ {
@@ -180,11 +172,11 @@ namespace TestClient
this.initializing = false; this.initializing = false;
this.lastServerToken = null; this.lastServerToken = null;
this.BeginInvoke( (Action)delegate() this.BeginInvoke( (Action)delegate ()
{ {
this.context.Dispose(); this.context.Dispose();
this.context = new ClientContext( this.context = new ClientContext(
this.cred, this.cred,
"", "",
ContextAttrib.InitIntegrity | ContextAttrib.InitIntegrity |
ContextAttrib.ReplayDetect | ContextAttrib.ReplayDetect |
@@ -195,9 +187,9 @@ namespace TestClient
); );
UpdateButtons(); UpdateButtons();
}); } );
} }
private void DoInit() private void DoInit()
{ {
SecurityStatus status; SecurityStatus status;
@@ -253,4 +245,4 @@ namespace TestClient
this.signButton.Enabled = this.connected; this.signButton.Enabled = this.connected;
} }
} }
} }

View File

@@ -1,22 +1,19 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
namespace TestClient namespace TestClient
{ {
static class Program internal static class Program
{ {
/// <summary> /// <summary>
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
static void Main() private static void Main()
{ {
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false ); Application.SetCompatibleTextRenderingDefault( false );
Application.Run( new ClientForm() ); Application.Run( new ClientForm() );
} }
} }
} }

View File

@@ -1,8 +1,7 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle( "TestClient" )] [assembly: AssemblyTitle( "TestClient" )]
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark( "" )] [assembly: AssemblyTrademark( "" )]
[assembly: AssemblyCulture( "" )] [assembly: AssemblyCulture( "" )]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. // COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible( false )] [assembly: ComVisible( false )]
@@ -25,11 +24,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values: // Version information for an assembly consists of the following four values:
// //
// Major Version // Major Version
// Minor Version // Minor Version
// Build Number // Build Number
// Revision // Revision
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion( "1.0.0.0" )] [assembly: AssemblyVersion( "1.0.0.0" )]

View File

@@ -1,6 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup> </startup>
</configuration> </configuration>

View File

@@ -1,10 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using NSspi; using NSspi;
@@ -13,7 +9,7 @@ namespace TestProtocol
public class CustomConnection public class CustomConnection
{ {
private Thread receiveThread; private Thread receiveThread;
private Socket socket; private Socket socket;
private bool running; private bool running;
@@ -24,7 +20,7 @@ namespace TestProtocol
} }
public delegate void ReceivedAction( Message message ); public delegate void ReceivedAction( Message message );
public event ReceivedAction Received; public event ReceivedAction Received;
public event Action Disconnected; public event Action Disconnected;
@@ -33,7 +29,7 @@ namespace TestProtocol
{ {
if( this.running ) if( this.running )
{ {
throw new InvalidOperationException("Already running"); throw new InvalidOperationException( "Already running" );
} }
this.socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp ); this.socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
@@ -65,7 +61,7 @@ namespace TestProtocol
throw new InvalidOperationException( "Not connected" ); throw new InvalidOperationException( "Not connected" );
} }
byte[] outBuffer = new byte[ message.Data.Length + 8 ]; byte[] outBuffer = new byte[message.Data.Length + 8];
int position = 0; int position = 0;
ByteWriter.WriteInt32_BE( (int)message.Operation, outBuffer, position ); ByteWriter.WriteInt32_BE( (int)message.Operation, outBuffer, position );
@@ -124,16 +120,15 @@ namespace TestProtocol
// |--4 bytes--|--4 bytes--|---N--| // |--4 bytes--|--4 bytes--|---N--|
// Every command is a TLV - | Operation | Length | Data | // Every command is a TLV - | Operation | Length | Data |
// Read the operation. // Read the operation.
this.socket.Receive( readBuffer, 4, SocketFlags.None ); this.socket.Receive( readBuffer, 4, SocketFlags.None );
// Check if we popped out of a receive call after we were shut down. // Check if we popped out of a receive call after we were shut down.
if( this.running == false ) { break; } if( this.running == false ) { break; }
operation = (ProtocolOp)ByteWriter.ReadInt32_BE( readBuffer, 0 ); operation = (ProtocolOp)ByteWriter.ReadInt32_BE( readBuffer, 0 );
// Read the length // Read the length
this.socket.Receive( readBuffer, 4, SocketFlags.None ); this.socket.Receive( readBuffer, 4, SocketFlags.None );
messageLength = ByteWriter.ReadInt32_BE( readBuffer, 0 ); messageLength = ByteWriter.ReadInt32_BE( readBuffer, 0 );
@@ -153,14 +148,13 @@ namespace TestProtocol
remaining -= chunkLength; remaining -= chunkLength;
position += chunkLength; position += chunkLength;
} }
} }
catch( SocketException e ) catch( SocketException e )
{ {
if( e.SocketErrorCode == SocketError.ConnectionAborted || if( e.SocketErrorCode == SocketError.ConnectionAborted ||
e.SocketErrorCode == SocketError.Interrupted || e.SocketErrorCode == SocketError.Interrupted ||
e.SocketErrorCode == SocketError.OperationAborted || e.SocketErrorCode == SocketError.OperationAborted ||
e.SocketErrorCode == SocketError.Shutdown || e.SocketErrorCode == SocketError.Shutdown ||
e.SocketErrorCode == SocketError.ConnectionReset ) e.SocketErrorCode == SocketError.ConnectionReset )
{ {
// Shutting down. // Shutting down.
@@ -179,7 +173,7 @@ namespace TestProtocol
byte[] dataCopy = new byte[messageLength]; byte[] dataCopy = new byte[messageLength];
Array.Copy( readBuffer, 0, dataCopy, 0, messageLength ); Array.Copy( readBuffer, 0, dataCopy, 0, messageLength );
Message message = new Message( operation, dataCopy ); Message message = new Message( operation, dataCopy );
try try
{ {
this.Received( message ); this.Received( message );
@@ -187,8 +181,7 @@ namespace TestProtocol
catch( Exception e ) catch( Exception e )
{ } { }
} }
} }
} }
} }
} }

View File

@@ -159,7 +159,7 @@ namespace TestProtocol
operation = (ProtocolOp)ByteWriter.ReadInt32_BE( readBuffer, 0 ); operation = (ProtocolOp)ByteWriter.ReadInt32_BE( readBuffer, 0 );
// Read the length // Read the length
this.readSocket.Receive( readBuffer, 4, SocketFlags.None ); this.readSocket.Receive( readBuffer, 4, SocketFlags.None );
messageLength = ByteWriter.ReadInt32_BE( readBuffer, 0 ); messageLength = ByteWriter.ReadInt32_BE( readBuffer, 0 );
@@ -212,7 +212,6 @@ namespace TestProtocol
catch( Exception ) catch( Exception )
{ } { }
} }
} }
try try
@@ -225,5 +224,4 @@ namespace TestProtocol
catch { } catch { }
} }
} }
}
}

View File

@@ -1,14 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestProtocol namespace TestProtocol
{ {
public class Message public class Message
{ {
public Message(ProtocolOp op, byte[] data) public Message( ProtocolOp op, byte[] data )
{ {
this.Operation = op; this.Operation = op;
this.Data = data; this.Data = data;
@@ -18,4 +14,4 @@ namespace TestProtocol
public byte[] Data { get; private set; } public byte[] Data { get; private set; }
} }
} }

View File

@@ -1,8 +1,7 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle( "TestProtocol" )] [assembly: AssemblyTitle( "TestProtocol" )]
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark( "" )] [assembly: AssemblyTrademark( "" )]
[assembly: AssemblyCulture( "" )] [assembly: AssemblyCulture( "" )]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. // COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible( false )] [assembly: ComVisible( false )]
@@ -25,12 +24,12 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values: // Version information for an assembly consists of the following four values:
// //
// Major Version // Major Version
// Minor Version // Minor Version
// Build Number // Build Number
// Revision // Revision
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion( "1.0.0.0" )] [assembly: AssemblyVersion( "1.0.0.0" )]
[assembly: AssemblyFileVersion( "1.0.0.0" )] [assembly: AssemblyFileVersion( "1.0.0.0" )]

View File

@@ -1,8 +1,4 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestProtocol namespace TestProtocol
{ {
@@ -14,4 +10,4 @@ namespace TestProtocol
EncryptedMessage = 3, EncryptedMessage = 3,
SignedMessage = 4, SignedMessage = 4,
} }
} }

View File

@@ -1,6 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<configuration> <configuration>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup> </startup>
</configuration> </configuration>

View File

@@ -1,22 +1,19 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
namespace TestServer namespace TestServer
{ {
static class Program internal static class Program
{ {
/// <summary> /// <summary>
/// The main entry point for the application. /// The main entry point for the application.
/// </summary> /// </summary>
[STAThread] [STAThread]
static void Main() private static void Main()
{ {
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault( false ); Application.SetCompatibleTextRenderingDefault( false );
Application.Run( new ServerForm() ); Application.Run( new ServerForm() );
} }
} }
} }

View File

@@ -1,8 +1,7 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle( "TestServer" )] [assembly: AssemblyTitle( "TestServer" )]
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark( "" )] [assembly: AssemblyTrademark( "" )]
[assembly: AssemblyCulture( "" )] [assembly: AssemblyCulture( "" )]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type. // COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible( false )] [assembly: ComVisible( false )]
@@ -25,12 +24,12 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values: // Version information for an assembly consists of the following four values:
// //
// Major Version // Major Version
// Minor Version // Minor Version
// Build Number // Build Number
// Revision // Revision
// //
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion( "1.0.0.0" )] [assembly: AssemblyVersion( "1.0.0.0" )]
[assembly: AssemblyFileVersion( "1.0.0.0" )] [assembly: AssemblyFileVersion( "1.0.0.0" )]

View File

@@ -1,11 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using TestProtocol; using TestProtocol;
@@ -19,7 +13,7 @@ namespace TestServer
public partial class ServerForm : Form public partial class ServerForm : Form
{ {
private ServerCredential serverCred; private ServerCurrentCredential serverCred;
private ServerContext serverContext; private ServerContext serverContext;
@@ -35,15 +29,15 @@ namespace TestServer
{ {
InitializeComponent(); InitializeComponent();
this.serverCred = new ServerCredential( PackageNames.Negotiate ); this.serverCred = new ServerCurrentCredential( PackageNames.Negotiate );
this.serverContext = new ServerContext( this.serverContext = new ServerContext(
serverCred, serverCred,
ContextAttrib.AcceptIntegrity | ContextAttrib.AcceptIntegrity |
ContextAttrib.ReplayDetect | ContextAttrib.ReplayDetect |
ContextAttrib.SequenceDetect | ContextAttrib.SequenceDetect |
ContextAttrib.MutualAuth | ContextAttrib.MutualAuth |
ContextAttrib.Delegate | ContextAttrib.Delegate |
ContextAttrib.Confidentiality ContextAttrib.Confidentiality
); );
@@ -129,11 +123,11 @@ namespace TestServer
{ {
MessageBox.Show( "Starting impersonation: " + Environment.UserName ); MessageBox.Show( "Starting impersonation: " + Environment.UserName );
FileStream stream = File.Create( Environment.GetFolderPath( Environment.SpecialFolder.DesktopDirectory) + @"\test.txt" ); FileStream stream = File.Create( Environment.GetFolderPath( Environment.SpecialFolder.DesktopDirectory ) + @"\test.txt" );
StreamWriter writer = new StreamWriter( stream, Encoding.UTF8 ); StreamWriter writer = new StreamWriter( stream, Encoding.UTF8 );
writer.WriteLine( "Hello world." ); writer.WriteLine( "Hello world." );
writer.Close(); writer.Close();
stream.Close(); stream.Close();
} }
@@ -150,7 +144,6 @@ namespace TestServer
this.signButton.Enabled = this.connected; this.signButton.Enabled = this.connected;
} }
private void server_Received( Message message ) private void server_Received( Message message )
{ {
if( message.Operation == ProtocolOp.ClientToken ) if( message.Operation == ProtocolOp.ClientToken )
@@ -177,7 +170,6 @@ namespace TestServer
this.initializing = true; this.initializing = true;
this.connected = false; this.connected = false;
this.serverContext.Dispose(); this.serverContext.Dispose();
this.serverContext = new ServerContext( this.serverContext = new ServerContext(
serverCred, serverCred,
@@ -189,14 +181,13 @@ namespace TestServer
ContextAttrib.Confidentiality ContextAttrib.Confidentiality
); );
this.BeginInvoke( (Action)delegate() this.BeginInvoke( (Action)delegate ()
{ {
UpdateButtons(); UpdateButtons();
this.clientUsernameTextBox.Text = ""; this.clientUsernameTextBox.Text = "";
}); } );
} }
private void HandleInit( Message message ) private void HandleInit( Message message )
{ {
byte[] nextToken; byte[] nextToken;
@@ -218,8 +209,8 @@ namespace TestServer
this.initializing = false; this.initializing = false;
this.connected = true; this.connected = true;
this.Invoke( (Action)delegate() this.Invoke( (Action)delegate ()
{ {
UpdateButtons(); UpdateButtons();
this.clientUsernameTextBox.Text = serverContext.ContextUserName; this.clientUsernameTextBox.Text = serverContext.ContextUserName;
} ); } );
@@ -227,29 +218,28 @@ namespace TestServer
} }
else else
{ {
this.Invoke( (Action)delegate() this.Invoke( (Action)delegate ()
{ {
MessageBox.Show( "Failed to accept token from client. Sspi error code: " + status ); MessageBox.Show( "Failed to accept token from client. Sspi error code: " + status );
} ); } );
} }
} }
} }
private void HandleEncrypted( Message message ) private void HandleEncrypted( Message message )
{ {
this.Invoke( (Action)delegate() this.Invoke( (Action)delegate ()
{ {
byte[] plainText = this.serverContext.Decrypt( message.Data ); byte[] plainText = this.serverContext.Decrypt( message.Data );
string text = Encoding.UTF8.GetString( plainText ); string text = Encoding.UTF8.GetString( plainText );
this.receivedTextbox.Text += "Received encrypted message from client:\r\n" + text + "\r\n"; this.receivedTextbox.Text += "Received encrypted message from client:\r\n" + text + "\r\n";
} ); } );
} }
private void HandleSigned( Message message ) private void HandleSigned( Message message )
{ {
this.Invoke( (Action)delegate() this.Invoke( (Action)delegate ()
{ {
byte[] plainText; byte[] plainText;
@@ -268,11 +258,10 @@ namespace TestServer
private void HandleUnknown( Message message ) private void HandleUnknown( Message message )
{ {
this.Invoke( (Action)delegate() this.Invoke( (Action)delegate ()
{ {
MessageBox.Show( "Received unexpected message from server. Message type: " + message.Operation ); MessageBox.Show( "Received unexpected message from server. Message type: " + message.Operation );
} ); } );
} }
} }
} }

View File

@@ -1,9 +1,12 @@
## Downloads ## ## Downloads ##
The latest release of NSspi is v0.1.1. The latest release of NSspi is v0.2.0.
* [Source](https://github.com/antiduh/nsspi/archive/0.1.1.zip) Version 0.2.0 adds the ability to authenticate using provided username/password credentials.
* [Binaries](https://github.com/antiduh/nsspi/releases/download/0.1.1/nsspi-0.1.1-bin.zip) **Please note** that v0.2.0 introduces a small change in the design that breaks backwards compatibility with previous verisons.
* [Source](https://github.com/antiduh/nsspi/archive/0.2.0.zip)
* [Binaries](https://github.com/antiduh/nsspi/releases/download/0.1.3/nsspi-0.2.0-bin.zip)
* [Nuget package](https://www.nuget.org/packages/NSspi) * [Nuget package](https://www.nuget.org/packages/NSspi)
You can also browse the list of [releases](https://github.com/antiduh/nsspi/releases). You can also browse the list of [releases](https://github.com/antiduh/nsspi/releases).