Skip to content

Commit b1a7f3f

Browse files
committed
Updated Windows Credential, added Console credential prompts
1 parent aa0eebe commit b1a7f3f

File tree

6 files changed

+213
-169
lines changed

6 files changed

+213
-169
lines changed

AdysTech.CredentialManager/AdysTech.CredentialManager.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
<ItemGroup>
4242
<Compile Include="Credential.cs" />
4343
<Compile Include="CredentialManager.cs" />
44-
<Compile Include="NativeStructs.cs" />
44+
<Compile Include="NativeCode.cs" />
4545
<Compile Include="CriticalCredentialHandle.cs" />
4646
<Compile Include="Properties\AssemblyInfo.cs" />
4747
</ItemGroup>

AdysTech.CredentialManager/Credential.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ namespace AdysTech.CredentialManager
1010
internal class Credential
1111
{
1212
public UInt32 Flags;
13-
public NativeStructs.CredentialType Type;
13+
public NativeCode.CredentialType Type;
1414
public string TargetName;
1515
public string Comment;
1616
public DateTime LastWritten;
1717
public UInt32 CredentialBlobSize;
1818
public string CredentialBlob;
19-
public NativeStructs.Persistance Persist;
19+
public NativeCode.Persistance Persist;
2020
public UInt32 AttributeCount;
2121
public IntPtr Attributes;
2222
public string TargetAlias;
@@ -27,7 +27,7 @@ public Credential()
2727

2828
}
2929

30-
internal Credential(NativeStructs.NativeCredential ncred)
30+
internal Credential(NativeCode.NativeCredential ncred)
3131
{
3232
CredentialBlobSize = ncred.CredentialBlobSize;
3333
CredentialBlob = Marshal.PtrToStringUni (ncred.CredentialBlob,
@@ -37,7 +37,7 @@ internal Credential(NativeStructs.NativeCredential ncred)
3737
TargetAlias = Marshal.PtrToStringUni (ncred.TargetAlias);
3838
Type = ncred.Type;
3939
Flags = ncred.Flags;
40-
Persist = (NativeStructs.Persistance) ncred.Persist;
40+
Persist = (NativeCode.Persistance) ncred.Persist;
4141
LastWritten = DateTime.FromFileTime ((long) ( (ulong) ncred.LastWritten.dwHighDateTime << 32 | (ulong) ncred.LastWritten.dwLowDateTime ));
4242
}
4343

@@ -50,8 +50,8 @@ public Credential(System.Net.NetworkCredential credential)
5050
Attributes = IntPtr.Zero;
5151
Comment = null;
5252
TargetAlias = null;
53-
Type = NativeStructs.CredentialType.GENERIC;
54-
Persist = NativeStructs.Persistance.SESSION;
53+
Type = NativeCode.CredentialType.Generic;
54+
Persist = NativeCode.Persistance.Session;
5555
}
5656

5757
/// <summary>
@@ -60,9 +60,9 @@ public Credential(System.Net.NetworkCredential credential)
6060
/// <param name="cred">The managed Credential counterpart containing data to be stored.</param>
6161
/// <returns>A NativeCredential instance that is derived from the given Credential
6262
/// instance.</returns>
63-
internal NativeStructs.NativeCredential GetNativeCredential()
63+
internal NativeCode.NativeCredential GetNativeCredential()
6464
{
65-
NativeStructs.NativeCredential ncred = new NativeStructs.NativeCredential ();
65+
NativeCode.NativeCredential ncred = new NativeCode.NativeCredential ();
6666
ncred.AttributeCount = 0;
6767
ncred.Attributes = IntPtr.Zero;
6868
ncred.Comment = IntPtr.Zero;

AdysTech.CredentialManager/CredentialManager.cs

Lines changed: 126 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
using Microsoft.Win32.SafeHandles;
2-
using System;
3-
using System.Collections.Generic;
1+
using System;
42
using System.ComponentModel;
5-
using System.Linq;
3+
using System.Diagnostics;
64
using System.Net;
75
using System.Runtime.InteropServices;
86
using System.Text;
9-
using System.Threading.Tasks;
107

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

30-
if ( !PromptForCredentials (Target, out username, out passwd, out domain) )
28+
if ( !PromptForCredentials (Target, ref save, out username, out passwd, out domain) )
3129
return null;
3230
return new NetworkCredential (username, passwd, domain);
3331
}
@@ -36,73 +34,165 @@ public static NetworkCredential PromptForCredentials(string Target)
3634
/// Opens OS Version specific Window prompting for credentials
3735
/// </summary>
3836
/// <param name="Target">A descriptive text for where teh credentials being asked are used for</param>
37+
/// <param name="save">Whether or not to offer the checkbox to save the credentials</param>
3938
/// <param name="Message">A brief message to display in the dialog box</param>
4039
/// <param name="Caption">Title for the dialog box</param>
4140
/// <returns>NetworkCredential object containing the user name, </returns>
42-
public static NetworkCredential PromptForCredentials(string Target, string Message, string Caption)
41+
public static NetworkCredential PromptForCredentials(string Target, ref bool save, string Message, string Caption)
4342
{
4443
var username = String.Empty;
4544
var passwd = String.Empty;
4645
var domain = String.Empty;
4746

48-
if ( !PromptForCredentials (Target, Message, Caption, out username, out passwd, out domain) )
47+
if ( !PromptForCredentials (Target, ref save, Message, Caption, out username, out passwd, out domain) )
4948
return null;
5049
return new NetworkCredential (username, passwd, domain);
5150
}
5251

