diff --git a/NSspi/ByteWriter.cs b/NSspi/ByteWriter.cs
index 088e591..16efb36 100644
--- a/NSspi/ByteWriter.cs
+++ b/NSspi/ByteWriter.cs
@@ -7,16 +7,31 @@ using System.Threading.Tasks;
namespace NSspi
{
+ ///
+ /// Reads and writes value types to byte arrays with explicit endianness.
+ ///
public static class ByteWriter
{
// Big endian: Most significant byte at lowest address in memory.
+ ///
+ /// Writes a 2-byte signed integer to the buffer in big-endian format.
+ ///
+ /// The value to write to the buffer.
+ /// The buffer to write to.
+ /// The index of the first byte to write to.
public static void WriteInt16_BE( Int16 value, byte[] buffer, int position )
{
buffer[position + 0] = (byte)( value >> 8 );
buffer[position + 1] = (byte)( value );
}
+ ///
+ /// Writes a 4-byte signed integer to the buffer in big-endian format.
+ ///
+ /// The value to write to the buffer.
+ /// The buffer to write to.
+ /// The index of the first byte to write to.
public static void WriteInt32_BE( Int32 value, byte[] buffer, int position )
{
buffer[position + 0] = (byte)( value >> 24 );
@@ -26,6 +41,13 @@ namespace NSspi
}
+ ///
+ /// Reads a 2-byte signed integer that is stored in the buffer in big-endian format.
+ /// The returned value is in the native endianness.
+ ///
+ /// The buffer to read.
+ /// The index of the first byte to read.
+ ///
public static Int16 ReadInt16_BE( byte[] buffer, int position )
{
Int16 value;
@@ -36,6 +58,13 @@ namespace NSspi
return value;
}
+ ///
+ /// Reads a 4-byte signed integer that is stored in the buffer in big-endian format.
+ /// The returned value is in the native endianness.
+ ///
+ /// The buffer to read.
+ /// The index of the first byte to read.
+ ///
public static Int32 ReadInt32_BE( byte[] buffer, int position )
{
Int32 value;
@@ -47,6 +76,5 @@ namespace NSspi
return value;
}
-
}
}
diff --git a/NSspi/Credentials/QueryNameSupport.cs b/NSspi/Credentials/QueryNameSupport.cs
index 606d313..876c4c3 100644
--- a/NSspi/Credentials/QueryNameSupport.cs
+++ b/NSspi/Credentials/QueryNameSupport.cs
@@ -14,7 +14,7 @@ namespace NSspi.Credentials.Credentials
internal struct QueryNameAttribCarrier
{
///
- /// A pointer to a null-terminated ascii c-string containing the principle name
+ /// A pointer to a null-terminated ascii-encoded containing the principle name
/// associated with a credential
///
public IntPtr Name;
diff --git a/NSspi/Credentials/ServerCredential.cs b/NSspi/Credentials/ServerCredential.cs
index eada90a..d452e6a 100644
--- a/NSspi/Credentials/ServerCredential.cs
+++ b/NSspi/Credentials/ServerCredential.cs
@@ -12,6 +12,11 @@ namespace NSspi.Credentials
///
public class ServerCredential : CurrentCredential
{
+ ///
+ /// Initializes a new instance of the ServerCredential class, acquiring credentials from
+ /// the current thread's security context.
+ ///
+ /// The name of the security package to obtain credentials from.
public ServerCredential( string package )
: base( package, CredentialUse.Inbound )
{
diff --git a/NSspi/NativeMethods.cs b/NSspi/NativeMethods.cs
index 2c0d75d..077020a 100644
--- a/NSspi/NativeMethods.cs
+++ b/NSspi/NativeMethods.cs
@@ -11,20 +11,6 @@ namespace NSspi
{
internal static class NativeMethods
{
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa374713(v=vs.85).aspx
-
- // The REMSSPI sample:
-
- // A C++ pure client/server example:
- // http://msdn.microsoft.com/en-us/library/windows/desktop/aa380536(v=vs.85).aspx
-
-
- /*
- SECURITY_STATUS SEC_Entry FreeContextBuffer(
- _In_ PVOID pvContextBuffer
- );
- */
-
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success)]
[DllImport( "Secur32.dll", EntryPoint = "FreeContextBuffer", CharSet = CharSet.Unicode )]
internal static extern SecurityStatus FreeContextBuffer( IntPtr buffer );
diff --git a/NSspi/PackageNames.cs b/NSspi/PackageNames.cs
index a6b971f..8883c86 100644
--- a/NSspi/PackageNames.cs
+++ b/NSspi/PackageNames.cs
@@ -6,12 +6,24 @@ using System.Threading.Tasks;
namespace NSspi
{
+ ///
+ /// Provides canonical names for security pacakges.
+ ///
public static class PackageNames
{
+ ///
+ /// Indicates the Negotiate security package.
+ ///
public const string Negotiate = "Negotiate";
+ ///
+ /// Indicates the Kerberos security package.
+ ///
public const string Kerberos = "Kerberos";
+ ///
+ /// Indicates the NTLM security package.
+ ///
public const string Ntlm = "NTLM";
}
}
diff --git a/NSspi/PackageSupport.cs b/NSspi/PackageSupport.cs
index 98bc3b1..b0c0a99 100644
--- a/NSspi/PackageSupport.cs
+++ b/NSspi/PackageSupport.cs
@@ -8,8 +8,16 @@ using System.Threading.Tasks;
namespace NSspi
{
+ ///
+ /// Queries information about security packages.
+ ///
public static class PackageSupport
{
+ ///
+ /// Returns the properties of the named package.
+ ///
+ /// The name of the package.
+ ///
public static SecPkgInfo GetPackageCapabilities( string packageName )
{
SecPkgInfo info;
@@ -52,6 +60,10 @@ namespace NSspi
return info;
}
+ ///
+ /// Returns a list of all known security package providers and their properties.
+ ///
+ ///
public static SecPkgInfo[] EnumeratePackages()
{
SecurityStatus status = SecurityStatus.InternalError;
diff --git a/NSspi/SecureBuffer/SecureBuffer.cs b/NSspi/SecureBuffer/SecureBuffer.cs
index 72c3bd8..3a66ba9 100644
--- a/NSspi/SecureBuffer/SecureBuffer.cs
+++ b/NSspi/SecureBuffer/SecureBuffer.cs
@@ -7,19 +7,51 @@ using System.Threading.Tasks;
namespace NSspi.Buffers
{
+ ///
+ /// Represents a native SecureBuffer structure, which is used for communicating
+ /// buffers to the native APIs.
+ ///
[StructLayout( LayoutKind.Sequential )]
internal struct SecureBufferInternal
{
+ ///
+ /// When provided to the native API, the total number of bytes available in the buffer.
+ /// On return from the native API, the number of bytes that were filled or used by the
+ /// native API.
+ ///
public int Count;
+ ///
+ /// The type or purpose of the buffer.
+ ///
public BufferType Type;
- // A pointer to a byte[]
+ ///
+ /// An pointer to a pinned byte[] buffer.
+ ///
public IntPtr Buffer;
}
+ ///
+ /// Stores buffers to provide tokens and data to the native SSPI APIs.
+ ///
+ /// 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,
+ /// this class stores and returns buffers as regular byte arrays. The buffer
+ /// pinning support code in SecureBufferAdapter handles conversion to SecureBufferInternal
+ /// 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
+ /// is needed to communicate the usage of the buffer separate from the length
+ /// of the buffer.
internal class SecureBuffer
{
+ ///
+ /// Initializes a new instance of the SecureBuffer class.
+ ///
+ /// The buffer to wrap.
+ /// The type or purpose of the buffer, for purposes of
+ /// invoking the native API.
public SecureBuffer( byte[] buffer, BufferType type )
{
this.Buffer = buffer;
@@ -27,10 +59,20 @@ namespace NSspi.Buffers
this.Length = this.Buffer.Length;
}
+ ///
+ /// The type or purposes of the API, for invoking the native API.
+ ///
public BufferType Type { get; set; }
+ ///
+ /// The buffer to provide to the native API.
+ ///
public byte[] Buffer { get; set; }
+ ///
+ /// The number of elements that were actually filled or used by the native API,
+ /// which may be less than the total length of the buffer.
+ ///
public int Length { get; internal set; }
}
}
diff --git a/NSspi/SecureBuffer/SecureBufferAdapter.cs b/NSspi/SecureBuffer/SecureBufferAdapter.cs
index 5bda383..d218005 100644
--- a/NSspi/SecureBuffer/SecureBufferAdapter.cs
+++ b/NSspi/SecureBuffer/SecureBufferAdapter.cs
@@ -8,26 +8,109 @@ using System.Threading.Tasks;
namespace NSspi.Buffers
{
+ ///
+ /// Prepares SecureBuffers for providing them to native API calls.
+ ///
+ ///
+ /// 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.
+ ///
+ /// 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
+ /// 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,
+ /// 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.
+ ///
+ /// 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
+ /// 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
+ /// forth to the native APIs.
+ /// * We have to pin all 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.
+ /// * 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
+ /// 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
+ /// implements IDisposable to know when to marshal values back from the unmanaged structures and to
+ /// release pinned handles.
+ ///
+ /// 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.
+ ///
+ /// 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
+ /// 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
+ /// (buffer write counts); release all GCHandles to unpin memory.
+ ///
+ /// The total descriptor structure is as follows:
+ /// |-- Descriptor handle
+ /// |-- Array of buffers
+ /// |-- Buffer 1
+ /// |-- Buffer 2
+ /// ...
+ /// |-- Buffer N.
+ ///
+ /// 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..
+ ///
internal sealed class SecureBufferAdapter : CriticalFinalizerObject, IDisposable
{
+ ///
+ /// Whether the adapter has already been disposed.
+ ///
private bool disposed;
+ ///
+ /// The list of mananged SecureBuffers the caller provided to us.
+ ///
private IList buffers;
+ ///
+ /// The top level handle representing the entire descriptor.
+ ///
private GCHandle descriptorHandle;
- private GCHandle[] bufferHandles;
-
- private SecureBufferDescInternal descriptor;
- private SecureBufferInternal[] bufferCarrier;
+ ///
+ /// The handle representing the array of buffers.
+ ///
private GCHandle bufferCarrierHandle;
+ ///
+ /// The handles representing each actual buffer.
+ ///
+ private GCHandle[] bufferHandles;
+
+ ///
+ /// The native buffer descriptor
+ ///
+ private SecureBufferDescInternal descriptor;
+
+ ///
+ /// An array of the native buffers.
+ ///
+ private SecureBufferInternal[] bufferCarrier;
+
+ ///
+ /// Initializes a SecureBufferAdapter to carry a single buffer to the native api.
+ ///
+ ///
public SecureBufferAdapter( SecureBuffer buffer )
: this( new[] { buffer } )
{
-
}
+ ///
+ /// Initializes the SecureBufferAdapter to carry a list of buffers to the native api.
+ ///
+ ///
public SecureBufferAdapter( IList buffers ) : base()
{
this.buffers = buffers;
@@ -66,6 +149,9 @@ namespace NSspi.Buffers
Dispose( false );
}
+ ///
+ /// Gets the top-level pointer to the secure buffer descriptor to pass to the native API.
+ ///
public IntPtr Handle
{
get
@@ -79,12 +165,22 @@ namespace NSspi.Buffers
}
}
+ ///
+ /// Completes any buffer passing marshaling and releases all resources associated with the adapter.
+ ///
public void Dispose()
{
this.Dispose( true );
GC.SuppressFinalize( this );
}
+ ///
+ /// Completes any buffer passing marshaling and releases all resources associated with the adapter.
+ /// This may be called by the finalizer, or by the regular Dispose method. In the case of the finalizer,
+ /// we've been leaked and there's no point in attempting to marshal back data from the native structures,
+ /// nor should we anyway since they may be gone.
+ ///
+ /// Whether Dispose is being called.
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success )]
private void Dispose( bool disposing )
{
@@ -121,6 +217,5 @@ namespace NSspi.Buffers
this.disposed = true;
}
-
}
}
diff --git a/NSspi/SecureBuffer/SecureBufferDataRep.cs b/NSspi/SecureBuffer/SecureBufferDataRep.cs
index ebd13f4..d12796f 100644
--- a/NSspi/SecureBuffer/SecureBufferDataRep.cs
+++ b/NSspi/SecureBuffer/SecureBufferDataRep.cs
@@ -6,13 +6,20 @@ using System.Threading.Tasks;
namespace NSspi.Buffers
{
+ ///
+ /// Describes how a buffer's opaque internals should be stored, with regards to byte ordering.
+ ///
internal enum SecureBufferDataRep : int
{
- /*
- #define SECURITY_NATIVE_DREP 0x00000010
- #define SECURITY_NETWORK_DREP 0x00000000
- */
- Nativee = 0x10,
+ ///
+ /// Buffers internals are to be stored in the machine native byte order, which will change depending on
+ /// what machine generated the buffer.
+ ///
+ Native = 0x10,
+
+ ///
+ /// Buffers are stored in network byte ordering, that is, big endian format.
+ ///
Network = 0x00
}
}
diff --git a/NSspi/SecureBuffer/SecureBufferDesc.cs b/NSspi/SecureBuffer/SecureBufferDesc.cs
index 9a7195d..4f9e3ad 100644
--- a/NSspi/SecureBuffer/SecureBufferDesc.cs
+++ b/NSspi/SecureBuffer/SecureBufferDesc.cs
@@ -8,15 +8,31 @@ using System.Threading.Tasks;
namespace NSspi.Buffers
{
+ ///
+ /// Represents the native layout of the secure buffer descriptor that is provided directly
+ /// to native API calls.
+ ///
[StructLayout( LayoutKind.Sequential)]
internal struct SecureBufferDescInternal
{
+ ///
+ /// The buffer structure version.
+ ///
public int Version;
+
+ ///
+ /// The number of buffers represented by this descriptor.
+ ///
public int NumBuffers;
- // A pointer to a SecureBuffer[]
+ ///
+ /// A pointer to a array of buffers, where each buffer is a byte[].
+ ///
public IntPtr Buffers;
+ ///
+ /// Indicates the buffer structure version supported by this structure. Always 0.
+ ///
public const int ApiVersion = 0;
}
}
diff --git a/NSspi/SecureBuffer/SecureBufferType.cs b/NSspi/SecureBuffer/SecureBufferType.cs
index 808a4f1..a3e181a 100644
--- a/NSspi/SecureBuffer/SecureBufferType.cs
+++ b/NSspi/SecureBuffer/SecureBufferType.cs
@@ -6,16 +6,53 @@ using System.Threading.Tasks;
namespace NSspi.Buffers
{
+ ///
+ /// Describes the type and purpose of a secure buffer passed to the native API.
+ ///
internal enum BufferType : int
{
+ ///
+ /// The buffer is empty.
+ ///
Empty = 0x00,
+
+ ///
+ /// The buffer contains message data. Message data can be plaintext or cipher text data.
+ ///
Data = 0x01,
+
+ ///
+ /// The buffer contains opaque authentication token data.
+ ///
Token = 0x02,
+
+ ///
+ /// The buffer contains parameters specific to the security package.
+ ///
Parameters = 0x03,
+
+ ///
+ /// The buffer placeholder indicating that some data is missing.
+ ///
Missing = 0x04,
+
+ ///
+ /// 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
+ /// is used as the transport. The extra data is returned back to the caller in a buffer of this type.
+ ///
Extra = 0x05,
+
+ ///
+ /// The buffer contains a security data trailer, such as a message signature or marker, or framing data.
+ ///
Trailer = 0x06,
+
+ ///
+ /// The buffer contains a security data header, such as a message signature, marker, or framing data.
+ ///
Header = 0x07,
+
Padding = 0x09,
Stream = 0x0A,
ChannelBindings = 0x0E,
diff --git a/NSspi/SecurityStatus.cs b/NSspi/SecurityStatus.cs
index bb86ed7..754ad11 100644
--- a/NSspi/SecurityStatus.cs
+++ b/NSspi/SecurityStatus.cs
@@ -60,7 +60,12 @@ namespace NSspi
Unsupported = 0x80090302,
TargetUnknown = 0x80090303,
InternalError = 0x80090304,
+
+ ///
+ /// No security provider package was found with the given name.
+ ///
PackageNotFound = 0x80090305,
+
NotOwner = 0x80090306,
CannotInstall = 0x80090307,
InvalidToken = 0x80090308,
@@ -73,6 +78,18 @@ namespace NSspi
MessageAltered = 0x8009030F,
OutOfSequence = 0x80090310,
NoAuthenticatingAuthority = 0x80090311,
+
+ ///
+ /// The buffer provided to an SSPI API call contained a message that was not complete.
+ ///
+ ///
+ /// 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,
+ /// did not contain the complete message.
+ /// 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
+ /// type 'extra'.
+ ///
IncompleteMessage = 0x80090318,
IncompleteCredentials = 0x80090320,
BufferNotEnough = 0x80090321,
@@ -89,8 +106,16 @@ namespace NSspi
BadBinding = 0x80090346
}
+ ///
+ /// Provides extension methods for the SecurityStatus enumeration.
+ ///
public static class SecurityStatusExtensions
{
+ ///
+ /// Returns whether or not the status represents an error.
+ ///
+ ///
+ /// True if the status represents an error condition.
public static bool IsError( this SecurityStatus status )
{
return (uint)status > 0x80000000u;
diff --git a/NSspi/SspiHandle.cs b/NSspi/SspiHandle.cs
index 9000774..d45ee35 100644
--- a/NSspi/SspiHandle.cs
+++ b/NSspi/SspiHandle.cs
@@ -11,8 +11,9 @@ using NSspi.Contexts;
namespace NSspi
{
///
- /// Represents any SSPI handle created for credential handles, context handles, and security package
- /// handles. Any SSPI handle is always the size of two native pointers.
+ /// 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
+ /// of two native pointers.
///
///
/// The documentation for SSPI handles can be found here:
@@ -21,8 +22,8 @@ namespace NSspi
/// This class is not reference safe - if used directly, or referenced directly, it may be leaked,
/// or subject to finalizer races, or any of the hundred of things SafeHandles were designed to fix.
/// Do not directly use this class - use only though SafeHandle wrapper objects. Any reference needed
- /// to this handle for performing work (InitializeSecurityContext, eg), should be done through
- /// a second class SafeSspiHandleReference so that reference counting is properly executed.
+ /// to this handle for performing work (InitializeSecurityContext, eg) should be performed a CER
+ /// that employs handle reference counting across the native API invocation.
///
[StructLayout( LayoutKind.Sequential, Pack = 1 ) ]
internal struct RawSspiHandle
@@ -30,12 +31,21 @@ namespace NSspi
private IntPtr lowPart;
private IntPtr highPart;
+ ///
+ /// Returns whether or not the handle is set to the default, empty value.
+ ///
+ ///
public bool IsZero()
{
return this.lowPart == IntPtr.Zero && this.highPart == IntPtr.Zero;
}
- // This guy has to be executed in a CER.
+ ///
+ /// Sets the handle to an invalid value.
+ ///
+ ///
+ /// This method is executed in a CER during handle release.
+ ///
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success)]
public void SetInvalid()
{
@@ -44,7 +54,9 @@ namespace NSspi
}
}
-
+ ///
+ /// Safely encapsulates a raw handle used in the SSPI api.
+ ///
public abstract class SafeSspiHandle : SafeHandle
{
internal RawSspiHandle rawHandle;
diff --git a/NSspi/TimeStamp.cs b/NSspi/TimeStamp.cs
index 1521545..9c80c6d 100644
--- a/NSspi/TimeStamp.cs
+++ b/NSspi/TimeStamp.cs
@@ -7,19 +7,33 @@ using System.Threading.Tasks;
namespace NSspi
{
+ ///
+ /// 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.
+ ///
[StructLayout( LayoutKind.Sequential )]
public struct TimeStamp
{
public static readonly DateTime Epoch = new DateTime( 1601, 1, 1, 0, 0, 0, DateTimeKind.Utc );
+ ///
+ /// Stores the time value. Infinite times are often represented as values near, but not exactly
+ /// at the maximum signed 64-bit 2's complement value.
+ ///
private long time;
+ ///
+ /// Converts the TimeStamp to an equivalant DateTime object. If the TimeStamp represents
+ /// a value larger than DateTime.MaxValue, then DateTime.MaxValue is returned.
+ ///
+ ///
public DateTime ToDateTime()
{
ulong test = (ulong)this.time + (ulong)(Epoch.Ticks);
// 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.
+ // It turns out that this value corresponds to a TimeStamp's maximum value, reduced by my local timezone
// http://stackoverflow.com/questions/24478056/
if ( test > (ulong)DateTime.MaxValue.Ticks )
{