From 9636bb44b5d3ba7fdf978d189d2607bfcb4f7d76 Mon Sep 17 00:00:00 2001 From: antiduh Date: Thu, 3 Jul 2014 03:24:16 +0000 Subject: [PATCH] Updated comments. --- NSspi/ByteWriter.cs | 30 +++++- NSspi/Credentials/QueryNameSupport.cs | 2 +- NSspi/Credentials/ServerCredential.cs | 5 + NSspi/NativeMethods.cs | 14 --- NSspi/PackageNames.cs | 12 +++ NSspi/PackageSupport.cs | 12 +++ NSspi/SecureBuffer/SecureBuffer.cs | 44 ++++++++- NSspi/SecureBuffer/SecureBufferAdapter.cs | 107 ++++++++++++++++++++-- NSspi/SecureBuffer/SecureBufferDataRep.cs | 17 +++- NSspi/SecureBuffer/SecureBufferDesc.cs | 18 +++- NSspi/SecureBuffer/SecureBufferType.cs | 37 ++++++++ NSspi/SecurityStatus.cs | 25 +++++ NSspi/SspiHandle.cs | 24 +++-- NSspi/TimeStamp.cs | 14 +++ 14 files changed, 326 insertions(+), 35 deletions(-) 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 ) {