From 6a5e6056a718f315e012c0e69571079d6fff9937 Mon Sep 17 00:00:00 2001 From: Kevin Thompson Date: Sun, 24 Sep 2017 02:39:49 -0400 Subject: [PATCH] Added password authentication support. Added the ability to acquire a valid credential by providing a username/password combination. --- NSspi/Credentials/AuthData.cs | 55 ++++++++++++++ NSspi/Credentials/CredentialNativeMethods.cs | 15 ++++ NSspi/Credentials/PasswordCredential.cs | 78 ++++++++++++++++++++ NSspi/NSspi.csproj | 2 + 4 files changed, 150 insertions(+) create mode 100644 NSspi/Credentials/AuthData.cs create mode 100644 NSspi/Credentials/PasswordCredential.cs diff --git a/NSspi/Credentials/AuthData.cs b/NSspi/Credentials/AuthData.cs new file mode 100644 index 0000000..bfa3f9b --- /dev/null +++ b/NSspi/Credentials/AuthData.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.InteropServices; + +namespace NSspi.Credentials +{ + /// + /// Provides authentication data in native method calls. + /// + /// + /// Implements the 'SEC_WINNT_AUTH_IDENTITY' structure. See: + /// + /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa380131(v=vs.85).aspx + /// + [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 + } +} \ No newline at end of file diff --git a/NSspi/Credentials/CredentialNativeMethods.cs b/NSspi/Credentials/CredentialNativeMethods.cs index 7db3c58..9b218f9 100644 --- a/NSspi/Credentials/CredentialNativeMethods.cs +++ b/NSspi/Credentials/CredentialNativeMethods.cs @@ -20,6 +20,21 @@ namespace NSspi.Credentials 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 )] [DllImport( "Secur32.dll", EntryPoint = "FreeCredentialsHandle", CharSet = CharSet.Unicode )] internal static extern SecurityStatus FreeCredentialsHandle( diff --git a/NSspi/Credentials/PasswordCredential.cs b/NSspi/Credentials/PasswordCredential.cs new file mode 100644 index 0000000..6ed3613 --- /dev/null +++ b/NSspi/Credentials/PasswordCredential.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; + +namespace NSspi.Credentials +{ + /// + /// Represents credentials acquired by providing a username, password, and domain. + /// + public class PasswordCredential : Credential + { + /// + /// Initializes a new instance of the PasswordCredential class. + /// + /// + /// 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. + /// + /// The domain to authenticate to. + /// The username of the user to authenticate as. + /// The user's password. + /// The SSPI security package to create credentials for. + /// + /// Specify inbound when acquiring credentials for a server; outbound for a client. + /// + 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(); + } + } +} diff --git a/NSspi/NSspi.csproj b/NSspi/NSspi.csproj index 11649bf..b0f5a8c 100644 --- a/NSspi/NSspi.csproj +++ b/NSspi/NSspi.csproj @@ -55,8 +55,10 @@ + +