Skip to content

Commit

Permalink
Updated Windows Credential, added Console credential prompts
Browse files Browse the repository at this point in the history
  • Loading branch information
mvadu committed Jan 20, 2016
1 parent aa0eebe commit b1a7f3f
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 169 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
<ItemGroup>
<Compile Include="Credential.cs" />
<Compile Include="CredentialManager.cs" />
<Compile Include="NativeStructs.cs" />
<Compile Include="NativeCode.cs" />
<Compile Include="CriticalCredentialHandle.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
Expand Down
16 changes: 8 additions & 8 deletions AdysTech.CredentialManager/Credential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ namespace AdysTech.CredentialManager
internal class Credential
{
public UInt32 Flags;
public NativeStructs.CredentialType Type;
public NativeCode.CredentialType Type;
public string TargetName;
public string Comment;
public DateTime LastWritten;
public UInt32 CredentialBlobSize;
public string CredentialBlob;
public NativeStructs.Persistance Persist;
public NativeCode.Persistance Persist;
public UInt32 AttributeCount;
public IntPtr Attributes;
public string TargetAlias;
Expand All @@ -27,7 +27,7 @@ public Credential()

}

internal Credential(NativeStructs.NativeCredential ncred)
internal Credential(NativeCode.NativeCredential ncred)
{
CredentialBlobSize = ncred.CredentialBlobSize;
CredentialBlob = Marshal.PtrToStringUni (ncred.CredentialBlob,
Expand All @@ -37,7 +37,7 @@ internal Credential(NativeStructs.NativeCredential ncred)
TargetAlias = Marshal.PtrToStringUni (ncred.TargetAlias);
Type = ncred.Type;
Flags = ncred.Flags;
Persist = (NativeStructs.Persistance) ncred.Persist;
Persist = (NativeCode.Persistance) ncred.Persist;
LastWritten = DateTime.FromFileTime ((long) ( (ulong) ncred.LastWritten.dwHighDateTime << 32 | (ulong) ncred.LastWritten.dwLowDateTime ));
}

Expand All @@ -50,8 +50,8 @@ public Credential(System.Net.NetworkCredential credential)
Attributes = IntPtr.Zero;
Comment = null;
TargetAlias = null;
Type = NativeStructs.CredentialType.GENERIC;
Persist = NativeStructs.Persistance.SESSION;
Type = NativeCode.CredentialType.Generic;
Persist = NativeCode.Persistance.Session;
}

