├── .gitattributes
├── .gitignore
├── App.config
├── LICENSE
├── NativeMethods.cs
├── PasswordPrompt.Designer.cs
├── PasswordPrompt.cs
├── PasswordPrompt.resx
├── Program.cs
├── Properties
└── AssemblyInfo.cs
├── askpass.csproj
├── askpass.sln
└── test.ps1
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text eol=crlf
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/*
2 | obj/*
3 | *.suo
4 | *~
5 |
--------------------------------------------------------------------------------
/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD-2-Clause
2 |
3 | Copyright 2014 Luke Sampson
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions
7 | are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright
10 | notice, this list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright
13 | notice, this list of conditions and the following disclaimer in the
14 | documentation and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
--------------------------------------------------------------------------------
/NativeMethods.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Runtime.InteropServices;
6 |
7 | /// This is copied from http://gitcredentialstore.codeplex.com/
8 | namespace askpass {
9 | internal static class NativeMethods {
10 | [DllImport("Advapi32.dll", SetLastError = true, EntryPoint = "CredWriteW", CharSet = CharSet.Unicode)]
11 | public static extern bool CredWrite(ref CREDENTIAL userCredential, UInt32 flags);
12 |
13 | [DllImport("advapi32.dll", EntryPoint = "CredReadW", CharSet = CharSet.Unicode, SetLastError = true)]
14 | public static extern bool CredRead(string target, CRED_TYPE type, int reservedFlag, out IntPtr credentialPtr);
15 |
16 | [DllImport("advapi32.dll", EntryPoint = "CredDeleteW", CharSet = CharSet.Unicode)]
17 | public static extern bool CredDelete(string target, CRED_TYPE type, int flags);
18 |
19 | [DllImport("advapi32.dll")]
20 | public static extern void CredFree(IntPtr credentialPtr);
21 |
22 | [DllImport("credui.dll", CharSet = CharSet.Unicode)]
23 | public static extern CredUIReturnCodes CredUIPromptForWindowsCredentials(
24 | ref CREDUI_INFO uiInfo,
25 | int authError,
26 | ref int authPackage,
27 | IntPtr InAuthBuffer,
28 | int InAuthBufferSize,
29 | out IntPtr refOutAuthBuffer,
30 | out int refOutAuthBufferSize,
31 | ref bool fSave,
32 | PromptForWindowsCredentialsFlags flags);
33 |
34 | [DllImport("credui.dll", CharSet = CharSet.Auto)]
35 | public static extern bool CredUnPackAuthenticationBuffer(
36 | int dwFlags,
37 | IntPtr pAuthBuffer,
38 | int cbAuthBuffer,
39 | StringBuilder pszUserName,
40 | ref int pcchMaxUserName,
41 | StringBuilder pszDomainName,
42 | ref int pcchMaxDomainame,
43 | StringBuilder pszPassword,
44 | ref int pcchMaxPassword);
45 |
46 | [DllImport("credui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
47 | public static extern bool CredPackAuthenticationBuffer(
48 | int dwFlags,
49 | string pszUserName,
50 | string pszPassword,
51 | IntPtr pPackedCredentials,
52 | ref int pcbPackedCredentials);
53 |
54 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
55 | public struct CREDUI_INFO {
56 | public int cbSize;
57 | public IntPtr hwndParent;
58 | public string pszMessageText;
59 | public string pszCaptionText;
60 | public IntPtr hbmBanner;
61 | }
62 |
63 | public enum PromptForWindowsCredentialsFlags {
64 | ///
65 | /// The caller is requesting that the credential provider return the user name and password in plain text.
66 | /// This value cannot be combined with SECURE_PROMPT.
67 | ///
68 | CREDUIWIN_GENERIC = 0x1,
69 | ///
70 | /// The Save check box is displayed in the dialog box.
71 | ///
72 | CREDUIWIN_CHECKBOX = 0x2,
73 | ///
74 | /// Only credential providers that support the authentication package specified by the authPackage parameter should be enumerated.
75 | /// This value cannot be combined with CREDUIWIN_IN_CRED_ONLY.
76 | ///
77 | CREDUIWIN_AUTHPACKAGE_ONLY = 0x10,
78 | ///
79 | /// Only the credentials specified by the InAuthBuffer parameter for the authentication package specified by the authPackage parameter should be enumerated.
80 | /// If this flag is set, and the InAuthBuffer parameter is NULL, the function fails.
81 | /// This value cannot be combined with CREDUIWIN_AUTHPACKAGE_ONLY.
82 | ///
83 | CREDUIWIN_IN_CRED_ONLY = 0x20,
84 | ///
85 | /// 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.
86 | ///
87 | CREDUIWIN_ENUMERATE_ADMINS = 0x100,
88 | ///
89 | /// Only the incoming credentials for the authentication package specified by the authPackage parameter should be enumerated.
90 | ///
91 | CREDUIWIN_ENUMERATE_CURRENT_USER = 0x200,
92 | ///
93 | /// The credential dialog box should be displayed on the secure desktop. This value cannot be combined with CREDUIWIN_GENERIC.
94 | /// Windows Vista: This value is not supported until Windows Vista with SP1.
95 | ///
96 | CREDUIWIN_SECURE_PROMPT = 0x1000,
97 | ///
98 | /// 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.
99 | ///
100 | CREDUIWIN_PACK_32_WOW = 0x10000000,
101 | }
102 |
103 | public enum CRED_TYPE : int {
104 | GENERIC = 1,
105 | DOMAIN_PASSWORD = 2,
106 | DOMAIN_CERTIFICATE = 3,
107 | DOMAIN_VISIBLE_PASSWORD = 4,
108 | MAXIMUM = 5
109 | }
110 |
111 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
112 | public struct CREDENTIAL {
113 | public int flags;
114 | public int type;
115 | [MarshalAs(UnmanagedType.LPWStr)]
116 | public string targetName;
117 | [MarshalAs(UnmanagedType.LPWStr)]
118 | public string comment;
119 | public System.Runtime.InteropServices.ComTypes.FILETIME lastWritten;
120 | public int credentialBlobSize;
121 | public IntPtr credentialBlob;
122 | public int persist;
123 | public int attributeCount;
124 | public IntPtr credAttribute;
125 | [MarshalAs(UnmanagedType.LPWStr)]
126 | public string targetAlias;
127 | [MarshalAs(UnmanagedType.LPWStr)]
128 | public string userName;
129 | }
130 |
131 | public enum CredUIReturnCodes : int {
132 | NO_ERROR = 0,
133 | ERROR_CANCELLED = 1223,
134 | ERROR_NO_SUCH_LOGON_SESSION = 1312,
135 | ERROR_NOT_FOUND = 1168,
136 | ERROR_INVALID_ACCOUNT_NAME = 1315,
137 | ERROR_INSUFFICIENT_BUFFER = 122,
138 | ERROR_INVALID_PARAMETER = 87,
139 | ERROR_INVALID_FLAGS = 1004
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/PasswordPrompt.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace askpass {
2 | partial class PasswordPrompt {
3 | ///
4 | /// Required designer variable.
5 | ///
6 | private System.ComponentModel.IContainer components = null;
7 |
8 | ///
9 | /// Clean up any resources being used.
10 | ///
11 | /// true if managed resources should be disposed; otherwise, false.
12 | protected override void Dispose(bool disposing) {
13 | if(disposing && (components != null)) {
14 | components.Dispose();
15 | }
16 | base.Dispose(disposing);
17 | }
18 |
19 | #region Windows Form Designer generated code
20 |
21 | ///
22 | /// Required method for Designer support - do not modify
23 | /// the contents of this method with the code editor.
24 | ///
25 | private void InitializeComponent() {
26 | this.ok = new System.Windows.Forms.Button();
27 | this.cancel = new System.Windows.Forms.Button();
28 | this.panel1 = new System.Windows.Forms.Panel();
29 | this.saveCheckbox = new System.Windows.Forms.CheckBox();
30 | this.badPassLabel = new System.Windows.Forms.Label();
31 | this.passwordLabel = new System.Windows.Forms.Label();
32 | this.passwordText = new System.Windows.Forms.TextBox();
33 | this.label1 = new System.Windows.Forms.Label();
34 | this.panel1.SuspendLayout();
35 | this.SuspendLayout();
36 | //
37 | // ok
38 | //
39 | this.ok.Location = new System.Drawing.Point(236, 142);
40 | this.ok.Name = "ok";
41 | this.ok.Size = new System.Drawing.Size(75, 25);
42 | this.ok.TabIndex = 2;
43 | this.ok.Text = "OK";
44 | this.ok.UseVisualStyleBackColor = true;
45 | this.ok.Click += new System.EventHandler(this.ok_Click);
46 | //
47 | // cancel
48 | //
49 | this.cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
50 | this.cancel.Location = new System.Drawing.Point(317, 142);
51 | this.cancel.Name = "cancel";
52 | this.cancel.Size = new System.Drawing.Size(75, 25);
53 | this.cancel.TabIndex = 3;
54 | this.cancel.Text = "Cancel";
55 | this.cancel.UseVisualStyleBackColor = true;
56 | //
57 | // panel1
58 | //
59 | this.panel1.BackColor = System.Drawing.SystemColors.Window;
60 | this.panel1.Controls.Add(this.saveCheckbox);
61 | this.panel1.Controls.Add(this.badPassLabel);
62 | this.panel1.Controls.Add(this.passwordLabel);
63 | this.panel1.Controls.Add(this.passwordText);
64 | this.panel1.Controls.Add(this.label1);
65 | this.panel1.ForeColor = System.Drawing.SystemColors.ControlText;
66 | this.panel1.Location = new System.Drawing.Point(-5, -8);
67 | this.panel1.Name = "panel1";
68 | this.panel1.Size = new System.Drawing.Size(415, 144);
69 | this.panel1.TabIndex = 2;
70 | //
71 | // saveCheckbox
72 | //
73 | this.saveCheckbox.AutoSize = true;
74 | this.saveCheckbox.Location = new System.Drawing.Point(21, 113);
75 | this.saveCheckbox.Name = "saveCheckbox";
76 | this.saveCheckbox.Size = new System.Drawing.Size(102, 17);
77 | this.saveCheckbox.TabIndex = 3;
78 | this.saveCheckbox.Text = "Save password";
79 | this.saveCheckbox.UseVisualStyleBackColor = true;
80 | //
81 | // badPassLabel
82 | //
83 | this.badPassLabel.AutoSize = true;
84 | this.badPassLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
85 | this.badPassLabel.ForeColor = System.Drawing.Color.Red;
86 | this.badPassLabel.Location = new System.Drawing.Point(18, 41);
87 | this.badPassLabel.Name = "badPassLabel";
88 | this.badPassLabel.Size = new System.Drawing.Size(137, 15);
89 | this.badPassLabel.TabIndex = 2;
90 | this.badPassLabel.Text = "Bad password, try again";
91 | this.badPassLabel.Visible = false;
92 | //
93 | // passwordLabel
94 | //
95 | this.passwordLabel.AutoSize = true;
96 | this.passwordLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
97 | this.passwordLabel.Location = new System.Drawing.Point(18, 59);
98 | this.passwordLabel.Name = "passwordLabel";
99 | this.passwordLabel.Size = new System.Drawing.Size(217, 15);
100 | this.passwordLabel.TabIndex = 0;
101 | this.passwordLabel.Text = "Password for /c/Users/Luke/.ssh//id_rsa";
102 | //
103 | // passwordText
104 | //
105 | this.passwordText.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
106 | this.passwordText.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
107 | this.passwordText.ForeColor = System.Drawing.SystemColors.WindowText;
108 | this.passwordText.Location = new System.Drawing.Point(19, 82);
109 | this.passwordText.Name = "passwordText";
110 | this.passwordText.Size = new System.Drawing.Size(378, 23);
111 | this.passwordText.TabIndex = 1;
112 | this.passwordText.UseSystemPasswordChar = true;
113 | this.passwordText.TextChanged += new System.EventHandler(this.passwordText_TextChanged);
114 | //
115 | // label1
116 | //
117 | this.label1.AutoSize = true;
118 | this.label1.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
119 | this.label1.ForeColor = System.Drawing.Color.MediumBlue;
120 | this.label1.Location = new System.Drawing.Point(17, 17);
121 | this.label1.Name = "label1";
122 | this.label1.Size = new System.Drawing.Size(191, 21);
123 | this.label1.TabIndex = 0;
124 | this.label1.Text = "SSH Private Key Password";
125 | //
126 | // PasswordPrompt
127 | //
128 | this.AcceptButton = this.ok;
129 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
130 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
131 | this.CancelButton = this.cancel;
132 | this.ClientSize = new System.Drawing.Size(404, 179);
133 | this.Controls.Add(this.panel1);
134 | this.Controls.Add(this.cancel);
135 | this.Controls.Add(this.ok);
136 | this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
137 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
138 | this.Name = "PasswordPrompt";
139 | this.ShowIcon = false;
140 | this.ShowInTaskbar = false;
141 | this.Text = "SSH";
142 | this.panel1.ResumeLayout(false);
143 | this.panel1.PerformLayout();
144 | this.ResumeLayout(false);
145 |
146 | }
147 |
148 | #endregion
149 |
150 | private System.Windows.Forms.Button ok;
151 | private System.Windows.Forms.Button cancel;
152 | private System.Windows.Forms.Panel panel1;
153 | private System.Windows.Forms.Label label1;
154 | private System.Windows.Forms.TextBox passwordText;
155 | private System.Windows.Forms.Label passwordLabel;
156 | private System.Windows.Forms.Label badPassLabel;
157 | private System.Windows.Forms.CheckBox saveCheckbox;
158 | }
159 | }
--------------------------------------------------------------------------------
/PasswordPrompt.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Data;
5 | using System.Drawing;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 |
11 | namespace askpass {
12 | public partial class PasswordPrompt : Form {
13 | public PasswordPrompt(string key, bool badPass) {
14 | InitializeComponent();
15 |
16 | passwordLabel.Text = "Password for " + key;
17 | passwordText.Focus();
18 | if(badPass) { badPassLabel.Visible = true; }
19 | }
20 |
21 | public string Password {
22 | get { return passwordText.Text; }
23 | }
24 |
25 | public bool Save {
26 | get { return saveCheckbox.Checked; }
27 | }
28 |
29 | private void ok_Click(object sender, EventArgs e) {
30 | this.DialogResult = DialogResult.OK;
31 | }
32 |
33 | private void passwordText_TextChanged(object sender, EventArgs e) {
34 | badPassLabel.Visible = false;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/PasswordPrompt.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Runtime.InteropServices;
8 | using System.Text;
9 | using System.Text.RegularExpressions;
10 | using System.Threading.Tasks;
11 | using System.Windows.Forms;
12 |
13 | namespace askpass {
14 | class Program {
15 | static string log = null;
16 |
17 | static void Main(string[] args) {
18 | log = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "askpass.log");
19 | if(File.Exists(log)) { File.Delete(log); }
20 |
21 | var lastPassFail = false;
22 | var m = Regex.Match(args[0], "Enter passphrase for ([^:]+)", RegexOptions.IgnoreCase);
23 | if(!m.Success) {
24 | m = Regex.Match(args[0], "Bad passphrase, try again for ([^:]+)", RegexOptions.IgnoreCase);
25 | if(m.Success) {
26 | Log("last password failed");
27 | lastPassFail = true;
28 | } else {
29 | Log("error parsing arguments: " + string.Join(", ", args));
30 | Environment.Exit(1);
31 | }
32 | }
33 |
34 | var key = m.Groups[1].Value;
35 |
36 | // make it look like a windows path
37 | key = Regex.Replace(key, "/{2,}", "/"); // clean double slashes
38 | key = Regex.Replace(key, "^/([A-Za-z])/", "$1:/"); // drive
39 | key = Regex.Replace(key, "/", "\\"); // use backslashes
40 |
41 | Log("key: " + key);
42 |
43 | string password = null;
44 | if(lastPassFail) {
45 | Log("removing saved password");
46 | DeletePassword(key);
47 | } else {
48 | password = SavedPassword(key);
49 | }
50 |
51 | if(password != null) {
52 | Log("found saved password");
53 | Console.WriteLine(password); // send password to ssh-add
54 | } else {
55 | Log("no password saved for " + key);
56 | bool save;
57 | password = PromptForPassword(key, lastPassFail, out save);
58 | if(password == null) { // empty or cancelled
59 | // failed: agent should ignore output and not call askpass again
60 | Environment.Exit(1);
61 | }
62 | Log("collected password");
63 | if(save) {
64 | Log("saving password");
65 | SavePassword(key, password);
66 | }
67 | Console.WriteLine(password); // send password to ssh-add
68 | }
69 | }
70 |
71 | static string PromptForPassword(string key, bool lastPassFail, out bool save) {
72 | Application.EnableVisualStyles();
73 | var prompt = new PasswordPrompt(key, lastPassFail);
74 | var res = prompt.ShowDialog();
75 | save = false;
76 | if(res == DialogResult.OK) {
77 | if(string.IsNullOrEmpty(prompt.Password)) return null;
78 | save = prompt.Save;
79 | return prompt.Password;
80 | }
81 | return null;
82 | }
83 |
84 | static string SavedPassword(string key) {
85 | IntPtr credPtr = IntPtr.Zero;
86 | var ok = NativeMethods.CredRead("ssh:" + key, NativeMethods.CRED_TYPE.GENERIC, 0, out credPtr);
87 | if(!ok) return null;
88 |
89 | // Decode the credential
90 | var cred = (NativeMethods.CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(NativeMethods.CREDENTIAL));
91 |
92 | var bytes = new byte[cred.credentialBlobSize];
93 | Marshal.Copy(cred.credentialBlob, bytes, 0, bytes.Length);
94 | return Encoding.Unicode.GetString(bytes);
95 | }
96 |
97 | static bool SavePassword(string key, string password) {
98 | string target = "ssh:" + key;
99 | NativeMethods.CREDENTIAL cred = new NativeMethods.CREDENTIAL() {
100 | type = 0x01, // Generic
101 | targetName = target,
102 | credentialBlob = Marshal.StringToCoTaskMemUni(password),
103 | persist = 0x02, // Local machine
104 | attributeCount = 0,
105 | userName = "(n/a)"
106 | };
107 | cred.credentialBlobSize = Encoding.Unicode.GetByteCount(password);
108 | if(!NativeMethods.CredWrite(ref cred, 0)) {
109 | Log("Failed to write credential: " + GetLastErrorMessage());
110 | return false;
111 | }
112 | return true;
113 | }
114 |
115 | static void DeletePassword(string key) {
116 | var target = "ssh:" + key;
117 | if (!NativeMethods.CredDelete(target, NativeMethods.CRED_TYPE.GENERIC, 0)) {
118 | Log("Failed to delete credential: " + GetLastErrorMessage());
119 | }
120 | }
121 |
122 | static string GetLastErrorMessage() {
123 | return new Win32Exception(Marshal.GetLastWin32Error()).Message;
124 | }
125 |
126 | static void Log(string message) {
127 | File.AppendAllText(log, message + "\r\n");
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("askpass")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("askpass")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("9c0acefc-f598-4cb9-b331-c790e3ddf049")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/askpass.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {7808B5D5-36F9-44C5-858B-B47F2BCA7592}
8 | Exe
9 | Properties
10 | askpass
11 | askpass
12 | v4.5
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | Form
49 |
50 |
51 | PasswordPrompt.cs
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | PasswordPrompt.cs
63 |
64 |
65 |
66 |
73 |
--------------------------------------------------------------------------------
/askpass.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Express 2012 for Windows Desktop
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "askpass", "askpass.csproj", "{7808B5D5-36F9-44C5-858B-B47F2BCA7592}"
5 | EndProject
6 | Global
7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
8 | Debug|Any CPU = Debug|Any CPU
9 | Release|Any CPU = Release|Any CPU
10 | EndGlobalSection
11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
12 | {7808B5D5-36F9-44C5-858B-B47F2BCA7592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
13 | {7808B5D5-36F9-44C5-858B-B47F2BCA7592}.Debug|Any CPU.Build.0 = Debug|Any CPU
14 | {7808B5D5-36F9-44C5-858B-B47F2BCA7592}.Release|Any CPU.ActiveCfg = Release|Any CPU
15 | {7808B5D5-36F9-44C5-858B-B47F2BCA7592}.Release|Any CPU.Build.0 = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(SolutionProperties) = preSolution
18 | HideSolutionNode = FALSE
19 | EndGlobalSection
20 | EndGlobal
21 |
--------------------------------------------------------------------------------
/test.ps1:
--------------------------------------------------------------------------------
1 | $env:SSH_ASKPASS = "$pwd\bin\debug\askpass.exe"
2 | $env:DISPLAY = "localhost:0.0"
3 |
4 | write-host "use ```$null | ssh-add`` to trigger"
--------------------------------------------------------------------------------