Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Finished converting to use new Git Credential API

  • Loading branch information...
commit c988908688d6ddd6b4b257642cf467e83dc8d70f 1 parent e6b17f0
@anurse authored
View
99 git-credential-winstore/NativeMethods.cs
@@ -11,7 +11,7 @@ internal static class NativeMethods
[DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredWriteW", CharSet = CharSet.Unicode)]
public static extern bool CredWrite(ref CREDENTIAL userCredential, UInt32 flags);
- [DllImport("advapi32.dll", EntryPoint="CredReadW", CharSet=CharSet.Unicode, SetLastError = true)]
+ [DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr credentialPtr);
[DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode)]
@@ -20,6 +20,89 @@ internal static class NativeMethods
[DllImport("advapi32.dll")]
public static extern void CredFree(IntPtr credentialPtr);
+ [DllImport("credui.dll", CharSet = CharSet.Unicode)]
+ public static extern CredUIReturnCodes CredUIPromptForWindowsCredentials(
+ ref CREDUI_INFO uiInfo,
+ int authError,
+ ref int authPackage,
+ IntPtr InAuthBuffer,
+ int InAuthBufferSize,
+ out IntPtr refOutAuthBuffer,
+ out int refOutAuthBufferSize,
+ ref bool fSave,
+ PromptForWindowsCredentialsFlags flags);
+
+ [DllImport("credui.dll", CharSet = CharSet.Auto)]
+ public static extern bool CredUnPackAuthenticationBuffer(
+ int dwFlags,
+ IntPtr pAuthBuffer,
+ int cbAuthBuffer,
+ StringBuilder pszUserName,
+ ref int pcchMaxUserName,
+ StringBuilder pszDomainName,
+ ref int pcchMaxDomainame,
+ StringBuilder pszPassword,
+ ref int pcchMaxPassword);
+
+ [DllImport("credui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ public static extern bool CredPackAuthenticationBuffer(
+ int dwFlags,
+ string pszUserName,
+ string pszPassword,
+ IntPtr pPackedCredentials,
+ ref int pcbPackedCredentials);
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ public struct CREDUI_INFO
+ {
+ public int cbSize;
+ public IntPtr hwndParent;
+ public string pszMessageText;
+ public string pszCaptionText;
+ public IntPtr hbmBanner;
+ }
+
+ public enum PromptForWindowsCredentialsFlags
+ {
+ /// <summary>
+ /// The caller is requesting that the credential provider return the user name and password in plain text.
+ /// This value cannot be combined with SECURE_PROMPT.
+ /// </summary>
+ CREDUIWIN_GENERIC = 0x1,
+ /// <summary>
+ /// The Save check box is displayed in the dialog box.
+ /// </summary>
+ CREDUIWIN_CHECKBOX = 0x2,
+ /// <summary>
+ /// Only credential providers that support the authentication package specified by the authPackage parameter should be enumerated.
+ /// This value cannot be combined with CREDUIWIN_IN_CRED_ONLY.
+ /// </summary>
+ CREDUIWIN_AUTHPACKAGE_ONLY = 0x10,
+ /// <summary>
+ /// Only the credentials specified by the InAuthBuffer parameter for the authentication package specified by the authPackage parameter should be enumerated.
+ /// If this flag is set, and the InAuthBuffer parameter is NULL, the function fails.
+ /// This value cannot be combined with CREDUIWIN_AUTHPACKAGE_ONLY.
+ /// </summary>
+ CREDUIWIN_IN_CRED_ONLY = 0x20,
+ /// <summary>
+ /// Credential providers should enumerate only administrators. This value is intended for User Account Control (UAC) purposes only. We recommend that external callers not set this flag.
+ /// </summary>
+ CREDUIWIN_ENUMERATE_ADMINS = 0x100,
+ /// <summary>
+ /// Only the incoming credentials for the authentication package specified by the authPackage parameter should be enumerated.
+ /// </summary>
+ CREDUIWIN_ENUMERATE_CURRENT_USER = 0x200,
+ /// <summary>
+ /// The credential dialog box should be displayed on the secure desktop. This value cannot be combined with CREDUIWIN_GENERIC.
+ /// Windows Vista: This value is not supported until Windows Vista with SP1.
+ /// </summary>
+ CREDUIWIN_SECURE_PROMPT = 0x1000,
+ /// <summary>
+ /// The credential provider should align the credential BLOB pointed to by the refOutAuthBuffer parameter to a 32-bit boundary, even if the provider is running on a 64-bit system.
+ /// </summary>
+ CREDUIWIN_PACK_32_WOW = 0x10000000,
+ }
+
public enum CRED_TYPE : int
{
GENERIC = 1,
@@ -48,6 +131,18 @@ public struct CREDENTIAL
public string targetAlias;
[MarshalAs(UnmanagedType.LPWStr)]
public string userName;
- }
+ }
+
+ public enum CredUIReturnCodes : int
+ {
+ NO_ERROR = 0,
+ ERROR_CANCELLED = 1223,
+ ERROR_NO_SUCH_LOGON_SESSION = 1312,
+ ERROR_NOT_FOUND = 1168,
+ ERROR_INVALID_ACCOUNT_NAME = 1315,
+ ERROR_INSUFFICIENT_BUFFER = 122,
+ ERROR_INVALID_PARAMETER = 87,
+ ERROR_INVALID_FLAGS = 1004
+ }
}
}
View
146 git-credential-winstore/Program.cs
@@ -84,6 +84,8 @@ private static void WriteUsage()
{
// Build the URL
Uri url = ExtractUrl(args);
+ string userName = args.GetOrDefault("username", null);
+ string password = null;
IntPtr credPtr = IntPtr.Zero;
try
@@ -93,13 +95,78 @@ private static void WriteUsage()
if (!NativeMethods.CredRead(target, NativeMethods.CRED_TYPE.GENERIC, 0, out credPtr))
{
// Don't have a credential for this user.
- yield break;
- }
+ credPtr = IntPtr.Zero;
+
+ // If we have a username, pack an input authentication buffer
+ Tuple<int, IntPtr> inputBuffer = null;;
+ IntPtr outputBuffer = IntPtr.Zero;
+ int outputBufferSize = 0;
+ try
+ {
+ inputBuffer = PackUserNameBuffer(userName);
+ if (inputBuffer == null) { yield break; }
+
+ // Setup UI
+ NativeMethods.CREDUI_INFO ui = new NativeMethods.CREDUI_INFO()
+ {
+ pszCaptionText = "Git Credentials",
+ pszMessageText = "Enter your credentials for: " + url.AbsoluteUri
+ };
+ ui.cbSize = Marshal.SizeOf(ui);
- // Decode the credential
- NativeMethods.CREDENTIAL cred = (NativeMethods.CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(NativeMethods.CREDENTIAL));
- yield return Tuple.Create("username", cred.userName);
- yield return Tuple.Create("password", Marshal.PtrToStringBSTR(cred.credentialBlob));
+ // Prompt!
+ int authPackage = 0;
+ bool save = false;
+ var ret = NativeMethods.CredUIPromptForWindowsCredentials(
+ uiInfo: ref ui,
+ authError: 0,
+ authPackage: ref authPackage,
+ InAuthBuffer: inputBuffer.Item2,
+ InAuthBufferSize: inputBuffer.Item1,
+ refOutAuthBuffer: out outputBuffer,
+ refOutAuthBufferSize: out outputBufferSize,
+ fSave: ref save,
+ flags: NativeMethods.PromptForWindowsCredentialsFlags.CREDUIWIN_GENERIC);
+ if (ret != NativeMethods.CredUIReturnCodes.NO_ERROR)
+ {
+ Console.Error.WriteLine("Error prompting for credentials: " + ret.ToString());
+ yield break;
+ }
+ }
+ finally
+ {
+ if (inputBuffer != null && inputBuffer.Item2 != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(inputBuffer.Item2);
+ }
+ }
+
+ try
+ {
+ // Unpack
+ if (!UnPackAuthBuffer(outputBuffer, outputBufferSize, out userName, out password))
+ {
+ yield break;
+ }
+ }
+ finally
+ {
+ if (outputBuffer != IntPtr.Zero)
+ {
+ Marshal.FreeHGlobal(outputBuffer);
+ }
+ }
+ }
+ else
+ {
+ // Decode the credential
+ NativeMethods.CREDENTIAL cred = (NativeMethods.CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(NativeMethods.CREDENTIAL));
+ userName = cred.userName;
+ password = Marshal.PtrToStringBSTR(cred.credentialBlob);
+ }
+
+ yield return Tuple.Create("username", userName);
+ yield return Tuple.Create("password", password);
}
finally
{
@@ -110,6 +177,73 @@ private static void WriteUsage()
}
}
+ private static bool UnPackAuthBuffer(IntPtr buffer, int size, out string userName, out string password)
+ {
+ userName = String.Empty;
+ password = String.Empty;
+
+ StringBuilder userNameBuffer = new StringBuilder(255);
+ StringBuilder passwordBuffer = new StringBuilder(255);
+ StringBuilder domainBuffer = new StringBuilder(255);
+ int userNameSize = 255;
+ int passwordSize = 255;
+ int domainSize = 255;
+ if (!NativeMethods.CredUnPackAuthenticationBuffer(
+ dwFlags: 0,
+ pAuthBuffer: buffer,
+ cbAuthBuffer: size,
+ pszUserName: userNameBuffer,
+ pcchMaxUserName: ref userNameSize,
+ pszDomainName: domainBuffer,
+ pcchMaxDomainame: ref domainSize,
+ pszPassword: passwordBuffer,
+ pcchMaxPassword: ref passwordSize))
+ {
+ Console.Error.WriteLine("Unable to unpack credential: " + GetLastErrorMessage());
+ return false;
+ }
+ userName = userNameBuffer.ToString();
+ password = passwordBuffer.ToString();
+ return true;
+ }
+
+ private static Tuple<int, IntPtr> PackUserNameBuffer(string userName)
+ {
+ if (String.IsNullOrWhiteSpace(userName))
+ {
+ return Tuple.Create(0, IntPtr.Zero);
+ }
+ IntPtr buf = IntPtr.Zero;
+ int size = 0;
+
+ // First, calculate size. (buf == IntPtr.Zero)
+ var result = NativeMethods.CredPackAuthenticationBuffer(
+ dwFlags: 4, // CRED_PACK_GENERIC_CREDENTIALS
+ pszUserName: userName,
+ pszPassword: String.Empty,
+ pPackedCredentials: buf,
+ pcbPackedCredentials: ref size);
+ Debug.Assert(!result);
+ if (Marshal.GetLastWin32Error() != 122)
+ {
+ Console.Error.WriteLine("Unable to calculate size of packed authentication buffer: " + GetLastErrorMessage());
+ return null;
+ }
+
+ buf = Marshal.AllocHGlobal(size);
+ if (!NativeMethods.CredPackAuthenticationBuffer(
+ dwFlags: 4, // CRED_PACK_GENERIC_CREDENTIALS
+ pszUserName: userName,
+ pszPassword: String.Empty,
+ pPackedCredentials: buf,
+ pcbPackedCredentials: ref size))
+ {
+ Console.Error.WriteLine("Unable to pack incoming username: " + GetLastErrorMessage());
+ return null;
+ }
+ return Tuple.Create(size, buf);
+ }
+
static IEnumerable<Tuple<string, string>> StoreCommand(IDictionary<string, string> args)
{
// Build the URL
Please sign in to comment.
Something went wrong with that request. Please try again.