| | 1 | | // Copyright (c) Microsoft Corporation. All rights reserved. |
| | 2 | | // Licensed under the MIT License. |
| | 3 | |
|
| | 4 | | using System; |
| | 5 | | using System.ComponentModel; |
| | 6 | | using System.Runtime.CompilerServices; |
| | 7 | | using System.Runtime.InteropServices; |
| | 8 | |
|
| | 9 | | namespace Azure.Identity |
| | 10 | | { |
| | 11 | | internal static class WindowsNativeMethods |
| | 12 | | { |
| | 13 | | public enum CRED_PERSIST : uint |
| | 14 | | { |
| | 15 | | CRED_PERSIST_SESSION = 1, |
| | 16 | | CRED_PERSIST_LOCAL_MACHINE = 2, |
| | 17 | | CRED_PERSIST_ENTERPRISE = 3 |
| | 18 | | } |
| | 19 | |
|
| | 20 | | public enum CRED_TYPE |
| | 21 | | { |
| | 22 | | GENERIC = 1, |
| | 23 | | DOMAIN_PASSWORD = 2, |
| | 24 | | DOMAIN_CERTIFICATE = 3, |
| | 25 | | DOMAIN_VISIBLE_PASSWORD = 4, |
| | 26 | | MAXIMUM = 5 |
| | 27 | | } |
| | 28 | |
|
| | 29 | | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] |
| | 30 | | public struct CredentialData |
| | 31 | | { |
| | 32 | | public uint Flags; |
| | 33 | | public CRED_TYPE Type; |
| | 34 | | public string TargetName; |
| | 35 | | public string Comment; |
| | 36 | | public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten; |
| | 37 | | public uint CredentialBlobSize; |
| | 38 | | public IntPtr CredentialBlob; |
| | 39 | | public CRED_PERSIST Persist; |
| | 40 | | public uint AttributeCount; |
| | 41 | | public IntPtr Attributes; |
| | 42 | | public string TargetAlias; |
| | 43 | | public string UserName; |
| | 44 | | } |
| | 45 | |
|
| | 46 | | public const int ERROR_NOT_FOUND = 1168; |
| | 47 | |
|
| | 48 | | public const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; |
| | 49 | | public const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; |
| | 50 | | public const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; |
| | 51 | |
|
| | 52 | | public static IntPtr CredRead(string target, CRED_TYPE type) |
| | 53 | | { |
| 48 | 54 | | ThrowIfFailed(Imports.CredRead(target, type, 0, out IntPtr userCredential)); |
| 48 | 55 | | return userCredential; |
| | 56 | | } |
| | 57 | |
|
| 16 | 58 | | public static void CredWrite(IntPtr userCredential) => ThrowIfFailed(Imports.CredWrite(userCredential, 0)); |
| | 59 | |
|
| 16 | 60 | | public static void CredDelete(string target, CRED_TYPE type) => ThrowIfFailed(Imports.CredDelete(target, type, 0 |
| | 61 | |
|
| | 62 | | public static void CredFree(IntPtr userCredential) |
| | 63 | | { |
| 48 | 64 | | if (userCredential != IntPtr.Zero) |
| | 65 | | { |
| 48 | 66 | | Imports.CredFree(userCredential); |
| | 67 | | } |
| 48 | 68 | | } |
| | 69 | |
|
| | 70 | | private static void ThrowIfFailed(bool isSucceeded, [CallerMemberName] string methodName = default) |
| | 71 | | { |
| 80 | 72 | | if (isSucceeded) |
| | 73 | | { |
| 80 | 74 | | return; |
| | 75 | | } |
| | 76 | |
|
| 0 | 77 | | var error = Marshal.GetLastWin32Error(); |
| 0 | 78 | | var message = error == ERROR_NOT_FOUND ? $"{methodName} has failed but error is unknown." : MessageFromError |
| 0 | 79 | | throw new InvalidOperationException(message); |
| | 80 | | } |
| | 81 | |
|
| | 82 | | private static string MessageFromErrorCode(int errorCode) |
| | 83 | | { |
| | 84 | | // Twy Win32 first |
| 0 | 85 | | uint flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; |
| | 86 | |
|
| 0 | 87 | | IntPtr messageBuffer = IntPtr.Zero; |
| 0 | 88 | | string message = null; |
| | 89 | |
|
| | 90 | | try |
| | 91 | | { |
| 0 | 92 | | var length = Imports.FormatMessage(flags, IntPtr.Zero, errorCode, 0, ref messageBuffer, 0, IntPtr.Zero); |
| 0 | 93 | | if (length == 0) |
| | 94 | | { |
| | 95 | | // If failed, try to convert NTSTATUS to Win32 error |
| 0 | 96 | | int code = Imports.RtlNtStatusToDosError(errorCode); |
| 0 | 97 | | return new Win32Exception(code).Message; |
| | 98 | | } |
| 0 | 99 | | } |
| | 100 | | finally |
| | 101 | | { |
| 0 | 102 | | if (messageBuffer != IntPtr.Zero) |
| | 103 | | { |
| 0 | 104 | | message = Marshal.PtrToStringUni(messageBuffer); |
| 0 | 105 | | Marshal.FreeHGlobal(messageBuffer); |
| | 106 | | } |
| 0 | 107 | | } |
| | 108 | |
|
| 0 | 109 | | return message ?? string.Empty; |
| 0 | 110 | | } |
| | 111 | |
|
| | 112 | | private static class Imports |
| | 113 | | { |
| | 114 | | [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] |
| | 115 | | [DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)] |
| | 116 | | public static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, int dwMessageId, uint dwLanguageId, r |
| | 117 | |
|
| | 118 | | [DllImport("ntdll.dll")] |
| | 119 | | [DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)] |
| | 120 | | public static extern int RtlNtStatusToDosError(int Status); |
| | 121 | |
|
| | 122 | | [DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)] |
| | 123 | | [DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)] |
| | 124 | | public static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr userCredentia |
| | 125 | |
|
| | 126 | | [DllImport("advapi32.dll", EntryPoint = "CredWriteW", CharSet = CharSet.Unicode, SetLastError = true)] |
| | 127 | | [DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)] |
| | 128 | | public static extern bool CredWrite(IntPtr userCredential, int reservedFlag); |
| | 129 | |
|
| | 130 | | [DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode, SetLastError = true)] |
| | 131 | | [DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)] |
| | 132 | | public static extern bool CredDelete(string target, CRED_TYPE type, int reservedFlag); |
| | 133 | |
|
| | 134 | | [DllImport("advapi32.dll", SetLastError = true)] |
| | 135 | | [DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.AssemblyDirectory)] |
| | 136 | | public static extern void CredFree([In] IntPtr buffer); |
| | 137 | | } |
| | 138 | | } |
| | 139 | | } |