Last active
March 27, 2019 14:48
-
-
Save Jaykul/2e1a52720cfff9fff0e9ca192f6a4970 to your computer and use it in GitHub Desktop.
Trying to add some features to BetterCredentials
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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