Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Last active March 27, 2019 14:48
Show Gist options
  • Select an option

  • Save Jaykul/2e1a52720cfff9fff0e9ca192f6a4970 to your computer and use it in GitHub Desktop.

Select an option

Save Jaykul/2e1a52720cfff9fff0e9ca192f6a4970 to your computer and use it in GitHub Desktop.
Trying to add some features to BetterCredentials
// Copyright (c) 2014, Joel Bennett
// Licensed under MIT license
using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
namespace CredentialManagement
{
using System.Management.Automation;
using System.Security;
public enum CredentialType : uint
{
None = 0,
Generic = 1,
DomainPassword = 2,
DomainCertificate = 3,
DomainVisiblePassword = 4,
GenericCertificate = 5,
DomainExtended = 6,
Maximum = 7,
MaximumEx = 1007
}
public enum PersistanceType : uint
{
Session = 1,
LocalComputer = 2,
Enterprise = 3
}
public static class SecureStringHelper
{
// Methods
public static SecureString CreateSecureString(string plainString)
{
var result = new SecureString();
if (!string.IsNullOrEmpty(plainString))
{
foreach (var c in plainString.ToCharArray())
{
result.AppendChar(c);
}
}
result.MakeReadOnly();
return result;
}
public static SecureString CreateSecureString(IntPtr ptrToString, int length = 0)
{
string password = length > 0
? Marshal.PtrToStringUni(ptrToString, length)
: Marshal.PtrToStringUni(ptrToString);
return CreateSecureString(password);
}
public static string CreateString(SecureString secureString)
{
string str;
IntPtr zero = IntPtr.Zero;
if ((secureString == null) || (secureString.Length == 0))
{
return string.Empty;
}
try
{
zero = Marshal.SecureStringToBSTR(secureString);
str = Marshal.PtrToStringBSTR(zero);
}
finally
{
if (zero != IntPtr.Zero)
{
Marshal.ZeroFreeBSTR(zero);
}
}
return str;
}
}
public static class IntPtrExtensions
{
public static IntPtr Increment(this IntPtr ptr, int cbSize)
{
return new IntPtr(ptr.ToInt64() + cbSize);
}
public static IntPtr Increment<T>(this IntPtr ptr)
{
return ptr.Increment(Marshal.SizeOf(typeof(T)));
}
public static T ElementAt<T>(this IntPtr ptr, int index)
{
var offset = Marshal.SizeOf(typeof(T))*index;
var offsetPtr = ptr.Increment(offset);
return (T)Marshal.PtrToStructure(offsetPtr, typeof(T));
}
}
public static class Store
{
public static PSObject Load(string target, CredentialType type = CredentialType.Generic, bool fix = true)
{
PSObject cred;
if(fix) {
target = FixTarget(target);
}
NativeMethods.CredRead(target, type, 0, out cred);
return cred;
}
private static string FixTarget(string target)
{
if (!target.Contains("/"))
{
if (target.Contains("="))
{
target = "MicrosoftPowerShell/" + target;
}
else
{
target = "MicrosoftPowerShell/user=" + target;
}
}
return target;
}
public static PSObject[] Enumerate(string filter = "")
{
uint count = 0;
int Flag = 0;
IntPtr credentialArray = IntPtr.Zero;
PSObject[] output = null;
if(string.IsNullOrEmpty(filter)) {
filter = null;
Flag = 1;
}
NativeMethods.PSCredentialMarshaler helper = new NativeMethods.PSCredentialMarshaler();
if(NativeMethods.CredEnumerate(filter, Flag, out count, out credentialArray)) {
IntPtr cred = IntPtr.Zero;
output = new PSObject[count];
for (int n = 0; n < count; n++)
{
cred = credentialArray.ElementAt<IntPtr>(n);
output[n] = (PSObject)helper.MarshalNativeToManaged(cred);
}
helper.CleanUpNativeData(credentialArray);
}
return output;
}
public static NativeMethods.CREDErrorCodes Save(PSObject credential)
{
var cred = credential.BaseObject as PSCredential;
if (cred == null)
{
throw new ArgumentException("Credential object does not contain a PSCredential");
}
if (!NativeMethods.CredWrite(credential, 0))
{
return (NativeMethods.CREDErrorCodes)Marshal.GetLastWin32Error();
}
else return NativeMethods.CREDErrorCodes.NO_ERROR;
}
public static NativeMethods.CREDErrorCodes Delete(string target, CredentialType type = CredentialType.Generic)
{
if (!NativeMethods.CredDelete(FixTarget(target), type, 0))
{
return (NativeMethods.CREDErrorCodes)Marshal.GetLastWin32Error();
}
else return NativeMethods.CREDErrorCodes.NO_ERROR;
}
}
public class NativeMethods
{
public enum CREDErrorCodes
{
NO_ERROR = 0,
ERROR_NOT_FOUND = 1168,
ERROR_NO_SUCH_LOGON_SESSION = 1312,
ERROR_INVALID_PARAMETER = 87,
ERROR_INVALID_FLAGS = 1004,
ERROR_BAD_USERNAME = 2202,
SCARD_E_NO_READERS_AVAILABLE = (int)(0x8010002E - 0x100000000),
SCARD_E_NO_SMARTCARD = (int)(0x8010000C - 0x100000000),
SCARD_W_REMOVED_CARD = (int)(0x80100069 - 0x100000000),
SCARD_W_WRONG_CHV = (int)(0x8010006B - 0x100000000)
}
[DllImport("Advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredRead(string target, CredentialType type, int reservedFlag,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PSCredentialMarshaler))]
out PSObject credentialout);
[DllImport("Advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredWrite([In]
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(PSCredentialMarshaler))]
PSObject userCredential, [In] UInt32 flags);
[DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredDelete(string target, CredentialType type, int flags);
[DllImport("Advapi32.dll", EntryPoint = "CredFree", SetLastError = true)]
public static extern bool CredFree([In] IntPtr cred);
[DllImport("advapi32.dll", EntryPoint = "CredEnumerateW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredEnumerate(string filter, int flag, out uint count, out IntPtr pCredentials);
[DllImport("ole32.dll")]
public static extern void CoTaskMemFree(IntPtr ptr);
public class PSCredentialMarshaler : ICustomMarshaler
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private class NATIVECREDENTIAL
{
public UInt32 Flags;
public CredentialType Type = CredentialType.Generic;
public string TargetName;
public string Comment;
public FILETIME LastWritten;
public UInt32 CredentialBlobSize;
public IntPtr CredentialBlob;
public PersistanceType Persist = PersistanceType.Enterprise;
public UInt32 AttributeCount;
public IntPtr Attributes;
public string TargetAlias;
public string UserName;
}
public void CleanUpManagedData(object ManagedObj)
{
// Nothing to do since all data can be garbage collected.
}
public void CleanUpNativeData(IntPtr pNativeData)
{
if (pNativeData == IntPtr.Zero)
{
return;
}
CredFree(pNativeData);
}
public int GetNativeDataSize()
{
return Marshal.SizeOf(typeof(NATIVECREDENTIAL));
}
public IntPtr MarshalManagedToNative(object obj)
{
PSCredential credential;
PSObject credo = obj as PSObject;
if (credo != null)
{
credential = credo.BaseObject as PSCredential;
}
else
{
credential = obj as PSCredential;
}
if (credential == null)
{
Console.WriteLine("Error: Can't convert!");
return IntPtr.Zero;
}
var nCred = new NATIVECREDENTIAL()
{
UserName = credential.UserName,
CredentialBlob = Marshal.SecureStringToCoTaskMemUnicode(credential.Password),
CredentialBlobSize = (uint)credential.Password.Length * 2,
TargetName = "MicrosoftPowerShell:user=" + credential.UserName,
Type = CredentialType.Generic,
Persist = PersistanceType.Enterprise
};
if (credo != null)
{
foreach (var m in credo.Members)
{
switch (m.Name)
{
case "Target":
if (m.Value != null)
nCred.TargetName = m.Value.ToString();
break;
case "TargetAlias":
if (m.Value != null)
nCred.TargetAlias = m.Value.ToString();
break;
case "Type":
if (m.Value != null)
nCred.Type = (CredentialType)m.Value;
break;
case "Persistence":
if (m.Value != null)
nCred.Persist = (PersistanceType)m.Value;
break;
case "Description":
if (m.Value != null)
nCred.Comment = m.Value.ToString();
break;
case "LastWriteTime":
// ignored
break;
}
}
}
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(nCred));
Marshal.StructureToPtr(nCred, ptr, false);
return ptr;
}
public object MarshalNativeToManaged(IntPtr pNativeData)
{
if (pNativeData == IntPtr.Zero)
{
return null;
}
var ncred = (NATIVECREDENTIAL)Marshal.PtrToStructure(pNativeData, typeof(NATIVECREDENTIAL));
var securePass = (ncred.CredentialBlob == IntPtr.Zero) ? new SecureString()
: SecureStringHelper.CreateSecureString(ncred.CredentialBlob, (int)(ncred.CredentialBlobSize)/2);
var credEx = new PSObject(new PSCredential(ncred.UserName, securePass));
credEx.Members.Add(new PSNoteProperty("Target", ncred.TargetName));
credEx.Members.Add(new PSNoteProperty("TargetAlias", ncred.TargetAlias));
credEx.Members.Add(new PSNoteProperty("Type", (CredentialType)ncred.Type));
credEx.Members.Add(new PSNoteProperty("Persistence", (PersistanceType)ncred.Persist));
credEx.Members.Add(new PSNoteProperty("Description", ncred.Comment));
credEx.Members.Add(new PSNoteProperty("LastWriteTime", DateTime.FromFileTime((((long)ncred.LastWritten.dwHighDateTime) << 32) + ncred.LastWritten.dwLowDateTime)));
return credEx;
}
public static ICustomMarshaler GetInstance(string cookie)
{
return new PSCredentialMarshaler();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment