using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Messages.Authentication; using Renci.SshNet.Messages.Connection; using Renci.SshNet.Security; using Renci.SshNet.Security.Cryptography.Ciphers; using Renci.SshNet.Security.Cryptography.Ciphers.Modes; namespace Renci.SshNet { /// /// Represents remote connection information class. /// /// /// This class is NOT thread-safe. Do not use the same with multiple /// client instances. /// public class ConnectionInfo : IConnectionInfoInternal { internal const int DefaultPort = 22; /// /// The default connection timeout. /// /// /// 30 seconds. /// private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30); /// /// The default channel close timeout. /// /// /// 1 second. /// private static readonly TimeSpan DefaultChannelCloseTimeout = TimeSpan.FromSeconds(1); /// /// 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 IList AuthenticationMethods { get; private set; } /// /// Gets supported compression algorithms for this connection. /// public IDictionary CompressionAlgorithms { get; private set; } /// /// Gets the supported channel requests for this connection. /// /// /// The 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. /// /// /// The connection host. /// public string Host { get; private set; } /// /// Gets connection port. /// /// /// The connection port. The default value is 22. /// 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. /// /// /// The connection timeout. The default value is 30 seconds. /// /// /// /// public TimeSpan Timeout { get; set; } /// /// Gets or sets the timeout to use when waiting for a server to acknowledge closing a channel. /// /// /// The channel close timeout. The default value is 1 second. /// /// /// If a server does not send a SSH_MSG_CHANNEL_CLOSE message before the specified timeout /// elapses, the channel will be closed immediately. /// public TimeSpan ChannelCloseTimeout { get; set; } /// /// Gets or sets the character encoding. /// /// /// The character encoding. The default is . /// public Encoding Encoding { get; set; } /// /// Gets or sets number of retry attempts when session channel creation failed. /// /// /// The number of retry attempts when session channel creation failed. The default /// value is 10. /// public int RetryAttempts { get; set; } /// /// Gets or sets maximum number of session channels to be open simultaneously. /// /// /// The maximum number of session channels to be open simultaneously. The default /// value is 10. /// 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; } /// /// Gets 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. /// is null. /// is a zero-length string. /// is null, a zero-length string or contains only whitespace characters. /// is null. /// No specified. public ConnectionInfo(string host, string username, params AuthenticationMethod[] authenticationMethods) : this(host, DefaultPort, username, ProxyTypes.None, proxyHost: null, 0, proxyUsername: null, proxyPassword: null, authenticationMethods) { } /// /// Initializes a new instance of the class. /// /// The host. /// The port. /// The username. /// The authentication methods. /// is null. /// is null, a zero-length string or contains only whitespace characters. /// is not within and . /// is null. /// No specified. public ConnectionInfo(string host, int port, string username, params AuthenticationMethod[] authenticationMethods) : this(host, port, username, ProxyTypes.None, proxyHost: null, 0, proxyUsername: null, proxyPassword: null, authenticationMethods) { } /// /// 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. /// is null. /// is null, a zero-length string or contains only whitespace characters. /// is not within and . /// is not and is null. /// is not and is not within and . /// is null. /// No specified. public ConnectionInfo(string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, params AuthenticationMethod[] authenticationMethods) { if (host is null) { throw new ArgumentNullException(nameof(host)); } port.ValidatePort("port"); if (username is null) { throw new ArgumentNullException(nameof(username)); } if (username.All(char.IsWhiteSpace)) { throw new ArgumentException("Cannot be empty or contain only whitespace.", nameof(username)); } if (proxyType != ProxyTypes.None) { if (proxyHost is null) { throw new ArgumentNullException(nameof(proxyHost)); } proxyPort.ValidatePort("proxyPort"); } if (authenticationMethods is null) { throw new ArgumentNullException(nameof(authenticationMethods)); } if (authenticationMethods.Length == 0) { throw new ArgumentException("At least one authentication method should be specified.", nameof(authenticationMethods)); } // Set default connection values Timeout = DefaultTimeout; ChannelCloseTimeout = DefaultChannelCloseTimeout; RetryAttempts = 10; MaxSessions = 10; Encoding = Encoding.UTF8; KeyExchangeAlgorithms = new Dictionary { { "curve25519-sha256", typeof(KeyExchangeECCurve25519) }, { "curve25519-sha256@libssh.org", typeof(KeyExchangeECCurve25519) }, { "ecdh-sha2-nistp256", typeof(KeyExchangeECDH256) }, { "ecdh-sha2-nistp384", typeof(KeyExchangeECDH384) }, { "ecdh-sha2-nistp521", typeof(KeyExchangeECDH521) }, { "diffie-hellman-group-exchange-sha256", typeof(KeyExchangeDiffieHellmanGroupExchangeSha256) }, { "diffie-hellman-group-exchange-sha1", typeof(KeyExchangeDiffieHellmanGroupExchangeSha1) }, { "diffie-hellman-group16-sha512", typeof(KeyExchangeDiffieHellmanGroup16Sha512) }, { "diffie-hellman-group14-sha256", typeof(KeyExchangeDiffieHellmanGroup14Sha256) }, { "diffie-hellman-group14-sha1", typeof(KeyExchangeDiffieHellmanGroup14Sha1) }, { "diffie-hellman-group1-sha1", typeof(KeyExchangeDiffieHellmanGroup1Sha1) }, }; Encryptions = new Dictionary { { "aes256-ctr", new CipherInfo(256, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, { "3des-cbc", new CipherInfo(192, (key, iv) => new TripleDesCipher(key, new CbcCipherMode(iv), padding: null)) }, { "aes128-cbc", new CipherInfo(128, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, { "aes192-cbc", new CipherInfo(192, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, { "aes256-cbc", new CipherInfo(256, (key, iv) => new AesCipher(key, new CbcCipherMode(iv), padding: null)) }, { "blowfish-cbc", new CipherInfo(128, (key, iv) => new BlowfishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "twofish-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "twofish192-cbc", new CipherInfo(192, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "twofish128-cbc", new CipherInfo(128, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "twofish256-cbc", new CipherInfo(256, (key, iv) => new TwofishCipher(key, new CbcCipherMode(iv), padding: null)) }, { "arcfour", new CipherInfo(128, (key, iv) => new Arc4Cipher(key, dischargeFirstBytes: false)) }, { "arcfour128", new CipherInfo(128, (key, iv) => new Arc4Cipher(key, dischargeFirstBytes: true)) }, { "arcfour256", new CipherInfo(256, (key, iv) => new Arc4Cipher(key, dischargeFirstBytes: true)) }, { "cast128-cbc", new CipherInfo(128, (key, iv) => new CastCipher(key, new CbcCipherMode(iv), padding: null)) }, { "aes128-ctr", new CipherInfo(128, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, { "aes192-ctr", new CipherInfo(192, (key, iv) => new AesCipher(key, new CtrCipherMode(iv), padding: null)) }, }; HmacAlgorithms = new Dictionary { { "hmac-md5", new HashInfo(16*8, CryptoAbstraction.CreateHMACMD5) }, { "hmac-md5-96", new HashInfo(16*8, key => CryptoAbstraction.CreateHMACMD5(key, 96)) }, { "hmac-sha1", new HashInfo(20*8, CryptoAbstraction.CreateHMACSHA1) }, { "hmac-sha1-96", new HashInfo(20*8, key => CryptoAbstraction.CreateHMACSHA1(key, 96)) }, { "hmac-sha2-256", new HashInfo(32*8, CryptoAbstraction.CreateHMACSHA256) }, { "hmac-sha2-256-96", new HashInfo(32*8, key => CryptoAbstraction.CreateHMACSHA256(key, 96)) }, { "hmac-sha2-512", new HashInfo(64 * 8, CryptoAbstraction.CreateHMACSHA512) }, { "hmac-sha2-512-96", new HashInfo(64 * 8, key => CryptoAbstraction.CreateHMACSHA512(key, 96)) }, { "hmac-ripemd160", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160) }, { "hmac-ripemd160@openssh.com", new HashInfo(160, CryptoAbstraction.CreateHMACRIPEMD160) }, }; HostKeyAlgorithms = new Dictionary> { { "ssh-ed25519", data => new KeyHostAlgorithm("ssh-ed25519", new ED25519Key(), data) }, { "ecdsa-sha2-nistp256", data => new KeyHostAlgorithm("ecdsa-sha2-nistp256", new EcdsaKey(), data) }, { "ecdsa-sha2-nistp384", data => new KeyHostAlgorithm("ecdsa-sha2-nistp384", new EcdsaKey(), data) }, { "ecdsa-sha2-nistp521", data => new KeyHostAlgorithm("ecdsa-sha2-nistp521", new EcdsaKey(), data) }, { "ssh-rsa", data => new KeyHostAlgorithm("ssh-rsa", new RsaKey(), data) }, { "ssh-dss", data => new KeyHostAlgorithm("ssh-dss", new DsaKey(), data) }, }; CompressionAlgorithms = new Dictionary { { "none", null }, }; 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() }, }; Host = host; Port = port; Username = username; ProxyType = proxyType; ProxyHost = proxyHost; ProxyPort = proxyPort; ProxyUsername = proxyUsername; ProxyPassword = proxyPassword; AuthenticationMethods = authenticationMethods; } /// /// Authenticates the specified session. /// /// The session to be authenticated. /// The factory to use for creating new services. /// is null. /// is null. /// No suitable authentication method found to complete authentication, or permission denied. internal void Authenticate(ISession session, IServiceFactory serviceFactory) { if (serviceFactory is null) { throw new ArgumentNullException(nameof(serviceFactory)); } IsAuthenticated = false; var clientAuthentication = serviceFactory.CreateClientAuthentication(); clientAuthentication.Authenticate(this, session); IsAuthenticated = true; } /// /// Signals that an authentication banner message was received from the server. /// /// The session in which the banner message was received. /// The banner message. void IConnectionInfoInternal.UserAuthenticationBannerReceived(object sender, MessageEventArgs e) { AuthenticationBanner?.Invoke(this, new AuthenticationBannerEventArgs(Username, e.Message.Message, e.Message.Language)); } IAuthenticationMethod IConnectionInfoInternal.CreateNoneAuthenticationMethod() { return new NoneAuthenticationMethod(Username); } IList IConnectionInfoInternal.AuthenticationMethods { get { return AuthenticationMethods.Cast().ToList(); } } } }