class Net::SSH::Authentication::Agent
This class implements an agent for JRuby + Pageant.
Written by Artūras Šlajus <arturas.slajus@gmail.com>
This class implements a simple client for the ssh-agent protocol. It does not implement any specific protocol, but instead copies the behavior of the ssh-agent functions in the OpenSSH library (3.8).
This means that although it behaves like a SSH1 client, it also has some SSH2 functionality (like signing data).
Constants
- SSH2_AGENT_FAILURE
- SSH2_AGENT_IDENTITIES_ANSWER
- SSH2_AGENT_REQUEST_IDENTITIES
- SSH2_AGENT_REQUEST_VERSION
- SSH2_AGENT_SIGN_REQUEST
- SSH2_AGENT_SIGN_RESPONSE
- SSH2_AGENT_VERSION_RESPONSE
- SSH_AGENT_FAILURE
- SSH_AGENT_REQUEST_RSA_IDENTITIES
- SSH_AGENT_RSA_IDENTITIES_ANSWER1
- SSH_AGENT_RSA_IDENTITIES_ANSWER2
- SSH_COM_AGENT2_FAILURE
Attributes
The underlying socket being used to communicate with the SSH agent.
Public Class Methods
Instantiates a new agent object, connects to a running SSH agent, negotiates the agent protocol version, and returns the agent object.
# File lib/net/ssh/authentication/agent/java_pageant.rb, line 22 def self.connect(logger=nil) agent = new(logger) agent.connect! agent end
Creates a new Agent object, using the optional logger instance to report status.
# File lib/net/ssh/authentication/agent/java_pageant.rb, line 30 def initialize(logger=nil) self.logger = logger end
Public Instance Methods
Simulate agent close. This agent reference is no longer able to query the agent.
# File lib/net/ssh/authentication/agent/java_pageant.rb, line 69 def close @agent_proxy = nil end
Connect to the agent process using the socket factory and socket name given by the attribute writers. If the agent on the other end of the socket reports that it is an SSH2-compatible agent, this will fail (it only supports the ssh-agent distributed by OpenSSH).
# File lib/net/ssh/authentication/agent/java_pageant.rb, line 38 def connect! debug { "connecting to Pageant ssh-agent (via java connector)" } @agent_proxy = JRubyPageant.create unless @agent_proxy.is_running raise AgentNotAvailable, "Pageant is not running!" end debug { "connection to Pageant ssh-agent (via java connector) succeeded" } rescue AgentProxyException => e error { "could not connect to Pageant ssh-agent (via java connector)" } raise AgentNotAvailable, e.message, e.backtrace end
Return an array of all identities (public keys) known to the agent. Each
key returned is augmented with a comment
property which is set
to the comment returned by the agent for that key.
# File lib/net/ssh/authentication/agent/java_pageant.rb, line 53 def identities debug { "getting identities from Pageant" } @agent_proxy.get_identities.map do |identity| blob = identity.get_blob key = Buffer.new(String.from_java_bytes(blob)).read_key key.extend(Key) key.java_blob = blob key.comment = String.from_java_bytes(identity.get_comment) key end rescue AgentProxyException => e raise AgentError, "Cannot get identities: #{e.message}", e.backtrace end
Attempts to negotiate the SSH agent protocol version. Raises an error if the version could not be negotiated successfully.
# File lib/net/ssh/authentication/agent/socket.rb, line 74 def negotiate! # determine what type of agent we're communicating with type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION) if type == SSH2_AGENT_VERSION_RESPONSE raise AgentNotAvailable, "SSH2 agents are not yet supported" elsif type == SSH2_AGENT_FAILURE debug { "Unexpected response type==#{type}, this will be ignored" } elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2 raise AgentNotAvailable, "unknown response from agent: #{type}, #{body.to_s.inspect}" end end
Using the agent and the given public key, sign the given data. The signature is returned in SSH2 format.
# File lib/net/ssh/authentication/agent/java_pageant.rb, line 75 def sign(key, data) signed = @agent_proxy.sign(key.java_blob, data.to_java_bytes) String.from_java_bytes(signed) rescue AgentProxyException => e raise AgentError, "agent could not sign data with requested identity: #{e.message}", e.backtrace end
Private Instance Methods
Returns true
if the parameter indicates a “failure” response
from the agent, and false
otherwise.
# File lib/net/ssh/authentication/agent/socket.rb, line 171 def agent_failed(type) type == SSH_AGENT_FAILURE || type == SSH2_AGENT_FAILURE || type == SSH_COM_AGENT2_FAILURE end
Returns the agent socket factory to use.
# File lib/net/ssh/authentication/agent/socket.rb, line 135 def agent_socket_factory if Net::SSH::Authentication::PLATFORM == :win32 Pageant::Socket else UNIXSocket end end
Read the next packet from the agent. This will return a two-part tuple consisting of the packet type, and the packet's body (which is returned as a Net::SSH::Buffer).
# File lib/net/ssh/authentication/agent/socket.rb, line 154 def read_packet buffer = Net::SSH::Buffer.new(@socket.read(4)) buffer.append(@socket.read(buffer.read_long)) type = buffer.read_byte debug { "received agent packet #{type} len #{buffer.length-4}" } return type, buffer end
Send the given packet and return the subsequent reply from the agent. (See send_packet and read_packet).
# File lib/net/ssh/authentication/agent/socket.rb, line 164 def send_and_wait(type, *args) send_packet(type, *args) read_packet end
Send a new packet of the given type, with the associated data.
# File lib/net/ssh/authentication/agent/socket.rb, line 144 def send_packet(type, *args) buffer = Buffer.from(*args) data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*") debug { "sending agent request #{type} len #{buffer.length}" } @socket.send data, 0 end