Skip to content

Commit c7eafd9

Browse files
committed
Change -x option to flag, read password from stdin
1 parent 5a59928 commit c7eafd9

File tree

3 files changed

+48
-27
lines changed

3 files changed

+48
-27
lines changed

Sshfs/SSHFS.CLI/Program.cs

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
using System.Text;
1212
using System.Text.RegularExpressions;
1313
using System.Threading.Tasks;
14+
using CommandLine.Text;
15+
using DokanNet.Logging;
1416

1517
namespace SSHFS.CLI
1618
{
@@ -21,6 +23,7 @@ class Options
2123
Required = true,
2224
HelpText = "Drive letter to mount the remote SFTP path under")]
2325
public char DriveLetter { get; set; }
26+
2427
[Option('r', "path",
2528
Required = true,
2629
HelpText = "Absolute path of directory to be mounted from remote system")]
@@ -31,8 +34,9 @@ class Options
3134
Required = true,
3235
HelpText = "IP or hostname of remote host")]
3336
public string Host { get; set; }
37+
3438
[Option('p', "port",
35-
Required = false, DefaultValue = 22,
39+
Required = false, Default = 22,
3640
HelpText = "SSH service port on remote server")]
3741
public int Port { get; set; }
3842

@@ -41,31 +45,37 @@ class Options
4145
Required = true,
4246
HelpText = "Name of SSH user on remote system")]
4347
public string Username { get; set; }
48+
4449
[Option('x', "password",
4550
Required = false,
46-
HelpText = "INSECURE: SSH user's password, if password-based or keyboard-interactive auth should be attempted. Note that the security model for this is equivalent to sshpass's -p option, and exposes your password to any process on the machine that cares to run ps")]
47-
public string Password { get; set; }
48-
[OptionArray('k', "private-keys",
51+
HelpText = "Read password from stdin")]
52+
public bool Password { get; set; }
53+
54+
[Option('k', "private-keys",
4955
Required = false,
5056
HelpText = "Path to SSH user's private key(s), if key-based auth should be attempted")]
51-
public string[] Keys { get; set; }
57+
public IEnumerable<string> Keys { get; set; }
5258
}
5359

5460
class Program
5561
{
5662
static void Main(string[] args)
5763
{
58-
var options = new Options();
59-
Parser.Default.ParseArgumentsStrict(args, options);
64+
Parser.Default.ParseArguments<Options>(args)
65+
.WithParsed(Start);
66+
}
6067

68+
static void Start(Options options)
69+
{
6170
var auths = GetAuthMechanisms(options);
6271

6372
var fs = auths
6473
.Select(auth => AttemptConnection(auth.Item1, auth.Item2, options.Path))
6574
.FirstOrDefault(result => result != null);
6675

6776
if (fs == null)
68-
throw new InvalidOperationException("Could not connect to server with any known authentication mechanism");
77+
throw new InvalidOperationException(
78+
"Could not connect to server with any known authentication mechanism");
6979

7080
fs.Mount($"{options.DriveLetter}");
7181
}
@@ -76,37 +86,43 @@ static void Main(string[] args)
7686

7787
if (options.Keys != null)
7888
{
79-
auths.AddRange(new(string, ConnectionInfo)[] {
89+
auths.AddRange(new(string, ConnectionInfo)[]
90+
{
8091
("private key", PrivateKeyConnectionInfo(options))
8192
});
8293
}
83-
84-
if (options.Password != null)
94+
else if (options.Password)
8595
{
86-
auths.AddRange(new(string, ConnectionInfo)[] {
87-
("password", new PasswordConnectionInfo(options.Host, options.Port, options.Username, options.Password)),
88-
("keyboard-interactive", KeyboardInteractiveConnectionInfo(options))
96+
Console.WriteLine("No SSH key file selected, using password auth instead.");
97+
var pass = ReadLine.ReadPassword("Please enter password: ");
98+
99+
auths.AddRange(new(string, ConnectionInfo)[]
100+
{
101+
("password", new PasswordConnectionInfo(options.Host, options.Port, options.Username, pass)),
102+
("keyboard-interactive", KeyboardInteractiveConnectionInfo(options, pass))
89103
});
90104
}
105+
else
106+
{
107+
Console.WriteLine(
108+
"No key files specified, and password auth not enabled (win-sshfs does not search for private keys). Aborting...");
109+
Environment.Exit(1);
110+
}
91111

92112
return auths;
93113
}
94114

95115
static PrivateKeyConnectionInfo PrivateKeyConnectionInfo(Options options)
96116
{
97117
var pkFiles = options.Keys.Select(k =>
98-
{
99-
var match = Regex.Match(k, @"(?<keyfile>\S+)(:?\s+ (?<passphrase>\S+))?");
100-
var keyfile = match.Groups["keyfile"];
101-
var passphrase = match.Groups["passphrase"];
102-
103-
return passphrase.Success ? new PrivateKeyFile(keyfile.Value, passphrase.Value) : new PrivateKeyFile(keyfile.Value);
104-
});
118+
options.Password
119+
? new PrivateKeyFile(k, ReadLine.ReadPassword($"Enter passphrase for {k}: "))
120+
: new PrivateKeyFile(k));
105121

106122
return new PrivateKeyConnectionInfo(options.Host, options.Port, options.Username, pkFiles.ToArray());
107123
}
108124

109-
static KeyboardInteractiveConnectionInfo KeyboardInteractiveConnectionInfo(Options options)
125+
static KeyboardInteractiveConnectionInfo KeyboardInteractiveConnectionInfo(Options options, string pass)
110126
{
111127
var auth = new KeyboardInteractiveConnectionInfo(options.Host, options.Port, options.Username);
112128

@@ -115,7 +131,7 @@ static KeyboardInteractiveConnectionInfo KeyboardInteractiveConnectionInfo(Optio
115131
var passPrompts = e.Prompts
116132
.Where(p => p.Request.StartsWith("Password:"));
117133
foreach (var p in passPrompts)
118-
p.Response = options.Password;
134+
p.Response = pass;
119135
};
120136

121137
return auth;
@@ -132,9 +148,10 @@ static SftpFilesystem AttemptConnection(string authType, ConnectionInfo connInfo
132148
}
133149
catch (SshAuthenticationException)
134150
{
135-
Console.WriteLine($"Failed to authenticate using {authType}, falling back to next auth mechanism if available.");
151+
Console.WriteLine(
152+
$"Failed to authenticate using {authType}, falling back to next auth mechanism if available.");
136153
return null;
137154
}
138155
}
139156
}
140-
}
157+
}

Sshfs/SSHFS.CLI/SSHFS.CLI.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,14 @@
6060
</ItemGroup>
6161
<ItemGroup>
6262
<PackageReference Include="CommandLineParser">
63-
<Version>1.9.71</Version>
63+
<Version>2.2.1</Version>
6464
</PackageReference>
6565
<PackageReference Include="DokanNet">
6666
<Version>1.1.1</Version>
6767
</PackageReference>
68+
<PackageReference Include="ReadLine">
69+
<Version>2.0.0</Version>
70+
</PackageReference>
6871
<PackageReference Include="System.ValueTuple">
6972
<Version>4.3.1</Version>
7073
</PackageReference>

appveyor.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
version: 1.0.{build}
1+
version: 2.0.{build}
22
skip_tags: true
3+
skip_branch_with_pr: true
34
image: Visual Studio 2017
45
configuration: Release
56
install:

0 commit comments

Comments
 (0)