-
-
Notifications
You must be signed in to change notification settings - Fork 960
Adding Pagant Support #705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
#if NETFRAMEWORK && !NET20 && !NET35 | ||
using System; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading; | ||
using Renci.SshNet.Common; | ||
using Renci.SshNet.Messages; | ||
using Renci.SshNet.Messages.Authentication; | ||
|
||
namespace Renci.SshNet { | ||
/// <summary> | ||
/// Provides functionality to perform private key authentication. | ||
/// </summary> | ||
public class AgentAuthenticationMethod : AuthenticationMethod, IDisposable { | ||
private AuthenticationResult _authenticationResult = AuthenticationResult.Failure; | ||
private EventWaitHandle _authenticationCompleted = new ManualResetEvent (false); | ||
private bool _isSignatureRequired; | ||
|
||
/// <summary> | ||
/// Gets authentication method name | ||
/// </summary> | ||
public override string Name { | ||
get { return "publickey"; } | ||
} | ||
|
||
/// <summary> | ||
/// | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing comment content |
||
/// </summary> | ||
public IAgentProtocol Protocol { get; private set; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PrivateKeyAuthenticationMethod"/> class. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wrong cref |
||
/// </summary> | ||
/// <param name="username">The username.</param> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: fragment sentences (sentences without subject-verb-complement) should not end with dot ('.') |
||
/// <param name="protocol">The key files.</param> | ||
/// <exception cref="ArgumentException"><paramref name="username"/> is whitespace or null.</exception> | ||
public AgentAuthenticationMethod (string username, IAgentProtocol protocol) : base (username) { | ||
Protocol = protocol; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Existing code curly-bracket alignment style is
not
|
||
|
||
/// <summary> | ||
/// Authenticates the specified session. | ||
/// </summary> | ||
/// <param name="session">The session to authenticate.</param> | ||
/// <returns></returns> | ||
public override AuthenticationResult Authenticate (Session session) { | ||
if (Protocol == null) | ||
return AuthenticationResult.Failure; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can a more specific error / a failure reason be provided? |
||
|
||
session.UserAuthenticationSuccessReceived += Session_UserAuthenticationSuccessReceived; | ||
session.UserAuthenticationFailureReceived += Session_UserAuthenticationFailureReceived; | ||
session.UserAuthenticationPublicKeyReceived += Session_UserAuthenticationPublicKeyReceived; | ||
|
||
session.RegisterMessage ("SSH_MSG_USERAUTH_PK_OK"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Declare a string const for "SSH_MSG_USERAUTH_PK_OK" and re-use the const instead of repeating the string's hardcoding. |
||
|
||
try { | ||
foreach (var identity in Protocol.GetIdentities ()) { | ||
_authenticationCompleted.Reset (); | ||
_isSignatureRequired = false; | ||
|
||
var message = new RequestMessagePublicKey (ServiceName.Connection, | ||
Username, | ||
identity.Type, | ||
identity.Blob); | ||
|
||
// Send public key authentication request | ||
session.SendMessage (message); | ||
|
||
session.WaitOnHandle (_authenticationCompleted); | ||
|
||
if (_isSignatureRequired) { | ||
_authenticationCompleted.Reset (); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no space character between method name and opening parenthesis for parameters. Here and through the change. |
||
|
||
var signatureMessage = new RequestMessagePublicKey (ServiceName.Connection, | ||
Username, | ||
identity.Type, | ||
identity.Blob); | ||
|
||
var signatureData = new SignatureData (message, session.SessionId).GetBytes (); | ||
|
||
signatureMessage.Signature = this.Protocol.SignData (identity, signatureData); | ||
|
||
// Send public key authentication request with signature | ||
session.SendMessage (signatureMessage); | ||
} | ||
|
||
session.WaitOnHandle (_authenticationCompleted); | ||
|
||
if (_authenticationResult == AuthenticationResult.Success) { | ||
break; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Empty line after block closing curly-brace and before next line of code. |
||
return _authenticationResult; | ||
} finally { | ||
session.UserAuthenticationSuccessReceived -= Session_UserAuthenticationSuccessReceived; | ||
session.UserAuthenticationFailureReceived -= Session_UserAuthenticationFailureReceived; | ||
session.UserAuthenticationPublicKeyReceived -= Session_UserAuthenticationPublicKeyReceived; | ||
session.UnRegisterMessage ("SSH_MSG_USERAUTH_PK_OK"); | ||
} | ||
} | ||
|
||
private void Session_UserAuthenticationSuccessReceived (object sender, MessageEventArgs<SuccessMessage> e) { | ||
this._authenticationResult = AuthenticationResult.Success; | ||
|
||
this._authenticationCompleted.Set (); | ||
} | ||
|
||
private void Session_UserAuthenticationFailureReceived (object sender, MessageEventArgs<FailureMessage> e) { | ||
if (e.Message.PartialSuccess) | ||
_authenticationResult = AuthenticationResult.PartialSuccess; | ||
else | ||
_authenticationResult = AuthenticationResult.Failure; | ||
|
||
// Copy allowed authentication methods | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this assignment really makes a copy or does it just take a reference? |
||
AllowedAuthentications = e.Message.AllowedAuthentications; | ||
|
||
_authenticationCompleted.Set (); | ||
} | ||
|
||
private void Session_UserAuthenticationPublicKeyReceived (object sender, MessageEventArgs<PublicKeyMessage> e) { | ||
this._isSignatureRequired = true; | ||
this._authenticationCompleted.Set (); | ||
} | ||
|
||
#region IDisposable Members | ||
|
||
private bool _isDisposed = false; | ||
|
||
/// <summary> | ||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. | ||
/// </summary> | ||
public void Dispose () { | ||
Dispose (true); | ||
GC.SuppressFinalize (this); | ||
} | ||
|
||
/// <summary> | ||
/// Releases unmanaged and - optionally - managed resources | ||
/// </summary> | ||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> | ||
protected virtual void Dispose (bool disposing) { | ||
// Check to see if Dispose has already been called. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid boiler-plate inline comments for explicit code. Preserve comments when they add context, something unclear about the code, etc. |
||
if (!_isDisposed) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be the opposite case, i.e. return if _isDisposed is true? |
||
return; | ||
|
||
if (disposing) { | ||
// If disposing equals true, dispose all managed | ||
// and unmanaged resources. | ||
if (disposing) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. duplicated test of disposing |
||
var authenticationCompleted = _authenticationCompleted; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use Interlocked.Exchange instead. It will return the previous value, acts as a memory barrier, and protect on this thread against race conditions / value caching per thread. |
||
// Dispose managed resources. | ||
if (this._authenticationCompleted != null) { | ||
_authenticationCompleted = null; | ||
authenticationCompleted.Dispose (); | ||
} | ||
} | ||
|
||
// Note disposing has been done. | ||
_isDisposed = true; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Releases unmanaged resources and performs other cleanup operations before the | ||
/// <see cref="PasswordConnectionInfo"/> is reclaimed by garbage collection. | ||
/// </summary> | ||
~AgentAuthenticationMethod () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Explicit destructor implementation is atypical. Do we really need it here? |
||
// Do not re-create Dispose clean-up code here. | ||
// Calling Dispose(false) is optimal in terms of | ||
// readability and maintainability. | ||
Dispose (false); | ||
} | ||
|
||
#endregion | ||
|
||
private class SignatureData : SshData { | ||
private readonly RequestMessagePublicKey _message; | ||
|
||
private byte[] _sessionId; | ||
private readonly byte[] _serviceName; | ||
private readonly byte[] _authenticationMethod; | ||
|
||
protected override int BufferCapacity { | ||
get { | ||
var capacity = base.BufferCapacity; | ||
capacity += 4; // SessionId length | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Define named constants instead of code comments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And then collapse the dozen lines into one. |
||
capacity += _sessionId.Length; // SessionId | ||
capacity += 1; // Authentication Message Code | ||
capacity += 4; // UserName length | ||
capacity += _message.Username.Length; // UserName | ||
capacity += 4; // ServiceName length | ||
capacity += _serviceName.Length; // ServiceName | ||
capacity += 4; // AuthenticationMethod length | ||
capacity += _authenticationMethod.Length; // AuthenticationMethod | ||
capacity += 1; // TRUE | ||
capacity += 4; // PublicKeyAlgorithmName length | ||
capacity += _message.PublicKeyAlgorithmName.Length; // PublicKeyAlgorithmName | ||
capacity += 4; // PublicKeyData length | ||
capacity += _message.PublicKeyData.Length; // PublicKeyData | ||
return capacity; | ||
} | ||
} | ||
|
||
public SignatureData (RequestMessagePublicKey message, byte[] sessionId) { | ||
_message = message; | ||
_sessionId = sessionId; | ||
_serviceName = Ascii.GetBytes ("ssh-connection"); | ||
_authenticationMethod = Ascii.GetBytes ("publickey"); | ||
} | ||
|
||
protected override void LoadData () { | ||
throw new System.NotImplementedException (); | ||
} | ||
|
||
protected override void SaveData () { | ||
WriteBinaryString (_sessionId); | ||
Write ((byte) RequestMessage.AuthenticationMessageCode); | ||
WriteBinaryString (_message.Username); | ||
WriteBinaryString (_serviceName); | ||
WriteBinaryString (_authenticationMethod); | ||
Write ((byte) 1); // TRUE | ||
WriteBinaryString (_message.PublicKeyAlgorithmName); | ||
WriteBinaryString (_message.PublicKeyData); | ||
} | ||
} | ||
} | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
#if NETFRAMEWORK && !NET20 && !NET35 | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.ObjectModel; | ||
using System.Linq; | ||
using System.Text; | ||
|
||
namespace Renci.SshNet { | ||
/// <summary> | ||
/// Provides connection information when private key authentication method is used | ||
/// </summary> | ||
public class AgentConnectionInfo : ConnectionInfo, IDisposable { | ||
/// <summary> | ||
/// Gets the key files used for authentication. | ||
/// </summary> | ||
public IAgentProtocol Protocol { get; private set; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PrivateKeyConnectionInfo"/> class. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix cref here and through the code change. |
||
/// </summary> | ||
/// <param name="host">Connection host.</param> | ||
/// <param name="username">Connection username.</param> | ||
/// <param name="protocol">Connection key files.</param> | ||
public AgentConnectionInfo (string host, string username, IAgentProtocol protocol) : this (host, 22, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, protocol) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid hardcoded magical values. Define named constants instead for readability. When calling a method with multiple parameters of the same type, use named parameters to remove the ambiguity for human readers. Compiler will match type but doesn't know purpose. Human will be able to assert purpose but don't have context of the called method unless named parameters are used. |
||
|
||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PrivateKeyConnectionInfo"/> class. | ||
/// </summary> | ||
/// <param name="host">Connection host.</param> | ||
/// <param name="port">Connection port.</param> | ||
/// <param name="username">Connection username.</param> | ||
/// <param name="protocol">Connection key files.</param> | ||
public AgentConnectionInfo (string host, int port, string username, IAgentProtocol protocol) : this (host, port, username, ProxyTypes.None, string.Empty, 0, string.Empty, string.Empty, protocol) { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PasswordConnectionInfo"/> class. | ||
/// </summary> | ||
/// <param name="host">Connection host.</param> | ||
/// <param name="port">The port.</param> | ||
/// <param name="username">Connection username.</param> | ||
/// <param name="proxyType">Type of the proxy.</param> | ||
/// <param name="proxyHost">The proxy host.</param> | ||
/// <param name="proxyPort">The proxy port.</param> | ||
/// <param name="protocol">The key files.</param> | ||
public AgentConnectionInfo (string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, IAgentProtocol protocol) : this (host, port, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, protocol) { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PasswordConnectionInfo"/> class. | ||
/// </summary> | ||
/// <param name="host">Connection host.</param> | ||
/// <param name="port">The port.</param> | ||
/// <param name="username">Connection username.</param> | ||
/// <param name="proxyType">Type of the proxy.</param> | ||
/// <param name="proxyHost">The proxy host.</param> | ||
/// <param name="proxyPort">The proxy port.</param> | ||
/// <param name="proxyUsername">The proxy username.</param> | ||
/// <param name="protocol">The key files.</param> | ||
public AgentConnectionInfo (string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, IAgentProtocol protocol) : this (host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, protocol) { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PasswordConnectionInfo"/> class. | ||
/// </summary> | ||
/// <param name="host">Connection host.</param> | ||
/// <param name="username">Connection username.</param> | ||
/// <param name="proxyType">Type of the proxy.</param> | ||
/// <param name="proxyHost">The proxy host.</param> | ||
/// <param name="proxyPort">The proxy port.</param> | ||
/// <param name="protocol">The key files.</param> | ||
public AgentConnectionInfo (string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, IAgentProtocol protocol) : this (host, 22, username, proxyType, proxyHost, proxyPort, string.Empty, string.Empty, protocol) { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PasswordConnectionInfo"/> class. | ||
/// </summary> | ||
/// <param name="host">Connection host.</param> | ||
/// <param name="username">Connection username.</param> | ||
/// <param name="proxyType">Type of the proxy.</param> | ||
/// <param name="proxyHost">The proxy host.</param> | ||
/// <param name="proxyPort">The proxy port.</param> | ||
/// <param name="proxyUsername">The proxy username.</param> | ||
/// <param name="protocol">The key files.</param> | ||
public AgentConnectionInfo (string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, IAgentProtocol protocol) : this (host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, string.Empty, protocol) { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PasswordConnectionInfo"/> class. | ||
/// </summary> | ||
/// <param name="host">Connection host.</param> | ||
/// <param name="username">Connection username.</param> | ||
/// <param name="proxyType">Type of the proxy.</param> | ||
/// <param name="proxyHost">The proxy host.</param> | ||
/// <param name="proxyPort">The proxy port.</param> | ||
/// <param name="proxyUsername">The proxy username.</param> | ||
/// <param name="proxyPassword">The proxy password.</param> | ||
/// <param name="protocol">The key files.</param> | ||
public AgentConnectionInfo (string host, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, IAgentProtocol protocol) : this (host, 22, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, protocol) { } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="PasswordConnectionInfo"/> class. | ||
/// </summary> | ||
/// <param name="host">Connection host.</param> | ||
/// <param name="port">The port.</param> | ||
/// <param name="username">Connection username.</param> | ||
/// <param name="proxyType">Type of the proxy.</param> | ||
/// <param name="proxyHost">The proxy host.</param> | ||
/// <param name="proxyPort">The proxy port.</param> | ||
/// <param name="proxyUsername">The proxy username.</param> | ||
/// <param name="proxyPassword">The proxy password.</param> | ||
/// <param name="protocol">The key files.</param> | ||
public AgentConnectionInfo (string host, int port, string username, ProxyTypes proxyType, string proxyHost, int proxyPort, string proxyUsername, string proxyPassword, IAgentProtocol protocol) : base (host, port, username, proxyType, proxyHost, proxyPort, proxyUsername, proxyPassword, new AgentAuthenticationMethod (username, protocol)) { | ||
this.Protocol = protocol; | ||
} | ||
|
||
#region IDisposable Members | ||
|
||
private bool isDisposed = false; | ||
|
||
/// <summary> | ||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. | ||
/// </summary> | ||
public void Dispose () { | ||
Dispose (true); | ||
|
||
GC.SuppressFinalize (this); | ||
} | ||
|
||
/// <summary> | ||
/// Releases unmanaged and - optionally - managed resources | ||
/// </summary> | ||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> | ||
protected virtual void Dispose (bool disposing) { | ||
// Check to see if Dispose has already been called. | ||
if (!this.isDisposed) { | ||
// If disposing equals true, dispose all managed | ||
// and unmanaged resources. | ||
if (disposing) { | ||
// Dispose managed resources. | ||
if (this.AuthenticationMethods != null) { | ||
foreach (var authenticationMethods in this.AuthenticationMethods.OfType<IDisposable> ()) { | ||
authenticationMethods.Dispose (); | ||
} | ||
} | ||
} | ||
|
||
// Note disposing has been done. | ||
isDisposed = true; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Releases unmanaged resources and performs other cleanup operations before the | ||
/// <see cref="PasswordConnectionInfo"/> is reclaimed by garbage collection. | ||
/// </summary> | ||
~AgentConnectionInfo () { | ||
// Do not re-create Dispose clean-up code here. | ||
// Calling Dispose(false) is optimal in terms of | ||
// readability and maintainability. | ||
Dispose (false); | ||
} | ||
|
||
#endregion | ||
} | ||
} | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.NET standard for .NET core is supported by the SSH.NET library in general so new additions to the code base should continue to honor that.