/// <summary>
Expand All @@ -60,9 +60,9 @@ public Credential(System.Net.NetworkCredential credential)
/// <param name="cred">The managed Credential counterpart containing data to be stored.</param>
/// <returns>A NativeCredential instance that is derived from the given Credential
/// instance.</returns>
internal NativeStructs.NativeCredential GetNativeCredential()
internal NativeCode.NativeCredential GetNativeCredential()
{
NativeStructs.NativeCredential ncred = new NativeStructs.NativeCredential ();
NativeCode.NativeCredential ncred = new NativeCode.NativeCredential ();
ncred.AttributeCount = 0;
ncred.Attributes = IntPtr.Zero;
ncred.Comment = IntPtr.Zero;
Expand Down
162 changes: 126 additions & 36 deletions AdysTech.CredentialManager/CredentialManager.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System;
using System.ComponentModel;
using System.Linq;
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace AdysTech.CredentialManager
{
Expand All @@ -20,14 +17,15 @@ public static class CredentialManager
/// Opens OS Version specific Window prompting for credentials
/// </summary>
/// <param name="Target">A descriptive text for where teh credentials being asked are used for</param>
/// <param name="save">Whether or not to offer the checkbox to save the credentials</param>
/// <returns>NetworkCredential object containing the user name, </returns>
public static NetworkCredential PromptForCredentials(string Target)
public static NetworkCredential PromptForCredentials(string Target, ref bool save)
{
var username = String.Empty;
var passwd = String.Empty;
var domain = String.Empty;

if ( !PromptForCredentials (Target, out username, out passwd, out domain) )
if ( !PromptForCredentials (Target, ref save, out username, out passwd, out domain) )
return null;
return new NetworkCredential (username, passwd, domain);
}
Expand All @@ -36,73 +34,165 @@ public static NetworkCredential PromptForCredentials(string Target)
/// Opens OS Version specific Window prompting for credentials
/// </summary>
/// <param name="Target">A descriptive text for where teh credentials being asked are used for</param>
/// <param name="save">Whether or not to offer the checkbox to save the credentials</param>
/// <param name="Message">A brief message to display in the dialog box</param>
/// <param name="Caption">Title for the dialog box</param>
/// <returns>NetworkCredential object containing the user name, </returns>
public static NetworkCredential PromptForCredentials(string Target, string Message, string Caption)
public static NetworkCredential PromptForCredentials(string Target, ref bool save, string Message, string Caption)
{
var username = String.Empty;
var passwd = String.Empty;
var domain = String.Empty;

if ( !PromptForCredentials (Target, Message, Caption, out username, out passwd, out domain) )
if ( !PromptForCredentials (Target, ref save, Message, Caption, out username, out passwd, out domain) )
return null;
return new NetworkCredential (username, passwd, domain);
}

internal static bool PromptForCredentials(string target, out string user, out string password, out string domain)
internal static bool PromptForCredentials(string target, ref bool save, out string user, out string password, out string domain)
{
return PromptForCredentials (target, new NativeStructs.CredentialUIInfo (), out user, out password, out domain);
return PromptForCredentials (target, new NativeCode.CredentialUIInfo (), ref save, out user, out password, out domain);
}

internal static bool PromptForCredentials(string target, string Message, string Caption, out string user, out string password, out string domain)
internal static bool PromptForCredentials(string target, ref bool save, string Message, string Caption, out string user, out string password, out string domain)
{
NativeStructs.CredentialUIInfo credUI = new NativeStructs.CredentialUIInfo ();
NativeCode.CredentialUIInfo credUI = new NativeCode.CredentialUIInfo ();
credUI.pszMessageText = Message;
credUI.pszCaptionText = Caption;
return PromptForCredentials (target, credUI, out user, out password, out domain);
return PromptForCredentials (target, credUI, ref save, out user, out password, out domain);
}

private static bool PromptForCredentials(string target, NativeStructs.CredentialUIInfo credUI, out string user, out string password, out string domain)
private static bool PromptForCredentials(string target, NativeCode.CredentialUIInfo credUI, ref bool save, out string user, out string password, out string domain)
{
user = String.Empty;
password = String.Empty;
domain = String.Empty;



// Setup the flags and variables
StringBuilder userPassword = new StringBuilder (), userID = new StringBuilder ();
credUI.cbSize = Marshal.SizeOf (credUI);
int errorcode = 0;
uint dialogReturn;
uint authPackage = 0;

IntPtr outCredBuffer = new IntPtr ();
uint outCredSize;
var flags = NativeCode.PromptForWindowsCredentialsFlags.GenericCredentials | NativeCode.PromptForWindowsCredentialsFlags.EnumerateCurrentUser;
flags = save ? flags | NativeCode.PromptForWindowsCredentialsFlags.ShowCheckbox : flags;

// Setup the flags and variables
int result = NativeCode.CredUIPromptForWindowsCredentials (ref credUI,
errorcode,
ref authPackage,
IntPtr.Zero,
0,
out outCredBuffer,
out outCredSize,
ref save,
flags);

var usernameBuf = new StringBuilder (100);
var passwordBuf = new StringBuilder (100);
var domainBuf = new StringBuilder (100);

int maxUserName = 100;
int maxDomain = 100;
int maxPassword = 100;
if ( result == 0 )
{
if ( NativeCode.CredUnPackAuthenticationBuffer (0, outCredBuffer, outCredSize, usernameBuf, ref maxUserName,
domainBuf, ref maxDomain, passwordBuf, ref maxPassword) )
{
user = usernameBuf.ToString ();
password = passwordBuf.ToString ();
domain = domainBuf.ToString ();
if ( String.IsNullOrWhiteSpace (domain) )
{
Debug.WriteLine ("Domain null");
if ( !ParseUserName (usernameBuf.ToString (), maxUserName, maxDomain, out user, out password) )
user = usernameBuf.ToString ();
}
}

//mimic SecureZeroMem function to make sure buffer is zeroed out. SecureZeroMem is not an exported function, neither is RtlSecureZeroMemory
var zeroBytes = new byte[outCredSize];
Marshal.Copy (zeroBytes, 0, outCredBuffer, (int) outCredSize);

//clear the memory allocated by CredUIPromptForWindowsCredentials
NativeCode.CoTaskMemFree (outCredBuffer);
return true;
}

user = null;
domain = null;
return false;
}

private static bool ParseUserName(string usernameBuf, int maxUserName, int maxDomain, out string user, out string domain)
{
StringBuilder userBuilder = new StringBuilder ();
StringBuilder domainBuilder = new StringBuilder ();
user = String.Empty;
domain = String.Empty;

var returnCode = NativeCode.CredUIParseUserName (usernameBuf, userBuilder, maxUserName, domainBuilder, maxDomain);
Debug.WriteLine (returnCode);
switch ( returnCode )
{
case NativeCode.CredentialUIReturnCodes.Success: // The username is valid.
user = userBuilder.ToString ();
domain = domainBuilder.ToString ();
return true;
}
return false;
}

/// <summary>
/// Accepts credentials in a console window
/// </summary>
/// <param name="Target">A descriptive text for where teh credentials being asked are used for</param>
/// <returns>NetworkCredential object containing the user name, </returns>
public static NetworkCredential PromptForCredentialsConsole(string target)
{
var user = String.Empty;
var password = String.Empty;
var domain = String.Empty;

// Setup the flags and variables
StringBuilder userPassword = new StringBuilder (), userID = new StringBuilder ();
bool save = true;
NativeStructs.CredentialUIFlags flags = NativeStructs.CredentialUIFlags.COMPLETE_USERNAME | NativeStructs.CredentialUIFlags.PERSIST | NativeStructs.CredentialUIFlags.EXCLUDE_CERTIFICATES;
NativeCode.CredentialUIFlags flags = NativeCode.CredentialUIFlags.CompleteUsername | NativeCode.CredentialUIFlags.ExcludeCertificates;

// Prompt the user
NativeStructs.CredentialUIReturnCodes returnCode = NativeStructs.CredUIPromptForCredentials (ref credUI, target, IntPtr.Zero, 0, userID, 100, userPassword, 100, ref save, flags);
NativeCode.CredentialUIReturnCodes returnCode = NativeCode.CredUICmdLinePromptForCredentials (target, IntPtr.Zero, 0, userID, 100, userPassword, 100, ref save, flags);

password = userPassword.ToString ();

StringBuilder userBuilder = new StringBuilder ();
StringBuilder domainBuilder = new StringBuilder ();

returnCode = NativeStructs.CredUIParseUserName (userID.ToString (), userBuilder, int.MaxValue, domainBuilder, int.MaxValue);
returnCode = NativeCode.CredUIParseUserName (userID.ToString (), userBuilder, int.MaxValue, domainBuilder, int.MaxValue);
switch ( returnCode )
{
case NativeStructs.CredentialUIReturnCodes.NO_ERROR: // The username is valid.
case NativeCode.CredentialUIReturnCodes.Success: // The username is valid.
user = userBuilder.ToString ();
domain = domainBuilder.ToString ();
return true;
break;

case NativeStructs.CredentialUIReturnCodes.ERROR_INVALID_ACCOUNT_NAME: // The username is not valid.
case NativeCode.CredentialUIReturnCodes.InvalidAccountName: // The username is not valid.
user = userID.ToString ();
domain = null;
return false;
break;

case NativeStructs.CredentialUIReturnCodes.ERROR_INSUFFICIENT_BUFFER: // One of the buffers is too small.
case NativeCode.CredentialUIReturnCodes.InsufficientBuffer: // One of the buffers is too small.
throw new OutOfMemoryException ();

case NativeStructs.CredentialUIReturnCodes.ERROR_INVALID_PARAMETER: // ulUserMaxChars or ulDomainMaxChars is zero OR userName, user, or domain is NULL.
case NativeCode.CredentialUIReturnCodes.InvalidParameter: // ulUserMaxChars or ulDomainMaxChars is zero OR userName, user, or domain is NULL.
throw new ArgumentNullException ("userName");

default:
user = null;
domain = null;
return false;
}
return new NetworkCredential (user, password, domain);
}


Expand All @@ -118,10 +208,10 @@ public static bool SaveCredentials(string Target, NetworkCredential credential)
// Go ahead with what we have are stuff it into the CredMan structures.
Credential cred = new Credential (credential);
cred.TargetName = Target;
cred.Persist = NativeStructs.Persistance.ENTERPRISE;
NativeStructs.NativeCredential ncred = cred.GetNativeCredential ();
cred.Persist = NativeCode.Persistance.Entrprise;
NativeCode.NativeCredential ncred = cred.GetNativeCredential ();
// Write the info into the CredMan storage.
bool written = NativeStructs.CredWrite (ref ncred, 0);
bool written = NativeCode.CredWrite (ref ncred, 0);
int lastError = Marshal.GetLastWin32Error ();
if ( written )
{
Expand All @@ -148,7 +238,7 @@ public static NetworkCredential GetCredentials(string Target)
var domain = String.Empty;

// Make the API call using the P/Invoke signature
bool ret = NativeStructs.CredRead (Target, NativeStructs.CredentialType.GENERIC, 0, out nCredPtr);
bool ret = NativeCode.CredRead (Target, NativeCode.CredentialType.Generic, 0, out nCredPtr);
int lastError = Marshal.GetLastWin32Error ();
if ( !ret )
throw new Win32Exception (lastError, "CredDelete throw an error");
Expand All @@ -163,12 +253,12 @@ public static NetworkCredential GetCredentials(string Target)
var user = cred.UserName;
StringBuilder userBuilder = new StringBuilder ();
StringBuilder domainBuilder = new StringBuilder ();
var ret1 = NativeStructs.CredUIParseUserName (user, userBuilder, int.MaxValue, domainBuilder, int.MaxValue);
var ret1 = NativeCode.CredUIParseUserName (user, userBuilder, int.MaxValue, domainBuilder, int.MaxValue);
lastError = Marshal.GetLastWin32Error ();

//assuming invalid account name to be not meeting condition for CredUIParseUserName
//"The name must be in UPN or down-level format, or a certificate"
if ( ret1 == NativeStructs.CredentialUIReturnCodes.ERROR_INVALID_ACCOUNT_NAME )
if ( ret1 == NativeCode.CredentialUIReturnCodes.InvalidAccountName )
userBuilder.Append (user);
else if ( (uint) ret1 > 0 )
throw new Win32Exception (lastError, "CredUIParseUserName throw an error");
Expand All @@ -190,7 +280,7 @@ public static NetworkCredential GetCredentials(string Target)
public static bool RemoveCredentials(string Target)
{
// Make the API call using the P/Invoke signature
var ret = NativeStructs.CredDelete (Target, NativeStructs.CredentialType.GENERIC, 0);
var ret = NativeCode.CredDelete (Target, NativeCode.CredentialType.Generic, 0);
int lastError = Marshal.GetLastWin32Error ();
if ( !ret )
throw new Win32Exception (lastError, "CredDelete throw an error");
Expand Down
6 changes: 3 additions & 3 deletions AdysTech.CredentialManager/CriticalCredentialHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ internal Credential GetCredential()
if ( !IsInvalid )
{
// Get the Credential from the mem location
NativeStructs.NativeCredential ncred = (NativeStructs.NativeCredential) Marshal.PtrToStructure (handle,
typeof (NativeStructs.NativeCredential));
NativeCode.NativeCredential ncred = (NativeCode.NativeCredential) Marshal.PtrToStructure (handle,
typeof (NativeCode.NativeCredential));

// Create a managed Credential type and fill it with data from the native counterpart.
Credential cred = new Credential (ncred);
Expand All @@ -46,7 +46,7 @@ override protected bool ReleaseHandle()
{
// NOTE: We should also ZERO out the memory allocated to the handle, before free'ing it
// so there are no traces of the sensitive data left in memory.
NativeStructs.CredFree (handle);
NativeCode.CredFree (handle);
// Mark the handle as invalid for future users.
SetHandleAsInvalid ();
return true;
Expand Down
Loading

0 comments on commit b1a7f3f

Please sign in to comment.