public static Credential CredentialPrompt(Program program, TargetUri targetUri, string titleMessage) { // ReadConsole 32768 fail, 32767 OK @linquize [https://github.com/Microsoft/Git-Credential-Manager-for-Windows/commit/a62b9a19f430d038dcd85a610d97e5f763980f85] const int BufferReadSize = 16 * 1024; if (program is null) { throw new ArgumentNullException(nameof(program)); } if (targetUri is null) { throw new ArgumentNullException(nameof(targetUri)); } titleMessage = titleMessage ?? "Please enter your credentials for "; StringBuilder buffer = new StringBuilder(BufferReadSize); uint read = 0; uint written = 0; NativeMethods.ConsoleMode consoleMode = 0; NativeMethods.FileAccess fileAccessFlags = NativeMethods.FileAccess.GenericRead | NativeMethods.FileAccess.GenericWrite; NativeMethods.FileAttributes fileAttributes = NativeMethods.FileAttributes.Normal; NativeMethods.FileCreationDisposition fileCreationDisposition = NativeMethods.FileCreationDisposition.OpenExisting; NativeMethods.FileShare fileShareFlags = NativeMethods.FileShare.Read | NativeMethods.FileShare.Write; using (SafeFileHandle stdout = NativeMethods.CreateFile(NativeMethods.ConsoleOutName, fileAccessFlags, fileShareFlags, IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero)) using (SafeFileHandle stdin = NativeMethods.CreateFile(NativeMethods.ConsoleInName, fileAccessFlags, fileShareFlags, IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero)) { // Read the current console mode. if (stdin.IsInvalid || stdout.IsInvalid) { program.Trace.WriteLine("not a tty detected, abandoning prompt."); return(null); } else if (!NativeMethods.GetConsoleMode(stdin, out consoleMode)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to determine console mode (" + NativeMethods.Win32Error.GetText(error) + ")."); } program.Trace.WriteLine($"console mode = '{consoleMode}'."); string username = null; string password = null; // Instruct the user as to what they are expected to do. buffer.Append(titleMessage) .Append(targetUri) .AppendLine(); if (!NativeMethods.WriteConsole(stdout, buffer, (uint)buffer.Length, out written, IntPtr.Zero)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to write to standard output (" + NativeMethods.Win32Error.GetText(error) + ")."); } // Clear the buffer for the next operation. buffer.Clear(); // Prompt the user for the username wanted. buffer.Append("username: "******"Unable to write to standard output (" + NativeMethods.Win32Error.GetText(error) + ")."); } // Clear the buffer for the next operation. buffer.Clear(); // Read input from the user. if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to read from standard input (" + NativeMethods.Win32Error.GetText(error) + ")."); } // Record input from the user into local storage, stripping any EOL chars. username = buffer.ToString(0, (int)read); username = username.Trim(program.Settings.NewLine.ToCharArray()); // Clear the buffer for the next operation. buffer.Clear(); // Set the console mode to current without echo input. NativeMethods.ConsoleMode consoleMode2 = consoleMode ^ NativeMethods.ConsoleMode.EchoInput; try { if (!NativeMethods.SetConsoleMode(stdin, consoleMode2)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to set console mode (" + NativeMethods.Win32Error.GetText(error) + ")."); } program.Trace.WriteLine($"console mode = '{(consoleMode2 & NativeMethods.ConsoleMode.AllFlags)}'."); // Prompt the user for password. buffer.Append("password: "******"Unable to write to standard output (" + NativeMethods.Win32Error.GetText(error) + ")."); } // Clear the buffer for the next operation. buffer.Clear(); // Read input from the user. if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to read from standard input (" + NativeMethods.Win32Error.GetText(error) + ")."); } // Record input from the user into local storage, stripping any EOL chars. password = buffer.ToString(0, (int)read); password = password.Trim(program.Settings.NewLine.ToCharArray()); } finally { // Restore the console mode to its original value. NativeMethods.SetConsoleMode(stdin, consoleMode); program.Trace.WriteLine($"console mode = '{consoleMode}'."); } if (username != null && password != null) { return(new Credential(username, password)); } } return(null); }
/// <summary> /// <para></para> /// <para>Tokens acquired are stored in the secure secret store provided during /// initialization.</para> /// </summary> /// <param name="targetUri">The unique identifier for the resource for which access is to /// be acquired.</param> /// <param name="credentials">(out) Credentials when acquision is successful; null otherwise.</param> /// <returns>True if success; otherwise false.</returns> public bool InteractiveLogon(Uri targetUri, out Credential credentials) { // ReadConsole 32768 fail, 32767 ok // @linquize [https://github.com/Microsoft/Git-Credential-Manager-for-Windows/commit/a62b9a19f430d038dcd85a610d97e5f763980f85] const int BufferReadSize = 32 * 1024 - 7; StringBuilder buffer = new StringBuilder(BufferReadSize); uint read = 0; uint written = 0; NativeMethods.FileAccess fileAccessFlags = NativeMethods.FileAccess.GenericRead | NativeMethods.FileAccess.GenericWrite; NativeMethods.FileAttributes fileAttributes = NativeMethods.FileAttributes.Normal; NativeMethods.FileCreationDisposition fileCreationDisposition = NativeMethods.FileCreationDisposition.OpenExisting; NativeMethods.FileShare fileShareFlags = NativeMethods.FileShare.Read | NativeMethods.FileShare.Write; using (SafeFileHandle stdout = NativeMethods.CreateFile("CONOUT$", fileAccessFlags, fileShareFlags, IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero)) using (SafeFileHandle stdin = NativeMethods.CreateFile("CONIN$", fileAccessFlags, fileShareFlags, IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero)) { // read the current console mode NativeMethods.ConsoleMode consoleMode; if (!NativeMethods.GetConsoleMode(stdin, out consoleMode)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to determine console mode (" + error + ")."); } // instruct the user as to what they are expected to do buffer.Append("Please enter your GitHub credentials for ") .Append(targetUri.Scheme) .Append("://") .Append(targetUri.DnsSafeHost) .Append("/") .Append(targetUri.PathAndQuery) .AppendLine(); if (!NativeMethods.WriteConsole(stdout, buffer, (uint)buffer.Length, out written, IntPtr.Zero)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to write to standard output (" + error + ")."); } // clear the buffer for the next operation buffer.Clear(); // prompt the user for the username wanted buffer.Append("username: "******"Unable to write to standard output (" + error + ")."); } // clear the buffer for the next operation buffer.Clear(); // read input from the user if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to read from standard input (" + error + ")."); } // record input from the user into local storage, stripping any eol chars string username = buffer.ToString(0, (int)read); username = username.Trim(Environment.NewLine.ToCharArray()); // clear the buffer for the next operation buffer.Clear(); // set the console mode to current without echo input NativeMethods.ConsoleMode consoleMode2 = consoleMode ^ NativeMethods.ConsoleMode.EchoInput; if (!NativeMethods.SetConsoleMode(stdin, consoleMode2)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to set console mode (" + error + ")."); } // prompt the user for password buffer.Append("password: "******"Unable to write to standard output (" + error + ")."); } // clear the buffer for the next operation buffer.Clear(); // read input from the user if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to read from standard input (" + error + ")."); } // record input from the user into local storage, stripping any eol chars string password = buffer.ToString(0, (int)read); password = password.Trim(Environment.NewLine.ToCharArray()); // clear the buffer for the next operation buffer.Clear(); // restore the console mode to its original value if (!NativeMethods.SetConsoleMode(stdin, consoleMode)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to set console mode (" + error + ")."); } GithubAuthenticationResult result; if (result = GithubAuthority.AcquireToken(targetUri, username, password, null, this.TokenScope).Result) { Trace.WriteLine(" token aquisition succeeded"); credentials = (Credential)result.Token; this.PersonalAccessTokenStore.WriteCredentials(targetUri, credentials); return(true); } else if (result == GithubAuthenticationResultType.TwoFactorApp || result == GithubAuthenticationResultType.TwoFactorSms) { buffer.Clear() .AppendLine() .Append("authcode: "); if (!NativeMethods.WriteConsole(stdout, buffer, (uint)buffer.Length, out written, IntPtr.Zero)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to write to standard output (" + error + ")."); } buffer.Clear(); // read input from the user if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero)) { int error = Marshal.GetLastWin32Error(); throw new Win32Exception(error, "Unable to read from standard input (" + error + ")."); } string authenticationCode = buffer.ToString(0, (int)read); authenticationCode = authenticationCode.Trim(Environment.NewLine.ToCharArray()); if (result = GithubAuthority.AcquireToken(targetUri, username, password, authenticationCode, this.TokenScope).Result) { Trace.WriteLine(" token aquisition succeeded"); credentials = (Credential)result.Token; this.PersonalAccessTokenStore.WriteCredentials(targetUri, credentials); return(true); } } } Trace.WriteLine(" interactive logon failed"); credentials = null; return(false); }