53-
internal static bool PromptForCredentials(string target, out string user, out string password, out string domain)
52+
internal static bool PromptForCredentials(string target, ref bool save, out string user, out string password, out string domain)
5453
{
55-
return PromptForCredentials (target, new NativeStructs.CredentialUIInfo (), out user, out password, out domain);
54+
return PromptForCredentials (target, new NativeCode.CredentialUIInfo (), ref save, out user, out password, out domain);
5655
}
5756

58-
internal static bool PromptForCredentials(string target, string Message, string Caption, out string user, out string password, out string domain)
57+
internal static bool PromptForCredentials(string target, ref bool save, string Message, string Caption, out string user, out string password, out string domain)
5958
{
60-
NativeStructs.CredentialUIInfo credUI = new NativeStructs.CredentialUIInfo ();
59+
NativeCode.CredentialUIInfo credUI = new NativeCode.CredentialUIInfo ();
6160
credUI.pszMessageText = Message;
6261
credUI.pszCaptionText = Caption;
63-
return PromptForCredentials (target, credUI, out user, out password, out domain);
62+
return PromptForCredentials (target, credUI, ref save, out user, out password, out domain);
6463
}
6564

66-
private static bool PromptForCredentials(string target, NativeStructs.CredentialUIInfo credUI, out string user, out string password, out string domain)
65+
private static bool PromptForCredentials(string target, NativeCode.CredentialUIInfo credUI, ref bool save, out string user, out string password, out string domain)
6766
{
67+
user = String.Empty;
68+
password = String.Empty;
69+
domain = String.Empty;
70+
71+
72+
6873
// Setup the flags and variables
69-
StringBuilder userPassword = new StringBuilder (), userID = new StringBuilder ();
7074
credUI.cbSize = Marshal.SizeOf (credUI);
75+
int errorcode = 0;
76+
uint dialogReturn;
77+
uint authPackage = 0;
78+
79+
IntPtr outCredBuffer = new IntPtr ();
80+
uint outCredSize;
81+
var flags = NativeCode.PromptForWindowsCredentialsFlags.GenericCredentials | NativeCode.PromptForWindowsCredentialsFlags.EnumerateCurrentUser;
82+
flags = save ? flags | NativeCode.PromptForWindowsCredentialsFlags.ShowCheckbox : flags;
83+
84+
// Setup the flags and variables
85+
int result = NativeCode.CredUIPromptForWindowsCredentials (ref credUI,
86+
errorcode,
87+
ref authPackage,
88+
IntPtr.Zero,
89+
0,
90+
out outCredBuffer,
91+
out outCredSize,
92+
ref save,
93+
flags);
94+
95+
var usernameBuf = new StringBuilder (100);
96+
var passwordBuf = new StringBuilder (100);
97+
var domainBuf = new StringBuilder (100);
98+
99+
int maxUserName = 100;
100+
int maxDomain = 100;
101+
int maxPassword = 100;
102+
if ( result == 0 )
103+
{
104+
if ( NativeCode.CredUnPackAuthenticationBuffer (0, outCredBuffer, outCredSize, usernameBuf, ref maxUserName,
105+
domainBuf, ref maxDomain, passwordBuf, ref maxPassword) )
106+
{
107+
user = usernameBuf.ToString ();
108+
password = passwordBuf.ToString ();
109+
domain = domainBuf.ToString ();
110+
if ( String.IsNullOrWhiteSpace (domain) )
111+
{
112+
Debug.WriteLine ("Domain null");
113+
if ( !ParseUserName (usernameBuf.ToString (), maxUserName, maxDomain, out user, out password) )
114+
user = usernameBuf.ToString ();
115+
}
116+
}
117+
118+
//mimic SecureZeroMem function to make sure buffer is zeroed out. SecureZeroMem is not an exported function, neither is RtlSecureZeroMemory
119+
var zeroBytes = new byte[outCredSize];
120+
Marshal.Copy (zeroBytes, 0, outCredBuffer, (int) outCredSize);
121+
122+
//clear the memory allocated by CredUIPromptForWindowsCredentials
123+
NativeCode.CoTaskMemFree (outCredBuffer);
124+
return true;
125+
}
126+
127+
user = null;
128+
domain = null;
129+
return false;
130+
}
131+
132+
private static bool ParseUserName(string usernameBuf, int maxUserName, int maxDomain, out string user, out string domain)
133+
{
134+
StringBuilder userBuilder = new StringBuilder ();
135+
StringBuilder domainBuilder = new StringBuilder ();
136+
user = String.Empty;
137+
domain = String.Empty;
138+
139+
var returnCode = NativeCode.CredUIParseUserName (usernameBuf, userBuilder, maxUserName, domainBuilder, maxDomain);
140+
Debug.WriteLine (returnCode);
141+
switch ( returnCode )
142+
{
143+
case NativeCode.CredentialUIReturnCodes.Success: // The username is valid.
144+
user = userBuilder.ToString ();
145+
domain = domainBuilder.ToString ();
146+
return true;
147+
}
148+
return false;
149+
}
150+
151+
/// <summary>
152+
/// Accepts credentials in a console window
153+
/// </summary>
154+
/// <param name="Target">A descriptive text for where teh credentials being asked are used for</param>
155+
/// <returns>NetworkCredential object containing the user name, </returns>
156+
public static NetworkCredential PromptForCredentialsConsole(string target)
157+
{
158+
var user = String.Empty;
159+
var password = String.Empty;
160+
var domain = String.Empty;
161+
162+
// Setup the flags and variables
163+
StringBuilder userPassword = new StringBuilder (), userID = new StringBuilder ();
71164
bool save = true;
72-
NativeStructs.CredentialUIFlags flags = NativeStructs.CredentialUIFlags.COMPLETE_USERNAME | NativeStructs.CredentialUIFlags.PERSIST | NativeStructs.CredentialUIFlags.EXCLUDE_CERTIFICATES;
165+
NativeCode.CredentialUIFlags flags = NativeCode.CredentialUIFlags.CompleteUsername | NativeCode.CredentialUIFlags.ExcludeCertificates;
73166

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

77170
password = userPassword.ToString ();
78171

79172
StringBuilder userBuilder = new StringBuilder ();
80173
StringBuilder domainBuilder = new StringBuilder ();
81174

82-
returnCode = NativeStructs.CredUIParseUserName (userID.ToString (), userBuilder, int.MaxValue, domainBuilder, int.MaxValue);
175+
returnCode = NativeCode.CredUIParseUserName (userID.ToString (), userBuilder, int.MaxValue, domainBuilder, int.MaxValue);
83176
switch ( returnCode )
84177
{
85-
case NativeStructs.CredentialUIReturnCodes.NO_ERROR: // The username is valid.
178+
case NativeCode.CredentialUIReturnCodes.Success: // The username is valid.
86179
user = userBuilder.ToString ();
87180
domain = domainBuilder.ToString ();
88-
return true;
181+
break;
89182

90-
case NativeStructs.CredentialUIReturnCodes.ERROR_INVALID_ACCOUNT_NAME: // The username is not valid.
183+
case NativeCode.CredentialUIReturnCodes.InvalidAccountName: // The username is not valid.
91184
user = userID.ToString ();
92185
domain = null;
93-
return false;
186+
break;
94187

95-
case NativeStructs.CredentialUIReturnCodes.ERROR_INSUFFICIENT_BUFFER: // One of the buffers is too small.
188+
case NativeCode.CredentialUIReturnCodes.InsufficientBuffer: // One of the buffers is too small.
96189
throw new OutOfMemoryException ();
97190

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

101-
default:
102-
user = null;
103-
domain = null;
104-
return false;
105194
}
195+
return new NetworkCredential (user, password, domain);
106196
}
107197

