summaryrefslogtreecommitdiffhomepage
path: root/paramiko/agent.py
diff options
context:
space:
mode:
authorJeff Forcier <jeff@bitprophet.org>2014-09-05 10:39:00 -0700
committerJeff Forcier <jeff@bitprophet.org>2014-09-05 10:39:00 -0700
commit0e0460f85942e79c94b7db9d93abdf60bf1dbac4 (patch)
tree152b80b4a2822abcb85136f7fbc7f9263279eb14 /paramiko/agent.py
parent683b3c22893be899d2fdbf1ada35e61fba8a3d70 (diff)
parenteb7da84bee49f6e7b568ee00fd5f3db0bb29f36a (diff)
Merge branch 'master' into 131-int
Diffstat (limited to 'paramiko/agent.py')
-rw-r--r--paramiko/agent.py147
1 files changed, 75 insertions, 72 deletions
diff --git a/paramiko/agent.py b/paramiko/agent.py
index 5d04dce8..5a08d452 100644
--- a/paramiko/agent.py
+++ b/paramiko/agent.py
@@ -7,7 +7,7 @@
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
-# Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY
+# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
@@ -17,7 +17,7 @@
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
-SSH Agent interface for Unix clients.
+SSH Agent interface
"""
import os
@@ -29,28 +29,21 @@ import time
import tempfile
import stat
from select import select
+from paramiko.common import asbytes, io_sleep
+from paramiko.py3compat import byte_chr
from paramiko.ssh_exception import SSHException
from paramiko.message import Message
from paramiko.pkey import PKey
-from paramiko.channel import Channel
-from paramiko.common import io_sleep
from paramiko.util import retry_on_signal
-SSH2_AGENTC_REQUEST_IDENTITIES, SSH2_AGENT_IDENTITIES_ANSWER, \
- SSH2_AGENTC_SIGN_REQUEST, SSH2_AGENT_SIGN_RESPONSE = range(11, 15)
+cSSH2_AGENTC_REQUEST_IDENTITIES = byte_chr(11)
+SSH2_AGENT_IDENTITIES_ANSWER = 12
+cSSH2_AGENTC_SIGN_REQUEST = byte_chr(13)
+SSH2_AGENT_SIGN_RESPONSE = 14
-class AgentSSH(object):
- """
- Client interface for using private keys from an SSH agent running on the
- local machine. If an SSH agent is running, this class can be used to
- connect to it and retreive L{PKey} objects which can be used when
- attempting to authenticate to remote SSH servers.
- Because the SSH agent protocol uses environment variables and unix-domain
- sockets, this probably doesn't work on Windows. It does work on most
- posix platforms though (Linux and MacOS X, for example).
- """
+class AgentSSH(object):
def __init__(self):
self._conn = None
self._keys = ()
@@ -61,19 +54,20 @@ class AgentSSH(object):
no SSH agent was running (or it couldn't be contacted), an empty list
will be returned.
- @return: a list of keys available on the SSH agent
- @rtype: tuple of L{AgentKey}
+ :return:
+ a tuple of `.AgentKey` objects representing keys available on the
+ SSH agent
"""
return self._keys
def _connect(self, conn):
self._conn = conn
- ptype, result = self._send_message(chr(SSH2_AGENTC_REQUEST_IDENTITIES))
+ ptype, result = self._send_message(cSSH2_AGENTC_REQUEST_IDENTITIES)
if ptype != SSH2_AGENT_IDENTITIES_ANSWER:
raise SSHException('could not get keys from ssh-agent')
keys = []
for i in range(result.get_int()):
- keys.append(AgentKey(self, result.get_string()))
+ keys.append(AgentKey(self, result.get_binary()))
result.get_string()
self._keys = tuple(keys)
@@ -83,7 +77,7 @@ class AgentSSH(object):
self._keys = ()
def _send_message(self, msg):
- msg = str(msg)
+ msg = asbytes(msg)
self._conn.send(struct.pack('>I', len(msg)) + msg)
l = self._read_all(4)
msg = Message(self._read_all(struct.unpack('>I', l)[0]))
@@ -100,8 +94,11 @@ class AgentSSH(object):
result += extra
return result
+
class AgentProxyThread(threading.Thread):
- """ Class in charge of communication between two chan """
+ """
+ Class in charge of communication between two channels.
+ """
def __init__(self, agent):
threading.Thread.__init__(self, target=self.run)
self._agent = agent
@@ -109,7 +106,7 @@ class AgentProxyThread(threading.Thread):
def run(self):
try:
- (r,addr) = self.get_connection()
+ (r, addr) = self.get_connection()
self.__inr = r
self.__addr = addr
self._agent.connect()
@@ -130,15 +127,23 @@ class AgentProxyThread(threading.Thread):
if len(data) != 0:
self.__inr.send(data)
else:
+ self._close()
break
elif self.__inr == fd:
data = self.__inr.recv(512)
if len(data) != 0:
self._agent._conn.send(data)
else:
+ self._close()
break
time.sleep(io_sleep)
+ def _close(self):
+ self._exit = True
+ self.__inr.close()
+ self._agent._conn.close()
+
+
class AgentLocalProxy(AgentProxyThread):
"""
Class to be used when wanting to ask a local SSH Agent being
@@ -148,18 +153,20 @@ class AgentLocalProxy(AgentProxyThread):
AgentProxyThread.__init__(self, agent)
def get_connection(self):
- """ Return a pair of socket object and string address
- May Block !
+ """
+ Return a pair of socket object and string address.
+
+ May block!
"""
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
conn.bind(self._agent._get_filename())
conn.listen(1)
- (r,addr) = conn.accept()
- return (r, addr)
+ (r, addr) = conn.accept()
+ return r, addr
except:
raise
- return None
+
class AgentRemoteProxy(AgentProxyThread):
"""
@@ -170,22 +177,20 @@ class AgentRemoteProxy(AgentProxyThread):
self.__chan = chan
def get_connection(self):
- """
- Class to be used when wanting to ask a local SSH Agent being
- asked from a remote fake agent (so use a unix socket for ex.)
- """
- return (self.__chan, None)
+ return self.__chan, None
+
class AgentClientProxy(object):
"""
Class proxying request as a client:
- -> client ask for a request_forward_agent()
- -> server creates a proxy and a fake SSH Agent
- -> server ask for establishing a connection when needed,
+
+ #. client ask for a request_forward_agent()
+ #. server creates a proxy and a fake SSH Agent
+ #. server ask for establishing a connection when needed,
calling the forward_agent_handler at client side.
- -> the forward_agent_handler launch a thread for connecting
+ #. the forward_agent_handler launch a thread for connecting
the remote fake agent and the local agent
- -> Communication occurs ...
+ #. Communication occurs ...
"""
def __init__(self, chanRemote):
self._conn = None
@@ -198,7 +203,7 @@ class AgentClientProxy(object):
def connect(self):
"""
- Method automatically called by the run() method of the AgentProxyThread
+ Method automatically called by ``AgentProxyThread.run``.
"""
if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
conn = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
@@ -208,7 +213,7 @@ class AgentClientProxy(object):
# probably a dangling env var: the ssh agent is gone
return
elif sys.platform == 'win32':
- import win_pageant
+ import paramiko.win_pageant as win_pageant
if win_pageant.can_talk_to_agent():
conn = win_pageant.PageantConnection()
else:
@@ -229,11 +234,12 @@ class AgentClientProxy(object):
if self._conn is not None:
self._conn.close()
+
class AgentServerProxy(AgentSSH):
"""
- @param t : transport used for the Forward for SSH Agent communication
+ :param .Transport t: Transport used for SSH Agent communication forwarding
- @raise SSHException: mostly if we lost the agent
+ :raises SSHException: mostly if we lost the agent
"""
def __init__(self, t):
AgentSSH.__init__(self)
@@ -248,11 +254,11 @@ class AgentServerProxy(AgentSSH):
self.close()
def connect(self):
- conn_sock = self.__t.open_forward_agent_channel()
- if conn_sock is None:
- raise SSHException('lost ssh-agent')
- conn_sock.set_name('auth-agent')
- self._connect(conn_sock)
+ conn_sock = self.__t.open_forward_agent_channel()
+ if conn_sock is None:
+ raise SSHException('lost ssh-agent')
+ conn_sock.set_name('auth-agent')
+ self._connect(conn_sock)
def close(self):
"""
@@ -269,16 +275,15 @@ class AgentServerProxy(AgentSSH):
"""
Helper for the environnement under unix
- @return: the SSH_AUTH_SOCK Environnement variables
- @rtype: dict
+ :return:
+ a dict containing the ``SSH_AUTH_SOCK`` environnement variables
"""
- env = {}
- env['SSH_AUTH_SOCK'] = self._get_filename()
- return env
+ return {'SSH_AUTH_SOCK': self._get_filename()}
def _get_filename(self):
return self._file
+
class AgentRequestHandler(object):
def __init__(self, chanClient):
self._conn = None
@@ -296,27 +301,22 @@ class AgentRequestHandler(object):
for p in self.__clientProxys:
p.close()
+
class Agent(AgentSSH):
"""
Client interface for using private keys from an SSH agent running on the
local machine. If an SSH agent is running, this class can be used to
- connect to it and retreive L{PKey} objects which can be used when
+ connect to it and retreive `.PKey` objects which can be used when
attempting to authenticate to remote SSH servers.
- Because the SSH agent protocol uses environment variables and unix-domain
- sockets, this probably doesn't work on Windows. It does work on most
- posix platforms though (Linux and MacOS X, for example).
- """
+ Upon initialization, a session with the local machine's SSH agent is
+ opened, if one is running. If no agent is running, initialization will
+ succeed, but `get_keys` will return an empty tuple.
+ :raises SSHException:
+ if an SSH agent is found, but speaks an incompatible protocol
+ """
def __init__(self):
- """
- Open a session with the local machine's SSH agent, if one is running.
- If no agent is running, initialization will succeed, but L{get_keys}
- will return an empty tuple.
-
- @raise SSHException: if an SSH agent is found, but speaks an
- incompatible protocol
- """
AgentSSH.__init__(self)
if ('SSH_AUTH_SOCK' in os.environ) and (sys.platform != 'win32'):
@@ -327,7 +327,7 @@ class Agent(AgentSSH):
# probably a dangling env var: the ssh agent is gone
return
elif sys.platform == 'win32':
- import win_pageant
+ from . import win_pageant
if win_pageant.can_talk_to_agent():
conn = win_pageant.PageantConnection()
else:
@@ -343,31 +343,34 @@ class Agent(AgentSSH):
"""
self._close()
+
class AgentKey(PKey):
"""
Private key held in a local SSH agent. This type of key can be used for
authenticating to a remote server (signing). Most other key operations
work as expected.
"""
-
def __init__(self, agent, blob):
self.agent = agent
self.blob = blob
- self.name = Message(blob).get_string()
+ self.name = Message(blob).get_text()
- def __str__(self):
+ def asbytes(self):
return self.blob
+ def __str__(self):
+ return self.asbytes()
+
def get_name(self):
return self.name
- def sign_ssh_data(self, rng, data):
+ def sign_ssh_data(self, data):
msg = Message()
- msg.add_byte(chr(SSH2_AGENTC_SIGN_REQUEST))
+ msg.add_byte(cSSH2_AGENTC_SIGN_REQUEST)
msg.add_string(self.blob)
msg.add_string(data)
msg.add_int(0)
ptype, result = self.agent._send_message(msg)
if ptype != SSH2_AGENT_SIGN_RESPONSE:
raise SSHException('key cannot be used for signing')
- return result.get_string()
+ return result.get_binary()