using System; using System.Collections.Generic; using System.Linq; using System.Text; using Renci.SshNet.Security; using Renci.SshNet.Messages.Connection; using Renci.SshNet.Common; using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Security.Cryptography; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; using Renci.SshNet.Security.Cryptography.Ciphers; namespace Renci.SshNet { /// /// Represents remote connection information class. /// public class ConnectionInfo { internal static int DEFAULT_PORT = 22; /// /// Gets supported key exchange algorithms for this connection. /// public IDictionary KeyExchangeAlgorithms { get; private set; } /// /// Gets supported encryptions for this connection. /// public IDictionary Encryptions { get; private set; } /// /// Gets supported hash algorithms for this connection. /// public IDictionary HmacAlgorithms { get; private set; } /// /// Gets supported host key algorithms for this connection. /// public IDictionary> HostKeyAlgorithms { get; private set; } /// /// Gets supported authentication methods for this connection. /// public IEnumerable AuthenticationMethods { get; private set; } /// /// Gets supported compression algorithms for this connection. /// public IDictionary CompressionAlgorithms { get; private set; } /// /// Gets supported channel requests for this connection. /// public IDictionary ChannelRequests { get; private set; } /// /// Gets a value indicating whether connection is authenticated. /// /// /// true if connection is authenticated; otherwise, false. /// public bool IsAuthenticated { get; private set; } /// /// Gets connection host. /// public string Host { get; private set; } /// /// Gets connection port. /// public int Port { get; private set; } /// /// Gets connection username. /// public string Username { get; private set; } /// /// Gets proxy type. /// /// /// The type of the proxy. /// public ProxyTypes ProxyType { get; private set; } /// /// Gets proxy connection host. /// public string ProxyHost { get; private set; } /// /// Gets proxy connection port. /// public int ProxyPort { get; private set; } /// /// Gets proxy connection username. /// public string ProxyUsername { get; private set; } /// /// Gets proxy connection password. /// public string ProxyPassword { get; private set; } /// /// Gets or sets connection timeout. /// /// /// Connection timeout. /// /// /// /// public TimeSpan Timeout { get; set; } /// /// Gets or sets the default encoding. /// /// /// The default encoding. /// public Encoding Encoding { get; set; } /// /// Gets or sets number of retry attempts when session channel creation failed. /// /// /// Number of retry attempts. /// public int RetryAttempts { get; set; } /// /// Gets or sets maximum number of session channels to be open simultaneously. /// /// /// The max sessions. /// public int MaxSessions { get; set; } /// /// Occurs when authentication banner is sent by the server. /// /// /// /// public event EventHandler AuthenticationBanner; /// /// Gets the current key exchange algorithm. /// public string CurrentKeyExchangeAlgorithm { get; internal set; } /// /// Gets the current server encryption. /// public string CurrentServerEncryption { get; internal set; } /// /// Gets the current client encryption. /// public string CurrentClientEncryption { get; internal set; } /// /// Gets the current server hash algorithm. /// public string CurrentServerHmacAlgorithm { get; internal set; } /// /// Gets the current client hash algorithm. /// public string CurrentClientHmacAlgorithm { get; internal set; } /// /// Gets the current host key algorithm. /// public string CurrentHostKeyAlgorithm { get; internal set; } /// /// Gets the current server compression algorithm. /// public string CurrentServerCompressionAlgorithm { get; internal set; } /// /// Gets the server version. /// public string ServerVersion { get; internal set; } /// /// Get the client version. /// public string ClientVersion { get; internal set; } /// /// Gets the current client compression algorithm. /// public string CurrentClientCompressionAlgorithm { get; internal set; } /// /// Initializes a new instance of the class. /// /// The host. /// The username. /// The authentication methods. public ConnectionInfo(string host, string username, params AuthenticationMethod[] authenticationMethods) : this(host, ConnectionInfo.DEFAULT_PORT, username, ProxyTypes.None, null, 0, null, null, authenticationMethods) { } /// /// Initializes a new instance of the class. /// /// The host. /// The port. /// The username. /// The authentication methods. public ConnectionInfo(string host, int port, string username, params AuthenticationMethod[] authenticationMethods) : this(host, port, username, ProxyTypes.None, null, 0, null, null, authenticationMethods) { } // TODO: DOCS Add exception documentation for this class. /// /// Initializes a new instance of the class. /// /// Connection host. /// Connection port. /// Connection username. /// Type of the proxy. /// The proxy host. /// The proxy port. /// The proxy username. /// The proxy password. /// The authentication methods. /// host /// proxyPort /// is invalid, or is null or contains whitespace characters. /// is not within and . /// is invalid, or is null or contains whitespace characters. public ConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params AuthenticationMethod[] authenticationMethods) { if (!host.IsValidHost()) throw new ArgumentException("host"); if (proxyType != ProxyTypes.None) { if (string.IsNullOrEmpty(proxyHost) && !proxyHost.IsValidHost()) throw new ArgumentException("proxyHost"); if (!proxyPort.IsValidPort()) throw new ArgumentOutOfRangeException("proxyPort"); } if (!port.IsValidPort()) throw new ArgumentOutOfRangeException("port"); if (username.IsNullOrWhiteSpace()) throw new ArgumentException("username"); if (authenticationMethods == null || authenticationMethods.Length < 1) throw new ArgumentException("authenticationMethods"); // Set default connection values this.Timeout = TimeSpan.FromSeconds(30); this.RetryAttempts = 10; this.MaxSessions = 10; this.Encoding = Encoding.UTF8; this.KeyExchangeAlgorithms = new Dictionary() { {"diffie-hellman-group-exchange-sha256", typeof(KeyExchangeDiffieHellmanGroupExchangeSha256)}, {"diffie-hellman-group-exchange-sha1", typeof(KeyExchangeDiffieHellmanGroupExchangeSha1)}, {"diffie-hellman-group14-sha1", typeof(KeyExchangeDiffieHellmanGroup14Sha1)}, {"diffie-hellman-group1-sha1", typeof(KeyExchangeDiffieHellmanGroup1Sha1)}, //{"ecdh-sha2-nistp256", typeof(KeyExchangeEllipticCurveDiffieHellman)}, //{"ecdh-sha2-nistp256", typeof(...)}, //{"ecdh-sha2-nistp384", typeof(...)}, //{"ecdh-sha2-nistp521", typeof(...)}, //"gss-group1-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD //"gss-gex-sha1-toWM5Slw5Ew8Mqkay+al2g==" - WinSSHD }; this.Encryptions = new Dictionary() { {"aes256-ctr", new CipherInfo(256, (key, iv)=>{ return new AesCipher(key, new CtrCipherMode(iv), null); }) }, {"3des-cbc", new CipherInfo(192, (key, iv)=>{ return new TripleDesCipher(key, new CbcCipherMode(iv), null); }) }, {"aes128-cbc", new CipherInfo(128, (key, iv)=>{ return new AesCipher(key, new CbcCipherMode(iv), null); }) }, {"aes192-cbc", new CipherInfo(192, (key, iv)=>{ return new AesCipher(key, new CbcCipherMode(iv), null); }) }, {"aes256-cbc", new CipherInfo(256, (key, iv)=>{ return new AesCipher(key, new CbcCipherMode(iv), null); }) }, {"blowfish-cbc", new CipherInfo(128, (key, iv)=>{ return new BlowfishCipher(key, new CbcCipherMode(iv), null); }) }, {"twofish-cbc", new CipherInfo(256, (key, iv)=>{ return new TwofishCipher(key, new CbcCipherMode(iv), null); }) }, {"twofish192-cbc", new CipherInfo(192, (key, iv)=>{ return new TwofishCipher(key, new CbcCipherMode(iv), null); }) }, {"twofish128-cbc", new CipherInfo(128, (key, iv)=>{ return new TwofishCipher(key, new CbcCipherMode(iv), null); }) }, {"twofish256-cbc", new CipherInfo(256, (key, iv)=>{ return new TwofishCipher(key, new CbcCipherMode(iv), null); }) }, ////{"serpent256-cbc", typeof(CipherSerpent256CBC)}, ////{"serpent192-cbc", typeof(...)}, ////{"serpent128-cbc", typeof(...)}, {"arcfour", new CipherInfo(128, (key, iv)=>{ return new Arc4Cipher(key, false); }) }, {"arcfour128", new CipherInfo(128, (key, iv)=>{ return new Arc4Cipher(key, true); }) }, {"arcfour256", new CipherInfo(256, (key, iv)=>{ return new Arc4Cipher(key, true); }) }, ////{"idea-cbc", typeof(...)}, {"cast128-cbc", new CipherInfo(128, (key, iv)=>{ return new CastCipher(key, new CbcCipherMode(iv), null); }) }, ////{"rijndael-cbc@lysator.liu.se", typeof(...)}, {"aes128-ctr", new CipherInfo(128, (key, iv)=>{ return new AesCipher(key, new CtrCipherMode(iv), null); }) }, {"aes192-ctr", new CipherInfo(192, (key, iv)=>{ return new AesCipher(key, new CtrCipherMode(iv), null); }) }, }; this.HmacAlgorithms = new Dictionary() { {"hmac-md5", new HashInfo(16 * 8, (key)=>{ return new HMac(key); }) }, {"hmac-sha1", new HashInfo(20 * 8, (key)=>{ return new HMac(key); }) }, {"hmac-sha2-256", new HashInfo(32 * 8, (key)=>{ return new HMac(key); }) }, {"hmac-sha2-256-96", new HashInfo(32 * 8, (key)=>{ return new HMac(key, 96); }) }, //{"hmac-sha2-512", new HashInfo(64 * 8, (key)=>{ return new HMac(key); }) }, //{"hmac-sha2-512-96", new HashInfo(64 * 8, (key)=>{ return new HMac(key, 96); }) }, //{"umac-64@openssh.com", typeof(HMacSha1)}, {"hmac-ripemd160", new HashInfo(160, (key)=>{ return new HMac(key); }) }, {"hmac-ripemd160@openssh.com", new HashInfo(160, (key)=>{ return new HMac(key); }) }, {"hmac-md5-96", new HashInfo(16 * 8, (key)=>{ return new HMac(key, 96); }) }, {"hmac-sha1-96", new HashInfo(20 * 8, (key)=>{ return new HMac(key, 96); }) }, //{"none", typeof(...)}, }; this.HostKeyAlgorithms = new Dictionary>() { {"ssh-rsa", (data) => { return new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data); }}, {"ssh-dss", (data) => { return new KeyHostAlgorithm("ssh-dss", new DsaKey(), data); }}, //{"ecdsa-sha2-nistp256 "} //{"x509v3-sign-rsa", () => { ... }, //{"x509v3-sign-dss", () => { ... }, //{"spki-sign-rsa", () => { ... }, //{"spki-sign-dss", () => { ... }, //{"pgp-sign-rsa", () => { ... }, //{"pgp-sign-dss", () => { ... }, }; this.CompressionAlgorithms = new Dictionary() { //{"zlib@openssh.com", typeof(ZlibOpenSsh)}, //{"zlib", typeof(Zlib)}, {"none", null}, }; this.ChannelRequests = new Dictionary() { {EnvironmentVariableRequestInfo.NAME, new EnvironmentVariableRequestInfo()}, {ExecRequestInfo.NAME, new ExecRequestInfo()}, {ExitSignalRequestInfo.NAME, new ExitSignalRequestInfo()}, {ExitStatusRequestInfo.NAME, new ExitStatusRequestInfo()}, {PseudoTerminalRequestInfo.NAME, new PseudoTerminalRequestInfo()}, {ShellRequestInfo.NAME, new ShellRequestInfo()}, {SignalRequestInfo.NAME, new SignalRequestInfo()}, {SubsystemRequestInfo.NAME, new SubsystemRequestInfo()}, {WindowChangeRequestInfo.NAME, new WindowChangeRequestInfo()}, {X11ForwardingRequestInfo.NAME, new X11ForwardingRequestInfo()}, {XonXoffRequestInfo.NAME, new XonXoffRequestInfo()}, {EndOfWriteRequestInfo.NAME, new EndOfWriteRequestInfo()}, {KeepAliveRequestInfo.NAME, new KeepAliveRequestInfo()}, }; this.Host = host; this.Port = port; this.Username = username; this.ProxyType = proxyType; this.ProxyHost = proxyHost; this.ProxyPort = proxyPort; this.ProxyUsername = proxyUsername; this.ProxyPassword = proxyPassword; this.AuthenticationMethods = authenticationMethods; } /// /// Authenticates the specified session. /// /// The session to be authenticated. /// true if authenticated; otherwise false. /// is null. /// No suitable authentication method found to complete authentication. public bool Authenticate(Session session) { var authenticated = AuthenticationResult.Failure; if (session == null) throw new ArgumentNullException("session"); session.RegisterMessage("SSH_MSG_USERAUTH_FAILURE"); session.RegisterMessage("SSH_MSG_USERAUTH_SUCCESS"); session.RegisterMessage("SSH_MSG_USERAUTH_BANNER"); session.UserAuthenticationBannerReceived += Session_UserAuthenticationBannerReceived; // Try to authenticate against none var noneAuthenticationMethod = new NoneAuthenticationMethod(this.Username); authenticated = noneAuthenticationMethod.Authenticate(session); var allowedAuthentications = noneAuthenticationMethod.AllowedAuthentications; var triedAuthentications = new List(); while (authenticated != AuthenticationResult.Success) { // Find first authentication method var method = this.AuthenticationMethods.Where((a) => allowedAuthentications.Contains(a.Name) && !triedAuthentications.Contains(a.Name)).FirstOrDefault(); if (method == null) throw new SshAuthenticationException("No suitable authentication method found to complete authentication."); triedAuthentications.Add(method.Name); authenticated = method.Authenticate(session); if (authenticated == AuthenticationResult.PartialSuccess || (method.AllowedAuthentications != null && method.AllowedAuthentications.Count() < allowedAuthentications.Count())) { // If further authentication is required then continue to try another method allowedAuthentications = method.AllowedAuthentications; continue; } // If authentication Fail, and all the authentication have been tried. if (authenticated == AuthenticationResult.Failure && (triedAuthentications.Count() == allowedAuthentications.Count())) { break; } } session.UserAuthenticationBannerReceived -= Session_UserAuthenticationBannerReceived; session.UnRegisterMessage("SSH_MSG_USERAUTH_FAILURE"); session.UnRegisterMessage("SSH_MSG_USERAUTH_SUCCESS"); session.UnRegisterMessage("SSH_MSG_USERAUTH_BANNER"); this.IsAuthenticated = authenticated == AuthenticationResult.Success; return authenticated == AuthenticationResult.Success; } private void Session_UserAuthenticationBannerReceived(object sender, MessageEventArgs e) { if (this.AuthenticationBanner != null) { this.AuthenticationBanner(this, new AuthenticationBannerEventArgs(this.Username, e.Message.Message, e.Message.Language)); } } } }