diff --git a/Renci.SshClient/Renci.SshClient/KeyFile.cs b/Renci.SshClient/Renci.SshClient/KeyFile.cs index 39b6f401..f1049764 100644 --- a/Renci.SshClient/Renci.SshClient/KeyFile.cs +++ b/Renci.SshClient/Renci.SshClient/KeyFile.cs @@ -131,7 +131,7 @@ namespace Renci.SshClient throw new NotSupportedException(string.Format("Key '{0}' is not supported.", keyName)); } - this._key.Load(System.Convert.FromBase64String(data.ToString()), passPhrase.GetSshBytes()); + this._key.Load(System.Convert.FromBase64String(data.ToString()), passPhrase); } public void Open(string fileName) diff --git a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj index 780b505e..ffc5b634 100644 --- a/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj +++ b/Renci.SshClient/Renci.SshClient/Renci.SshClient.csproj @@ -121,10 +121,11 @@ - - - - + + + + + @@ -189,9 +190,6 @@ - - - @@ -201,6 +199,7 @@ + diff --git a/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKey.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKey.cs index db2fdb67..12f930a5 100644 --- a/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKey.cs +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKey.cs @@ -10,7 +10,7 @@ namespace Renci.SshClient.Security this.Load(data, null); } - public abstract void Load(IEnumerable data, IEnumerable passPhrase); + public abstract void Load(IEnumerable data, string passPhrase); public abstract CryptoPublicKey GetPublicKey(); diff --git a/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyDss.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyDss.cs index 45535191..03f3c577 100644 --- a/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyDss.cs +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyDss.cs @@ -19,7 +19,7 @@ namespace Renci.SshClient.Security get { return "ssh-dss"; } } - public override void Load(IEnumerable data, IEnumerable passPhrase) + public override void Load(IEnumerable data, string passPhrase) { if (passPhrase != null) { @@ -68,7 +68,7 @@ namespace Renci.SshClient.Security public override CryptoPublicKey GetPublicKey() { - return new CryptoPublicKeyDss(); + return new CryptoPublicKeyDss(this._p, this._q, this._g, this._x); } public override IEnumerable GetSignature(IEnumerable key) @@ -90,7 +90,7 @@ namespace Renci.SshClient.Security var DSA = new System.Security.Cryptography.DSACryptoServiceProvider(); DSA.ImportParameters(DSAKeyInfo); - var DSAFormatter = new RSAPKCS1SignatureFormatter(DSA); + var DSAFormatter = new DSASignatureFormatter(DSA); DSAFormatter.SetHashAlgorithm("SHA1"); var signature = DSAFormatter.CreateSignature(sha1); diff --git a/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyRsa.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyRsa.cs index 89beb8be..3f3a566c 100644 --- a/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyRsa.cs +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPrivateKeyRsa.cs @@ -22,7 +22,7 @@ namespace Renci.SshClient.Security get { return "ssh-rsa"; } } - public override void Load(IEnumerable data, IEnumerable passPhrase) + public override void Load(IEnumerable data, string passPhrase) { if (passPhrase != null) { @@ -92,14 +92,14 @@ namespace Renci.SshClient.Security { RSAParameters RSAKeyInfo = new RSAParameters(); - RSAKeyInfo.Exponent = _exponent.TrimLeadinZero().ToArray(); - RSAKeyInfo.D = _dValue.TrimLeadinZero().ToArray(); - RSAKeyInfo.Modulus = _modulus.TrimLeadinZero().ToArray(); - RSAKeyInfo.P = _pValue.TrimLeadinZero().ToArray(); - RSAKeyInfo.Q = _qValue.TrimLeadinZero().ToArray(); - RSAKeyInfo.DP = _dpValue.TrimLeadinZero().ToArray(); - RSAKeyInfo.DQ = _dqValue.TrimLeadinZero().ToArray(); - RSAKeyInfo.InverseQ = _inverseQ.TrimLeadinZero().ToArray(); + RSAKeyInfo.Exponent = this._exponent.TrimLeadinZero().ToArray(); + RSAKeyInfo.D = this._dValue.TrimLeadinZero().ToArray(); + RSAKeyInfo.Modulus = this._modulus.TrimLeadinZero().ToArray(); + RSAKeyInfo.P = this._pValue.TrimLeadinZero().ToArray(); + RSAKeyInfo.Q = this._qValue.TrimLeadinZero().ToArray(); + RSAKeyInfo.DP = this._dpValue.TrimLeadinZero().ToArray(); + RSAKeyInfo.DQ = this._dqValue.TrimLeadinZero().ToArray(); + RSAKeyInfo.InverseQ = this._inverseQ.TrimLeadinZero().ToArray(); cs.Write(data, 0, data.Length); diff --git a/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyDss.cs b/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyDss.cs index 2d0edca3..925318a2 100644 --- a/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyDss.cs +++ b/Renci.SshClient/Renci.SshClient/Security/CryptoPublicKeyDss.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; +using Renci.SshClient.Common; namespace Renci.SshClient.Security { @@ -111,7 +112,38 @@ namespace Renci.SshClient.Security public override IEnumerable GetBytes() { - throw new NotImplementedException(); + return new DsaPublicKeyData + { + P = this._p, + Q = this._q, + G = this._g, + Public = this._x, + }.GetBytes(); } + + private class DsaPublicKeyData : SshData + { + public IEnumerable P { get; set; } + + public IEnumerable Q { get; set; } + + public IEnumerable G { get; set; } + + public IEnumerable Public { get; set; } + + protected override void LoadData() + { + } + + protected override void SaveData() + { + this.Write("ssh-dss"); + this.Write(this.P.GetSshString()); + this.Write(this.Q.GetSshString()); + this.Write(this.G.GetSshString()); + this.Write(this.Public.GetSshString()); + } + } + } } diff --git a/Renci.SshClient/Renci.SshClient/Security/UserAuthentication.cs b/Renci.SshClient/Renci.SshClient/Security/UserAuthentication.cs new file mode 100644 index 00000000..5e758326 --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Security/UserAuthentication.cs @@ -0,0 +1,65 @@ +using Renci.SshClient.Messages; +using Renci.SshClient.Messages.Authentication; + +namespace Renci.SshClient.Security +{ + internal abstract class UserAuthentication + { + public abstract string Name { get; } + + public bool IsAuthenticated { get; private set; } + + public string ErrorMessage { get; private set; } + + protected Session Session { get; private set; } + + public UserAuthentication(Session session) + { + this.Session = session; + } + + /// + /// Executes this instance. + /// + /// true if method was execute; otherwise false. + public bool Execute() + { + Message.RegisterMessageType(MessageTypes.UserAuthenticationFailure); + Message.RegisterMessageType(MessageTypes.UserAuthenticationSuccess); + Message.RegisterMessageType(MessageTypes.UserAuthenticationBanner); + + this.Session.MessageReceived += Session_MessageReceived; + + var result = this.Run(); + + this.Session.MessageReceived -= Session_MessageReceived; + + Message.UnRegisterMessageType(MessageTypes.UserAuthenticationFailure); + Message.UnRegisterMessageType(MessageTypes.UserAuthenticationSuccess); + Message.UnRegisterMessageType(MessageTypes.UserAuthenticationBanner); + + return result; + } + + protected abstract bool Run(); + + protected abstract void HandleMessage(T message) where T : Message; + + protected virtual void HandleMessage(SuccessMessage message) + { + this.IsAuthenticated = true; + } + + protected virtual void HandleMessage(FailureMessage message) + { + this.ErrorMessage = message.Message; + this.IsAuthenticated = false; + } + + private void Session_MessageReceived(object sender, Common.MessageReceivedEventArgs e) + { + dynamic message = e.Message; + this.HandleMessage(message); + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationHost.cs b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationHost.cs similarity index 62% rename from Renci.SshClient/Renci.SshClient/Services/UserAuthenticationHost.cs rename to Renci.SshClient/Renci.SshClient/Security/UserAuthenticationHost.cs index 44e1bb29..30f4cbb5 100644 --- a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationHost.cs +++ b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationHost.cs @@ -1,4 +1,4 @@ -namespace Renci.SshClient.Services +namespace Renci.SshClient.Security { internal class UserAuthenticationHost : UserAuthentication { @@ -15,7 +15,12 @@ } - public override bool Start() + protected override bool Run() + { + throw new System.NotImplementedException(); + } + + protected override void HandleMessage(T message) { throw new System.NotImplementedException(); } diff --git a/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationNone.cs b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationNone.cs new file mode 100644 index 00000000..c2e64e6e --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationNone.cs @@ -0,0 +1,51 @@ +using System.Threading; +using Renci.SshClient.Messages; +using Renci.SshClient.Messages.Authentication; + +namespace Renci.SshClient.Security +{ + internal class UserAuthenticationNone : UserAuthentication + { + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); + + public override string Name + { + get { return "none"; } + } + + public UserAuthenticationNone(Session session) + : base(session) + { + + } + + protected override bool Run() + { + this.Session.SendMessage(new RequestMessage + { + ServiceName = ServiceNames.Connection, + Username = this.Session.ConnectionInfo.Username, + }); + + this.Session.WaitHandle(this._authenticationCompleted); + + return true; + } + + protected override void HandleMessage(T message) + { + } + + protected override void HandleMessage(SuccessMessage message) + { + base.HandleMessage(message); + this._authenticationCompleted.Set(); + } + + protected override void HandleMessage(FailureMessage message) + { + base.HandleMessage(message); + this._authenticationCompleted.Set(); + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPassword.cs b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPassword.cs new file mode 100644 index 00000000..5c7b8a18 --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPassword.cs @@ -0,0 +1,62 @@ +using System.Threading; +using Renci.SshClient.Messages; +using Renci.SshClient.Messages.Authentication; + +namespace Renci.SshClient.Security +{ + internal class UserAuthenticationPassword : UserAuthentication + { + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); + + public override string Name + { + get + { + return "password"; + } + } + + public UserAuthenticationPassword(Session session) + : base(session) + { + + } + + protected override bool Run() + { + // TODO: Handle all user authentication messages + //Message.RegisterMessageType(MessageTypes.UserAuthenticationPasswordChangeRequired); + + if (string.IsNullOrEmpty(this.Session.ConnectionInfo.Password)) + return false; + + this.Session.SendMessage(new PasswordRequestMessage + { + ServiceName = ServiceNames.Connection, + Username = this.Session.ConnectionInfo.Username, + Password = this.Session.ConnectionInfo.Password, + }); + + this.Session.WaitHandle(this._authenticationCompleted); + + return true; + } + + protected override void HandleMessage(T message) + { + // TODO: Handle password specific messages + } + + protected override void HandleMessage(SuccessMessage message) + { + base.HandleMessage(message); + this._authenticationCompleted.Set(); + } + + protected override void HandleMessage(FailureMessage message) + { + base.HandleMessage(message); + this._authenticationCompleted.Set(); + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPublicKey.cs b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPublicKey.cs new file mode 100644 index 00000000..19db474a --- /dev/null +++ b/Renci.SshClient/Renci.SshClient/Security/UserAuthenticationPublicKey.cs @@ -0,0 +1,109 @@ +using System.Threading; +using Renci.SshClient.Common; +using Renci.SshClient.Messages; +using Renci.SshClient.Messages.Authentication; + +namespace Renci.SshClient.Security +{ + internal class UserAuthenticationPublicKey : UserAuthentication + { + private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); + + public override string Name + { + get + { + return "publickey"; + } + } + + public UserAuthenticationPublicKey(Session session) + : base(session) + { + + } + + protected override bool Run() + { + if (this.Session.ConnectionInfo.KeyFile == null) + return false; + + Message.RegisterMessageType(MessageTypes.UserAuthenticationInformationRequest); + + + // TODO: Complete full public key implemention which includes other messages + var message = new PublicKeyRequestMessage + { + ServiceName = ServiceNames.Connection, + Username = this.Session.ConnectionInfo.Username, + PublicKeyAlgorithmName = this.Session.ConnectionInfo.KeyFile.AlgorithmName, + PublicKeyData = this.Session.ConnectionInfo.KeyFile.PublicKey, + Signature = new byte[] { }, + //Signature = this.Session.ConnectionInfo.KeyFile.GetSignature(this.Session.SessionId), + }; + + var signatureData = new SignatureData(message, this.Session.SessionId.GetSshString()).GetBytes(); + + var signature = this.Session.ConnectionInfo.KeyFile.GetSignature(signatureData); + + message.Signature = signature; + + this.Session.SendMessage(message); + + this.Session.WaitHandle(this._authenticationCompleted); + + Message.UnRegisterMessageType(MessageTypes.UserAuthenticationInformationRequest); + + + return true; + } + + protected override void HandleMessage(T message) + { + throw new System.NotImplementedException(); + } + + protected override void HandleMessage(SuccessMessage message) + { + base.HandleMessage(message); + this._authenticationCompleted.Set(); + } + + protected override void HandleMessage(FailureMessage message) + { + base.HandleMessage(message); + this._authenticationCompleted.Set(); + } + + private class SignatureData : SshData + { + + private PublicKeyRequestMessage _message; + + private string _sessionId; + + public SignatureData(PublicKeyRequestMessage message, string sessionId) + { + this._message = message; + this._sessionId = sessionId; + } + + protected override void LoadData() + { + throw new System.NotImplementedException(); + } + + protected override void SaveData() + { + this.Write(this._sessionId); + this.Write((byte)this._message.MessageType); + this.Write(this._message.Username); + this.Write("ssh-connection"); + this.Write("publickey"); + this.Write((byte)1); + this.Write(this._message.PublicKeyAlgorithmName); + this.Write(this._message.PublicKeyData.GetSshString()); + } + } + } +} diff --git a/Renci.SshClient/Renci.SshClient/Services/ConnectionService.cs b/Renci.SshClient/Renci.SshClient/Services/ConnectionService.cs deleted file mode 100644 index 8719d78e..00000000 --- a/Renci.SshClient/Renci.SshClient/Services/ConnectionService.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using Renci.SshClient.Messages; - -namespace Renci.SshClient.Services -{ - internal class ConnectionService : Service - { - public override ServiceNames ServiceName - { - get { throw new NotImplementedException(); } - } - - public ConnectionService(Session session) - : base(session) - { - - } - } -} diff --git a/Renci.SshClient/Renci.SshClient/Services/Service.cs b/Renci.SshClient/Renci.SshClient/Services/Service.cs deleted file mode 100644 index 23ebe0fb..00000000 --- a/Renci.SshClient/Renci.SshClient/Services/Service.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Renci.SshClient.Messages; - -namespace Renci.SshClient.Services -{ - internal abstract class Service - { - public abstract ServiceNames ServiceName { get; } - - protected Session Session { get; private set; } - - public Service(Session session) - { - this.Session = session; - } - - protected void SendMessage(Message message) - { - this.Session.SendMessage(message); - } - - } -} diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthentication.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthentication.cs deleted file mode 100644 index ba2e2907..00000000 --- a/Renci.SshClient/Renci.SshClient/Services/UserAuthentication.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Renci.SshClient.Messages; - -namespace Renci.SshClient.Services -{ - internal abstract class UserAuthentication - { - public abstract string Name { get; } - - protected Session Session { get; private set; } - - public UserAuthentication(Session session) - { - this.Session = session; - } - - public abstract bool Start(); - - protected void SendMessage(Message message) - { - this.Session.SendMessage(message); - } - - } -} diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPassword.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPassword.cs deleted file mode 100644 index 546806fb..00000000 --- a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPassword.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Renci.SshClient.Messages; -using Renci.SshClient.Messages.Authentication; - -namespace Renci.SshClient.Services -{ - internal class UserAuthenticationPassword : UserAuthentication - { - public override string Name - { - get - { - return "password"; - } - } - - public UserAuthenticationPassword(Session session) - : base(session) - { - - } - - public override bool Start() - { - // TODO: Handle all user authentication messages - //Message.RegisterMessageType(MessageTypes.UserAuthenticationPasswordChangeRequired); - - if (!string.IsNullOrEmpty(this.Session.ConnectionInfo.Password)) - { - this.SendMessage(new PasswordRequestMessage - { - ServiceName = ServiceNames.Connection, - Username = this.Session.ConnectionInfo.Username, - Password = this.Session.ConnectionInfo.Password, - }); - return true; - } - return false; - } - } -} diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPublicKey.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPublicKey.cs deleted file mode 100644 index c68aef7a..00000000 --- a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationPublicKey.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Renci.SshClient.Messages; -using Renci.SshClient.Messages.Authentication; - -namespace Renci.SshClient.Services -{ - internal class UserAuthenticationPublicKey : UserAuthentication - { - public override string Name - { - get - { - return "publickey"; - } - } - - public UserAuthenticationPublicKey(Session session) - : base(session) - { - - } - - - public override bool Start() - { - if (this.Session.ConnectionInfo.KeyFile != null) - { - // TODO: Complete full public key implemention which includes other messages - this.SendMessage(new PublicKeyRequestMessage - { - ServiceName = ServiceNames.Connection, - Username = this.Session.ConnectionInfo.Username, - PublicKeyAlgorithmName = this.Session.ConnectionInfo.KeyFile.AlgorithmName, - PublicKeyData = this.Session.ConnectionInfo.KeyFile.PublicKey, - Signature = this.Session.ConnectionInfo.KeyFile.GetSignature(this.Session.SessionId), - }); - return true; - } - return false; - } - } -} diff --git a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationService.cs b/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationService.cs deleted file mode 100644 index bc903553..00000000 --- a/Renci.SshClient/Renci.SshClient/Services/UserAuthenticationService.cs +++ /dev/null @@ -1,155 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Renci.SshClient.Common; -using Renci.SshClient.Messages; -using Renci.SshClient.Messages.Authentication; -using Renci.SshClient.Messages.Transport; -using Renci.SshClient.Security; - -namespace Renci.SshClient.Services -{ - internal class UserAuthenticationService : Service - { - private IList _executedMethods = new List(); - - private EventWaitHandle _serviceAccepted = new AutoResetEvent(false); - - private EventWaitHandle _authenticationCompleted = new AutoResetEvent(false); - - public override ServiceNames ServiceName - { - get { return ServiceNames.UserAuthentication; } - } - - public EventWaitHandle AuthenticationCompletedHandle { get; private set; } - - public string ErrorMessage { get; private set; } - - public bool IsAuthenticated { get; private set; } - - public UserAuthenticationService(Session session) - : base(session) - { - this.AuthenticationCompletedHandle = new AutoResetEvent(false); - } - - public void AuthenticateUser() - { - // Register Authentication response messages - Message.RegisterMessageType(MessageTypes.UserAuthenticationFailure); - Message.RegisterMessageType(MessageTypes.UserAuthenticationSuccess); - Message.RegisterMessageType(MessageTypes.UserAuthenticationBanner); - - // Attach event handlers to handle messages - this.Session.MessageReceived += SessionInfo_MessageReceived; - - // Request user authorization service - this.SendMessage(new ServiceRequestMessage - { - ServiceName = ServiceNames.UserAuthentication, - }); - - // Wait for service to be accepted - this.Session.WaitHandle(this._serviceAccepted); - - // Start by quering supported authentication methods - this.SendMessage(new RequestMessage - { - Username = this.Session.ConnectionInfo.Username, - ServiceName = ServiceNames.Connection, - }); - - // Wait for authentication to be completed - this.Session.WaitHandle(this._authenticationCompleted); - - this.Session.MessageReceived -= SessionInfo_MessageReceived; - - Message.UnRegisterMessageType(MessageTypes.UserAuthenticationFailure); - Message.UnRegisterMessageType(MessageTypes.UserAuthenticationSuccess); - Message.UnRegisterMessageType(MessageTypes.UserAuthenticationBanner); - } - - private void SessionInfo_MessageReceived(object sender, MessageReceivedEventArgs e) - { - this.HandleMessage((dynamic)e.Message); - } - - private void HandleMessage(T message) - { - // Ignore messages that cannot be handled by this module - } - - private void HandleMessage(ServiceAcceptMessage message) - { - if (message.ServiceName == ServiceNames.UserAuthentication) - { - this._serviceAccepted.Set(); - } - } - - private void HandleMessage(SuccessMessage message) - { - this.AuthenticationSucceded(); - } - - private void HandleMessage(FailureMessage message) - { - if (message.PartialSuccess) - { - this.AuthenticationFailed(message.Message); - return; - } - - // Get method that was not executed yet - var methodsToTry = message.AllowedAuthentications.Except(this._executedMethods); - - if (methodsToTry.Count() == 0) - { - this.AuthenticationFailed(string.Format("User '{0}' cannot be authorized.", this.Session.ConnectionInfo.Username)); - return; - } - - // Execute authentication method - foreach (var methodName in methodsToTry) - { - UserAuthentication userAuthentication = null; - - if (methodName == "publickey") - { - userAuthentication = new UserAuthenticationPublicKey(this.Session); - } - else if (methodName == "password") - { - userAuthentication = new UserAuthenticationPassword(this.Session); - } - this._executedMethods.Add(methodName); - if (userAuthentication != null) - { - if (userAuthentication.Start()) - break; - } - } - } - - private void HandleMessage(BannerMessage message) - { - } - - private void AuthenticationFailed(string message) - { - this.IsAuthenticated = false; - this.ErrorMessage = message; - this._authenticationCompleted.Set(); - } - - private void AuthenticationSucceded() - { - this.IsAuthenticated = true; - this._authenticationCompleted.Set(); - } - - - - } -} diff --git a/Renci.SshClient/Renci.SshClient/Session.cs b/Renci.SshClient/Renci.SshClient/Session.cs index 7873dbe1..c6304982 100644 --- a/Renci.SshClient/Renci.SshClient/Session.cs +++ b/Renci.SshClient/Renci.SshClient/Session.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.IO; using System.Net; using System.Net.Sockets; +using System.Security.Authentication; using System.Security.Cryptography; using System.Text; using System.Threading; @@ -14,7 +15,6 @@ using Renci.SshClient.Messages; using Renci.SshClient.Messages.Connection; using Renci.SshClient.Messages.Transport; using Renci.SshClient.Security; -using Renci.SshClient.Services; namespace Renci.SshClient { @@ -59,19 +59,19 @@ namespace Renci.SshClient private Socket _socket; - private int _waitTimeout = 5 * 1000; // Set default receive timeout to 5 seconds - private KeyExchange _keyExhcange; private BackgroundWorker _messageListener; private EventWaitHandle _keyExhangedFinishedWaitHandle = new AutoResetEvent(false); - private IDictionary _services = new Dictionary(); + private EventWaitHandle _serviceAccepted = new AutoResetEvent(false); + + private EventWaitHandle _exceptionWaitHandle = new AutoResetEvent(false); private IDictionary _openChannels = new Dictionary(); - private EventWaitHandle _exceptionWaitHandle = new AutoResetEvent(false); + private IDictionary _executedAuthenticationMethods = new Dictionary(); /// /// Exception that need to be thrown by waiting thread @@ -83,6 +83,11 @@ namespace Renci.SshClient /// private bool _isDisconnectByClient; + /// + /// Specifies weither connection is authenticated + /// + private bool _isAuthenticated; + public event EventHandler MessageReceived; protected HMAC ServerMac { get; private set; } @@ -195,13 +200,38 @@ namespace Renci.SshClient return; } - var authenticationService = new UserAuthenticationService(this); - - authenticationService.AuthenticateUser(); - - if (!authenticationService.IsAuthenticated) + // Request user authorization service + this.SendMessage(new ServiceRequestMessage { - throw new InvalidOperationException(string.Format("User cannot be authenticated. Reason: {0}.", authenticationService.ErrorMessage)); + ServiceName = ServiceNames.UserAuthentication, + }); + + // Wait for service to be accepted + this.WaitHandle(this._serviceAccepted); + + // This implemention will ignore supported by server methods and will try to authenticated user using method supported by the client. + string errorMessage = null; // Hold last authentication error if any + foreach (var methodName in Settings.SupportedAuthenticationMethods.Keys) + { + var userAuthentication = Settings.SupportedAuthenticationMethods[methodName](this); + + if (userAuthentication.Execute()) + { + if (userAuthentication.IsAuthenticated) + { + this._isAuthenticated = true; + break; + } + else + { + errorMessage = userAuthentication.ErrorMessage; + } + } + } + + if (!this._isAuthenticated) + { + throw new AuthenticationException(errorMessage ?? "User cannot be authenticated."); } } @@ -277,6 +307,7 @@ namespace Renci.SshClient protected virtual void HandleMessage(ServiceAcceptMessage message) { + this._serviceAccepted.Set(); } protected virtual void HandleMessage(ServiceRequestMessage message) diff --git a/Renci.SshClient/Renci.SshClient/Settings.cs b/Renci.SshClient/Renci.SshClient/Settings.cs index 5cd5b6cd..a873cc3c 100644 --- a/Renci.SshClient/Renci.SshClient/Settings.cs +++ b/Renci.SshClient/Renci.SshClient/Settings.cs @@ -16,6 +16,8 @@ namespace Renci.SshClient public static IDictionary> HostKeyAlgorithms { get; private set; } + public static IDictionary> SupportedAuthenticationMethods { get; private set; } + static Settings() { Settings.KeyExchangeAlgorithms = new Dictionary>() @@ -43,6 +45,13 @@ namespace Renci.SshClient {"ssh-rsa", () => { return new CryptoPublicKeyRsa();}}, {"ssh-dsa", () => { return new CryptoPublicKeyDss();}}, // TODO: Need to be tested }; + + Settings.SupportedAuthenticationMethods = new Dictionary>() + { + {"none", (session)=> {return new UserAuthenticationNone(session);}}, + {"publickey", (session)=> {return new UserAuthenticationPublicKey(session);}}, + {"password", (session)=> {return new UserAuthenticationPassword(session);}}, + }; } } }