Class: Orthrus::SSH::Agent
- Inherits:
-
Object
- Object
- Orthrus::SSH::Agent
- Defined in:
- lib/orthrus/ssh/agent.rb
Overview
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).
Constant Summary collapse
- SSH2_AGENT_REQUEST_VERSION =
1
- SSH2_AGENT_REQUEST_IDENTITIES =
11
- SSH2_AGENT_IDENTITIES_ANSWER =
12
- SSH2_AGENT_SIGN_REQUEST =
13
- SSH2_AGENT_SIGN_RESPONSE =
14
- SSH2_AGENT_FAILURE =
30
- SSH2_AGENT_VERSION_RESPONSE =
103
- SSH_COM_AGENT2_FAILURE =
102
- SSH_AGENT_REQUEST_RSA_IDENTITIES =
1
- SSH_AGENT_RSA_IDENTITIES_ANSWER1 =
2
- SSH_AGENT_RSA_IDENTITIES_ANSWER2 =
5
- SSH_AGENT_FAILURE =
5
- ID =
"SSH-2.0-Ruby/Orthrus #{RUBY_PLATFORM}"
Instance Attribute Summary collapse
-
#socket ⇒ Object
readonly
The underlying socket being used to communicate with the SSH agent.
Class Method Summary collapse
- .available? ⇒ Boolean
-
.connect ⇒ Object
Instantiates a new agent object, connects to a running SSH agent, negotiates the agent protocol version, and returns the agent object.
Instance Method Summary collapse
-
#close ⇒ Object
Closes this socket.
-
#connect! ⇒ Object
Connect to the agent process using the socket factory and socket name given by the attribute writers.
- #hexsign(key, data) ⇒ Object
-
#identities ⇒ Object
Return an array of all identities (public keys) known to the agent.
-
#initialize ⇒ Agent
constructor
Creates a new Agent object.
-
#negotiate! ⇒ Object
Attempts to negotiate the SSH agent protocol version.
-
#sign(key, data) ⇒ Object
Using the agent and the given public key, sign the given data.
Constructor Details
#initialize ⇒ Agent
Creates a new Agent object.
54 55 56 |
# File 'lib/orthrus/ssh/agent.rb', line 54 def initialize @socket = nil end |
Instance Attribute Details
#socket ⇒ Object (readonly)
The underlying socket being used to communicate with the SSH agent.
38 39 40 |
# File 'lib/orthrus/ssh/agent.rb', line 38 def socket @socket end |
Class Method Details
.available? ⇒ Boolean
49 50 51 |
# File 'lib/orthrus/ssh/agent.rb', line 49 def self.available? ENV.key?("SSH_AUTH_SOCK") && File.exists?(ENV['SSH_AUTH_SOCK']) end |
.connect ⇒ Object
Instantiates a new agent object, connects to a running SSH agent, negotiates the agent protocol version, and returns the agent object.
42 43 44 45 46 47 |
# File 'lib/orthrus/ssh/agent.rb', line 42 def self.connect agent = new agent.connect! agent.negotiate! agent end |
Instance Method Details
#close ⇒ Object
Closes this socket. This agent reference is no longer able to query the agent.
114 115 116 |
# File 'lib/orthrus/ssh/agent.rb', line 114 def close @socket.close end |
#connect! ⇒ Object
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).
62 63 64 65 66 67 68 |
# File 'lib/orthrus/ssh/agent.rb', line 62 def connect! begin @socket = UNIXSocket.open(ENV['SSH_AUTH_SOCK']) rescue raise AgentNotAvailable, $!. end end |
#hexsign(key, data) ⇒ Object
136 137 138 139 140 |
# File 'lib/orthrus/ssh/agent.rb', line 136 def hexsign(key, data) type, sig = sign key, data [type, [sig].pack("m").gsub("\n","")] end |
#identities ⇒ Object
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.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/orthrus/ssh/agent.rb', line 88 def identities type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES) raise AgentError, "could not get identity count" if agent_failed(type) raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER identities = [] body.read_long.times do key = Buffer.new(body.read_string).read_key case key when OpenSSL::PKey::RSA key = RSAPublicKey.new key when OpenSSL::PKey::DSA key = DSAPublicKey.new key else raise AgentError, "Unknown key type - #{key.class}" end key.comment = body.read_string identities.push key end return identities end |
#negotiate! ⇒ Object
Attempts to negotiate the SSH agent protocol version. Raises an error if the version could not be negotiated successfully.
74 75 76 77 78 79 80 81 82 83 |
# File 'lib/orthrus/ssh/agent.rb', line 74 def negotiate! # determine what type of agent we're communicating with type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, ID) if type == SSH2_AGENT_VERSION_RESPONSE raise NotImplementedError, "SSH2 agents are not yet supported" elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2 raise AgentError, "unknown response from agent: #{type}, #{body.to_s.inspect}" end end |
#sign(key, data) ⇒ Object
Using the agent and the given public key, sign the given data. The signature is returned in SSH2 format.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/orthrus/ssh/agent.rb', line 120 def sign(key, data) type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, 0) if agent_failed(type) raise AgentError, "agent could not sign data with requested identity" elsif type != SSH2_AGENT_SIGN_RESPONSE raise AgentError, "bad authentication response #{type}" end b = Buffer.new reply.read_string [b.read_string, b.read_string] end |