2009-07-19 22:45:02 -04:00
|
|
|
# Copyright (C) 2003-2009 Robey Pointer <robeypointer@gmail.com>
|
2006-05-07 20:20:07 -04:00
|
|
|
#
|
|
|
|
# This file is part of paramiko.
|
|
|
|
#
|
|
|
|
# Paramiko is free software; you can redistribute it and/or modify it under the
|
|
|
|
# terms of the GNU Lesser General Public License as published by the Free
|
|
|
|
# Software Foundation; either version 2.1 of the License, or (at your option)
|
|
|
|
# any later version.
|
|
|
|
#
|
2013-09-28 00:29:18 -04:00
|
|
|
# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
|
2006-05-07 20:20:07 -04:00
|
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
|
|
# details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
|
|
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
|
|
|
|
|
|
"""
|
|
|
|
Some unit tests for SSHClient.
|
|
|
|
"""
|
|
|
|
|
|
|
|
import socket
|
2014-01-16 23:15:16 -05:00
|
|
|
from tempfile import mkstemp
|
2006-05-07 20:20:07 -04:00
|
|
|
import threading
|
|
|
|
import unittest
|
2006-12-15 17:21:08 -05:00
|
|
|
import weakref
|
2014-01-07 16:36:28 -05:00
|
|
|
import warnings
|
|
|
|
import os
|
2013-10-30 19:19:30 -04:00
|
|
|
from tests.util import test_path
|
2006-05-07 20:20:07 -04:00
|
|
|
import paramiko
|
|
|
|
|
|
|
|
|
|
|
|
class NullServer (paramiko.ServerInterface):
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2006-05-07 20:20:07 -04:00
|
|
|
def get_allowed_auths(self, username):
|
|
|
|
if username == 'slowdive':
|
|
|
|
return 'publickey,password'
|
|
|
|
return 'publickey'
|
|
|
|
|
|
|
|
def check_auth_password(self, username, password):
|
|
|
|
if (username == 'slowdive') and (password == 'pygmalion'):
|
|
|
|
return paramiko.AUTH_SUCCESSFUL
|
|
|
|
return paramiko.AUTH_FAILED
|
|
|
|
|
2007-02-10 21:25:53 -05:00
|
|
|
def check_auth_publickey(self, username, key):
|
2013-11-19 13:09:08 -05:00
|
|
|
if (key.get_name() == 'ssh-dss') and key.get_fingerprint() == b'\x44\x78\xf0\xb9\xa2\x3c\xc5\x18\x20\x09\xff\x75\x5b\xc1\xd2\x6c':
|
2007-02-10 21:25:53 -05:00
|
|
|
return paramiko.AUTH_SUCCESSFUL
|
|
|
|
return paramiko.AUTH_FAILED
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2006-05-07 20:20:07 -04:00
|
|
|
def check_channel_request(self, kind, chanid):
|
|
|
|
return paramiko.OPEN_SUCCEEDED
|
|
|
|
|
|
|
|
def check_channel_exec_request(self, channel, command):
|
|
|
|
if command != 'yes':
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
class SSHClientTest (unittest.TestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.sockl = socket.socket()
|
|
|
|
self.sockl.bind(('localhost', 0))
|
|
|
|
self.sockl.listen(1)
|
|
|
|
self.addr, self.port = self.sockl.getsockname()
|
|
|
|
self.event = threading.Event()
|
|
|
|
|
|
|
|
def tearDown(self):
|
2012-09-10 14:34:52 -04:00
|
|
|
for attr in "tc ts socks sockl".split():
|
|
|
|
if hasattr(self, attr):
|
|
|
|
getattr(self, attr).close()
|
2006-05-07 20:20:07 -04:00
|
|
|
|
|
|
|
def _run(self):
|
|
|
|
self.socks, addr = self.sockl.accept()
|
|
|
|
self.ts = paramiko.Transport(self.socks)
|
2013-10-30 19:22:52 -04:00
|
|
|
host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
|
2006-05-07 20:20:07 -04:00
|
|
|
self.ts.add_server_key(host_key)
|
|
|
|
server = NullServer()
|
|
|
|
self.ts.start_server(self.event, server)
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2006-05-07 20:20:07 -04:00
|
|
|
def test_1_client(self):
|
|
|
|
"""
|
|
|
|
verify that the SSHClient stuff works too.
|
|
|
|
"""
|
2014-01-16 23:15:16 -05:00
|
|
|
threading.Thread(target=self._run).start()
|
2013-10-30 19:22:52 -04:00
|
|
|
host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
|
2013-10-30 20:09:34 -04:00
|
|
|
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2006-05-07 20:20:07 -04:00
|
|
|
self.tc = paramiko.SSHClient()
|
2009-11-02 00:28:47 -05:00
|
|
|
self.tc.get_host_keys().add('[%s]:%d' % (self.addr, self.port), 'ssh-rsa', public_host_key)
|
2006-05-07 20:20:07 -04:00
|
|
|
self.tc.connect(self.addr, self.port, username='slowdive', password='pygmalion')
|
|
|
|
|
|
|
|
self.event.wait(1.0)
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertTrue(self.event.isSet())
|
|
|
|
self.assertTrue(self.ts.is_active())
|
|
|
|
self.assertEqual('slowdive', self.ts.get_username())
|
|
|
|
self.assertEqual(True, self.ts.is_authenticated())
|
2006-05-07 20:20:07 -04:00
|
|
|
|
|
|
|
stdin, stdout, stderr = self.tc.exec_command('yes')
|
|
|
|
schan = self.ts.accept(1.0)
|
|
|
|
|
|
|
|
schan.send('Hello there.\n')
|
|
|
|
schan.send_stderr('This is on stderr.\n')
|
|
|
|
schan.close()
|
|
|
|
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertEqual('Hello there.\n', stdout.readline())
|
|
|
|
self.assertEqual('', stdout.readline())
|
|
|
|
self.assertEqual('This is on stderr.\n', stderr.readline())
|
|
|
|
self.assertEqual('', stderr.readline())
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2006-05-07 20:20:07 -04:00
|
|
|
stdin.close()
|
|
|
|
stdout.close()
|
|
|
|
stderr.close()
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2007-02-10 21:25:53 -05:00
|
|
|
def test_2_client_dsa(self):
|
|
|
|
"""
|
|
|
|
verify that SSHClient works with a DSA key.
|
|
|
|
"""
|
2014-01-16 23:15:16 -05:00
|
|
|
threading.Thread(target=self._run).start()
|
2013-10-30 19:22:52 -04:00
|
|
|
host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
|
2013-10-30 20:09:34 -04:00
|
|
|
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2007-02-10 21:25:53 -05:00
|
|
|
self.tc = paramiko.SSHClient()
|
2009-11-02 00:28:47 -05:00
|
|
|
self.tc.get_host_keys().add('[%s]:%d' % (self.addr, self.port), 'ssh-rsa', public_host_key)
|
2013-10-30 19:22:52 -04:00
|
|
|
self.tc.connect(self.addr, self.port, username='slowdive', key_filename=test_path('test_dss.key'))
|
2007-02-10 21:25:53 -05:00
|
|
|
|
|
|
|
self.event.wait(1.0)
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertTrue(self.event.isSet())
|
|
|
|
self.assertTrue(self.ts.is_active())
|
|
|
|
self.assertEqual('slowdive', self.ts.get_username())
|
|
|
|
self.assertEqual(True, self.ts.is_authenticated())
|
2007-02-10 21:25:53 -05:00
|
|
|
|
|
|
|
stdin, stdout, stderr = self.tc.exec_command('yes')
|
|
|
|
schan = self.ts.accept(1.0)
|
|
|
|
|
|
|
|
schan.send('Hello there.\n')
|
|
|
|
schan.send_stderr('This is on stderr.\n')
|
|
|
|
schan.close()
|
|
|
|
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertEqual('Hello there.\n', stdout.readline())
|
|
|
|
self.assertEqual('', stdout.readline())
|
|
|
|
self.assertEqual('This is on stderr.\n', stderr.readline())
|
|
|
|
self.assertEqual('', stderr.readline())
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2007-02-10 21:25:53 -05:00
|
|
|
stdin.close()
|
|
|
|
stdout.close()
|
|
|
|
stderr.close()
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2008-07-06 16:37:06 -04:00
|
|
|
def test_3_multiple_key_files(self):
|
|
|
|
"""
|
|
|
|
verify that SSHClient accepts and tries multiple key files.
|
|
|
|
"""
|
2014-01-16 23:15:16 -05:00
|
|
|
threading.Thread(target=self._run).start()
|
2013-10-30 19:22:52 -04:00
|
|
|
host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
|
2013-10-30 20:09:34 -04:00
|
|
|
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2008-07-06 16:37:06 -04:00
|
|
|
self.tc = paramiko.SSHClient()
|
2009-11-02 00:28:47 -05:00
|
|
|
self.tc.get_host_keys().add('[%s]:%d' % (self.addr, self.port), 'ssh-rsa', public_host_key)
|
2013-10-30 19:22:52 -04:00
|
|
|
self.tc.connect(self.addr, self.port, username='slowdive', key_filename=[ test_path('test_rsa.key'), test_path('test_dss.key') ])
|
2008-07-06 16:37:06 -04:00
|
|
|
|
|
|
|
self.event.wait(1.0)
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertTrue(self.event.isSet())
|
|
|
|
self.assertTrue(self.ts.is_active())
|
|
|
|
self.assertEqual('slowdive', self.ts.get_username())
|
|
|
|
self.assertEqual(True, self.ts.is_authenticated())
|
2006-05-07 20:20:07 -04:00
|
|
|
|
2008-07-06 16:37:06 -04:00
|
|
|
def test_4_auto_add_policy(self):
|
2006-05-07 20:20:07 -04:00
|
|
|
"""
|
|
|
|
verify that SSHClient's AutoAddPolicy works.
|
|
|
|
"""
|
2014-01-16 23:15:16 -05:00
|
|
|
threading.Thread(target=self._run).start()
|
2013-10-30 19:22:52 -04:00
|
|
|
host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
|
2013-10-30 20:09:34 -04:00
|
|
|
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2006-05-07 20:20:07 -04:00
|
|
|
self.tc = paramiko.SSHClient()
|
|
|
|
self.tc.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertEqual(0, len(self.tc.get_host_keys()))
|
2006-05-07 20:20:07 -04:00
|
|
|
self.tc.connect(self.addr, self.port, username='slowdive', password='pygmalion')
|
|
|
|
|
|
|
|
self.event.wait(1.0)
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertTrue(self.event.isSet())
|
|
|
|
self.assertTrue(self.ts.is_active())
|
|
|
|
self.assertEqual('slowdive', self.ts.get_username())
|
|
|
|
self.assertEqual(True, self.ts.is_authenticated())
|
|
|
|
self.assertEqual(1, len(self.tc.get_host_keys()))
|
|
|
|
self.assertEqual(public_host_key, self.tc.get_host_keys()['[%s]:%d' % (self.addr, self.port)]['ssh-rsa'])
|
2006-12-15 17:21:08 -05:00
|
|
|
|
2014-01-07 16:36:28 -05:00
|
|
|
def test_5_save_host_keys(self):
|
|
|
|
"""
|
|
|
|
verify that SSHClient correctly saves a known_hosts file.
|
|
|
|
"""
|
|
|
|
warnings.filterwarnings('ignore', 'tempnam.*')
|
|
|
|
|
2014-01-16 23:15:16 -05:00
|
|
|
host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
|
|
|
|
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
|
|
|
|
fd, localname = mkstemp()
|
|
|
|
os.close(fd)
|
2014-01-07 16:36:28 -05:00
|
|
|
|
2014-01-08 15:35:46 -05:00
|
|
|
client = paramiko.SSHClient()
|
|
|
|
self.assertEquals(0, len(client.get_host_keys()))
|
2014-01-07 16:36:28 -05:00
|
|
|
|
2014-01-08 15:39:11 -05:00
|
|
|
host_id = '[%s]:%d' % (self.addr, self.port)
|
|
|
|
|
|
|
|
client.get_host_keys().add(host_id, 'ssh-rsa', public_host_key)
|
2014-01-08 15:35:46 -05:00
|
|
|
self.assertEquals(1, len(client.get_host_keys()))
|
2014-01-08 15:39:11 -05:00
|
|
|
self.assertEquals(public_host_key, client.get_host_keys()[host_id]['ssh-rsa'])
|
2014-01-07 16:36:28 -05:00
|
|
|
|
2014-01-08 15:35:46 -05:00
|
|
|
client.save_host_keys(localname)
|
2014-01-08 15:44:12 -05:00
|
|
|
|
|
|
|
with open(localname) as fd:
|
|
|
|
assert host_id in fd.read()
|
2014-01-07 16:36:28 -05:00
|
|
|
|
|
|
|
os.unlink(localname)
|
|
|
|
|
|
|
|
def test_6_cleanup(self):
|
2006-12-15 17:21:08 -05:00
|
|
|
"""
|
|
|
|
verify that when an SSHClient is collected, its transport (and the
|
|
|
|
transport's packetizer) is closed.
|
|
|
|
"""
|
2014-01-16 23:15:16 -05:00
|
|
|
threading.Thread(target=self._run).start()
|
2013-10-30 19:22:52 -04:00
|
|
|
host_key = paramiko.RSAKey.from_private_key_file(test_path('test_rsa.key'))
|
2013-10-30 20:09:34 -04:00
|
|
|
public_host_key = paramiko.RSAKey(data=host_key.asbytes())
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2006-12-15 17:21:08 -05:00
|
|
|
self.tc = paramiko.SSHClient()
|
|
|
|
self.tc.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertEqual(0, len(self.tc.get_host_keys()))
|
2006-12-15 17:21:08 -05:00
|
|
|
self.tc.connect(self.addr, self.port, username='slowdive', password='pygmalion')
|
|
|
|
|
|
|
|
self.event.wait(1.0)
|
2013-11-02 23:18:18 -04:00
|
|
|
self.assertTrue(self.event.isSet())
|
|
|
|
self.assertTrue(self.ts.is_active())
|
2009-11-02 00:28:47 -05:00
|
|
|
|
2006-12-15 17:21:08 -05:00
|
|
|
p = weakref.ref(self.tc._transport.packetizer)
|
2013-11-02 17:56:43 -04:00
|
|
|
self.assertTrue(p() is not None)
|
|
|
|
self.tc.close()
|
2006-12-15 17:21:08 -05:00
|
|
|
del self.tc
|
2013-11-02 17:56:43 -04:00
|
|
|
|
2006-12-27 14:41:21 -05:00
|
|
|
# hrm, sometimes p isn't cleared right away. why is that?
|
2013-11-01 12:49:52 -04:00
|
|
|
#st = time.time()
|
|
|
|
#while (time.time() - st < 5.0) and (p() is not None):
|
|
|
|
# time.sleep(0.1)
|
|
|
|
|
|
|
|
# instead of dumbly waiting for the GC to collect, force a collection
|
|
|
|
# to see whether the SSHClient object is deallocated correctly
|
|
|
|
import gc
|
|
|
|
gc.collect()
|
|
|
|
|
2013-11-02 17:56:43 -04:00
|
|
|
self.assertTrue(p() is None)
|