diff options
author | Jeff Forcier <jeff@bitprophet.org> | 2014-09-05 10:39:00 -0700 |
---|---|---|
committer | Jeff Forcier <jeff@bitprophet.org> | 2014-09-05 10:39:00 -0700 |
commit | 0e0460f85942e79c94b7db9d93abdf60bf1dbac4 (patch) | |
tree | 152b80b4a2822abcb85136f7fbc7f9263279eb14 /paramiko/agent.py | |
parent | 683b3c22893be899d2fdbf1ada35e61fba8a3d70 (diff) | |
parent | eb7da84bee49f6e7b568ee00fd5f3db0bb29f36a (diff) |
Merge branch 'master' into 131-int
Diffstat (limited to 'paramiko/agent.py')
-rw-r--r-- | paramiko/agent.py | 147 |
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() |