108198

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

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

169259
//assuming invalid account name to be not meeting condition for CredUIParseUserName
170260
//"The name must be in UPN or down-level format, or a certificate"
171-
if ( ret1 == NativeStructs.CredentialUIReturnCodes.ERROR_INVALID_ACCOUNT_NAME )
261+
if ( ret1 == NativeCode.CredentialUIReturnCodes.InvalidAccountName )
172262
userBuilder.Append (user);
173263
else if ( (uint) ret1 > 0 )
174264
throw new Win32Exception (lastError, "CredUIParseUserName throw an error");
@@ -190,7 +280,7 @@ public static NetworkCredential GetCredentials(string Target)
190280
public static bool RemoveCredentials(string Target)
191281
{
192282
// Make the API call using the P/Invoke signature
193-
var ret = NativeStructs.CredDelete (Target, NativeStructs.CredentialType.GENERIC, 0);
283+
var ret = NativeCode.CredDelete (Target, NativeCode.CredentialType.Generic, 0);
194284
int lastError = Marshal.GetLastWin32Error ();
195285
if ( !ret )
196286
throw new Win32Exception (lastError, "CredDelete throw an error");

AdysTech.CredentialManager/CriticalCredentialHandle.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ internal Credential GetCredential()
2121
if ( !IsInvalid )
2222
{
2323
// Get the Credential from the mem location
24-
NativeStructs.NativeCredential ncred = (NativeStructs.NativeCredential) Marshal.PtrToStructure (handle,
25-
typeof (NativeStructs.NativeCredential));
24+
NativeCode.NativeCredential ncred = (NativeCode.NativeCredential) Marshal.PtrToStructure (handle,
25+
typeof (NativeCode.NativeCredential));
2626

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

0 commit comments

Comments
 (0)