Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/duplicati/duplicati.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax <michal@naiman.eu>2016-06-24 16:03:08 +0300
committerMax <michal@naiman.eu>2016-06-24 16:03:08 +0300
commite53f5808d9eb6efb89055768b7ae9ed02e3832b6 (patch)
treeb87b7caa28700d8a4bf16a41e62ae668387bd368 /Duplicati/Library/Backend/SSHv2
parent0bb4494114638c8fb1196fa404f6b9488406543c (diff)
add ability to check server fingerprint
Diffstat (limited to 'Duplicati/Library/Backend/SSHv2')
-rw-r--r--Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs29
-rw-r--r--Duplicati/Library/Backend/SSHv2/Strings.cs3
2 files changed, 30 insertions, 2 deletions
diff --git a/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs b/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
index 883b7d89d..c5f96e431 100644
--- a/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
+++ b/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
@@ -31,6 +31,7 @@ namespace Duplicati.Library.Backend
{
public const string SSH_KEYFILE_OPTION = "ssh-keyfile";
public const string SSH_KEYFILE_INLINE = "ssh-key";
+ public const string SSH_FINGERPRINT_OPTION = "ssh-fingerprint";
public const string KEYFILE_URI = "sshkey://";
Dictionary<string, string> m_options;
@@ -39,6 +40,7 @@ namespace Duplicati.Library.Backend
private string m_path;
private string m_username;
private string m_password;
+ private string m_fingerprint;
private int m_port = 22;
@@ -59,10 +61,14 @@ namespace Duplicati.Library.Backend
m_username = options["auth-username"];
if (options.ContainsKey("auth-password"))
m_password = options["auth-password"];
- if (!string.IsNullOrEmpty(uri .Username))
+ if (options.ContainsKey(SSH_FINGERPRINT_OPTION))
+ m_fingerprint = options[SSH_FINGERPRINT_OPTION];
+ if (!string.IsNullOrEmpty(uri.Username))
m_username = uri.Username;
- if (!string.IsNullOrEmpty(uri .Password))
+ if (!string.IsNullOrEmpty(uri.Password))
m_password = uri.Password;
+ if (uri.QueryParameters != null && uri.QueryParameters[SSH_FINGERPRINT_OPTION] != null)
+ m_fingerprint = uri.QueryParameters[SSH_FINGERPRINT_OPTION];
m_path = uri.Path;
@@ -136,6 +142,7 @@ namespace Duplicati.Library.Backend
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument("auth-password", CommandLineArgument.ArgumentType.Password, Strings.SSHv2Backend.DescriptionAuthPasswordShort, Strings.SSHv2Backend.DescriptionAuthPasswordLong),
new CommandLineArgument("auth-username", CommandLineArgument.ArgumentType.String, Strings.SSHv2Backend.DescriptionAuthUsernameShort, Strings.SSHv2Backend.DescriptionAuthUsernameLong),
+ new CommandLineArgument(SSH_FINGERPRINT_OPTION, CommandLineArgument.ArgumentType.String, Strings.SSHv2Backend.DescriptionFingerprintShort, Strings.SSHv2Backend.DescriptionFingerprintLong),
new CommandLineArgument(SSH_KEYFILE_OPTION, CommandLineArgument.ArgumentType.Path, Strings.SSHv2Backend.DescriptionSshkeyfileShort, Strings.SSHv2Backend.DescriptionSshkeyfileLong),
new CommandLineArgument(SSH_KEYFILE_INLINE, CommandLineArgument.ArgumentType.Password, Strings.SSHv2Backend.DescriptionSshkeyShort, Strings.SSHv2Backend.DescriptionSshkeyLong(KEYFILE_URI)),
});
@@ -205,6 +212,24 @@ namespace Duplicati.Library.Backend
else
con = new SftpClient(m_server, m_port, m_username, m_password);
+ con.HostKeyReceived += delegate (object sender, HostKeyEventArgs e)
+ {
+ e.CanTrust = false;
+
+ if (string.IsNullOrEmpty(m_fingerprint))
+ {
+ e.CanTrust = true;
+ return;
+ }
+
+ string hostFingerprint = e.HostKeyName + " " + e.KeyLength.ToString() + " " + BitConverter.ToString(e.FingerPrint).Replace('-', ':');
+
+ if (hostFingerprint.ToLower() != m_fingerprint.ToLower())
+ throw new Exception(Strings.SSHv2Backend.FingerprintNotMatchManagedError(hostFingerprint.ToLower()));
+ else
+ e.CanTrust = true;
+ };
+
con.Connect();
try
diff --git a/Duplicati/Library/Backend/SSHv2/Strings.cs b/Duplicati/Library/Backend/SSHv2/Strings.cs
index e7b31352c..0cd6a6982 100644
--- a/Duplicati/Library/Backend/SSHv2/Strings.cs
+++ b/Duplicati/Library/Backend/SSHv2/Strings.cs
@@ -24,11 +24,14 @@ namespace Duplicati.Library.Backend.Strings {
public static string DescriptionAuthPasswordShort { get { return LC.L(@"Supplies the password used to connect to the server"); } }
public static string DescriptionAuthUsernameLong { get { return LC.L(@"The username used to connect to the server. This may also be supplied as the environment variable ""AUTH_USERNAME""."); } }
public static string DescriptionAuthUsernameShort { get { return LC.L(@"Supplies the username used to connect to the server"); } }
+ public static string DescriptionFingerprintLong { get { return LC.L(@"The server fingerprint used for validation of server identity. Format is eg. ""ssh - rsa 4096 11:22:33:44:55:66:77:88:99:00:11:22:33:44:55:66""."); } }
+ public static string DescriptionFingerprintShort { get { return LC.L(@"Supplies server fingerprint used for validation of server identity"); } }
public static string DescriptionSshkeyfileLong { get { return LC.L(@"Points to a valid OpenSSH keyfile. If the file is encrypted, the password supplied is used to decrypt the keyfile. If this option is supplied, the password is not used to authenticate. This option only works when using the managed SSH client."); } }
public static string DescriptionSshkeyfileShort { get { return LC.L(@"Uses a SSH private key to authenticate"); } }
public static string DescriptionSshkeyLong(string urlprefix) { return LC.L(@"An url-encoded SSH private key. The private key must be prefixed with {0}. If the file is encrypted, the password supplied is used to decrypt the keyfile. If this option is supplied, the password is not used to authenticate. This option only works when using the managed SSH client.", urlprefix); }
public static string DescriptionSshkeyShort { get { return LC.L(@"Uses a SSH private key to authenticate"); } }
public static string DisplayName { get { return LC.L(@"SFTP (SSH)"); } }
public static string FolderNotFoundManagedError(string foldername, string message) { return LC.L(@"Unable to set folder to {0}, error message: {1}", foldername, message); }
+ public static string FingerprintNotMatchManagedError(string fingerprint) { return LC.L(@"Validation of server fingerprint failed. Server returned fingerprint ""{0}"". Cause of this message is either not correct configuration or Man-in-the-middle attack!", fingerprint); }
}
}