├── .bzrignore ├── .gitignore ├── .travis.yml ├── ChangeLog.0 ├── ChangeLog.1 ├── LICENSE ├── MANIFEST.in ├── NEWS ├── NOTES ├── README ├── README.md ├── TODO ├── demos ├── demo.py ├── demo_keygen.py ├── demo_server.py ├── demo_sftp.py ├── demo_simple.py ├── forward.py ├── fuzzing_corpus.py ├── interactive.py ├── rforward.py ├── test_rsa.key ├── user_rsa_key └── user_rsa_key.pub ├── dev-requirements.txt ├── images ├── paramiko-banner.png ├── paramiko-banner.psd └── paramiko.png ├── paramiko ├── __init__.py ├── _version.py ├── _winapi.py ├── agent.py ├── auth_handler.py ├── ber.py ├── buffered_pipe.py ├── channel.py ├── client.py ├── common.py ├── compress.py ├── config.py ├── dsskey.py ├── ecdsakey.py ├── file.py ├── fuzz.py ├── hostkeys.py ├── kex_gex.py ├── kex_group1.py ├── kex_group14.py ├── kex_gss.py ├── message.py ├── packet.py ├── pipe.py ├── pkey.py ├── primes.py ├── proxy.py ├── py3compat.py ├── resource.py ├── rsakey.py ├── server.py ├── sftp.py ├── sftp_attr.py ├── sftp_client.py ├── sftp_file.py ├── sftp_handle.py ├── sftp_server.py ├── sftp_si.py ├── ssh_exception.py ├── ssh_gss.py ├── transport.py ├── util.py └── win_pageant.py ├── setup.cfg ├── setup.py ├── setup_helper.py ├── sites ├── docs │ ├── api │ │ ├── agent.rst │ │ ├── buffered_pipe.rst │ │ ├── channel.rst │ │ ├── client.rst │ │ ├── config.rst │ │ ├── file.rst │ │ ├── hostkeys.rst │ │ ├── kex_gss.rst │ │ ├── keys.rst │ │ ├── message.rst │ │ ├── packet.rst │ │ ├── pipe.rst │ │ ├── proxy.rst │ │ ├── server.rst │ │ ├── sftp.rst │ │ ├── ssh_exception.rst │ │ ├── ssh_gss.rst │ │ └── transport.rst │ ├── conf.py │ └── index.rst ├── shared_conf.py └── www │ ├── _templates │ └── rss.xml │ ├── changelog.rst │ ├── conf.py │ ├── contact.rst │ ├── contributing.rst │ ├── faq.rst │ ├── index.rst │ └── installing.rst ├── tasks.py ├── test.py ├── tests ├── __init__.py ├── loop.py ├── stub_sftp.py ├── test_auth.py ├── test_buffered_pipe.py ├── test_client.py ├── test_dss.key ├── test_dss_password.key ├── test_ecdsa.key ├── test_ecdsa_password.key ├── test_file.py ├── test_gssapi.py ├── test_hostkeys.py ├── test_kex.py ├── test_kex_gss.py ├── test_message.py ├── test_packetizer.py ├── test_pkey.py ├── test_rsa.key ├── test_rsa_password.key ├── test_sftp.py ├── test_sftp_big.py ├── test_ssh_gss.py ├── test_transport.py ├── test_util.py └── util.py ├── tox-requirements.txt └── tox.ini /.bzrignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | ./build 3 | ./paramiko.egg-info 4 | ./dist 5 | ./.project 6 | ./paramiko.tmproj 7 | ./test.log 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build/ 3 | dist/ 4 | .tox/ 5 | paramiko.egg-info/ 6 | test.log 7 | docs/ 8 | demos/*.log 9 | !sites/docs 10 | _build 11 | .coverage 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | sudo: false 3 | python: 4 | - "2.6" 5 | - "2.7" 6 | - "3.3" 7 | - "3.4" 8 | - "3.5" 9 | install: 10 | # Self-install for setup.py-driven deps 11 | - pip install -e . 12 | # Dev (doc/test running) requirements 13 | - pip install coveralls # For coveralls.io specifically 14 | - pip install -r dev-requirements.txt 15 | script: 16 | # Main tests, w/ coverage! 17 | - inv test --coverage 18 | # Ensure documentation & invoke pipeline run OK. 19 | # Run 'docs' first since its objects.inv is referred to by 'www'. 20 | # Also force warnings to be errors since most of them tend to be actual 21 | # problems. 22 | - invoke docs -o -W www -o -W 23 | notifications: 24 | irc: 25 | channels: "irc.freenode.org#paramiko" 26 | template: 27 | - "%{repository}@%{branch}: %{message} (%{build_url})" 28 | on_success: change 29 | on_failure: change 30 | email: false 31 | after_success: 32 | - coveralls 33 | -------------------------------------------------------------------------------- /ChangeLog.0: -------------------------------------------------------------------------------- 1 | 2 | 2003-08-24: 3 | * implemented the other hashes: all 4 from the draft are working now 4 | * added 'aes128-cbc' and '3des-cbc' cipher support 5 | * fixed channel eof/close semantics 6 | 2003-09-12: version "aerodactyl" 7 | * implemented group-exchange kex ("kex-gex") 8 | * implemented RSA/DSA private key auth 9 | 2003-09-13: 10 | * fixed inflate_long and deflate_long to handle negatives, even though 11 | they're never used in the current ssh protocol 12 | 2003-09-14: 13 | * fixed session_id handling: re-keying works now 14 | * added the ability for a Channel to have a fileno() for select/poll 15 | purposes, although this will cause worse window performance if the 16 | client app isn't careful 17 | 2003-09-16: version "bulbasaur" 18 | * fixed pipe (fileno) method to be nonblocking and it seems to work now 19 | * fixed silly bug that caused large blocks to be truncated 20 | 2003-10-08: 21 | * patch to fix Channel.invoke_subsystem and add Channel.exec_command 22 | [vaclav dvorak] 23 | * patch to add Channel.sendall [vaclav dvorak] 24 | * patch to add Channel.shutdown [vaclav dvorak] 25 | * patch to add Channel.makefile and a ChannelFile class which emulates 26 | a python file object [vaclav dvorak] 27 | 2003-10-26: 28 | * thread creation no longer happens during construction -- use the new 29 | method "start_client(event)" to get things rolling 30 | * re-keying now takes place after 1GB of data or 1 billion packets 31 | (these limits can be easily changed per-session if needed) 32 | 2003-11-06: 33 | * added a demo server and host key 34 | 2003-11-09: 35 | * lots of changes to server mode 36 | * ChannelFile supports universal newline mode; fixed readline 37 | * fixed a bug with parsing the remote banner 38 | 2003-11-10: version "charmander" 39 | * renamed SSHException -> SecshException 40 | * cleaned up server mode and the demo server 41 | 42 | *** for all subsequent changes, please see 'tla changelog'. 43 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE test.py setup_helper.py 2 | recursive-include docs * 3 | recursive-include tests *.py *.key 4 | recursive-include demos *.py *.key user_rsa_key user_rsa_key.pub 5 | -------------------------------------------------------------------------------- /NOTES: -------------------------------------------------------------------------------- 1 | 2 | +-------------------+ +-----------------+ 3 | (Socket)InputStream ---> | ssh2 transport | <===> | ssh2 channel | 4 | (Socket)OutputStream --> | (auth, pipe) | N | (buffer) | 5 | +-------------------+ +-----------------+ 6 | @ feeder thread | | 7 | - read InputStream | +-> InputStream 8 | - feed into channel +---> OutputStream 9 | buffers 10 | 11 | SIS <-- @ --> (parse, find chan) --> ssh2 chan: buffer <-- SSHInputStream 12 | SSHOutputStream --> ssh2 chan --> ssh2 transport --> SOS [no thread] 13 | 14 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | ======== 3 | paramiko 4 | ======== 5 | 6 | :Paramiko: Python SSH module 7 | :Copyright: Copyright (c) 2003-2009 Robey Pointer 8 | :Copyright: Copyright (c) 2013-2015 Jeff Forcier 9 | :License: LGPL 10 | :Homepage: https://github.com/paramiko/paramiko/ 11 | :API docs: http://docs.paramiko.org 12 | 13 | 14 | What 15 | ---- 16 | 17 | "paramiko" is a combination of the esperanto words for "paranoid" and 18 | "friend". it's a module for python 2.6+ that implements the SSH2 protocol 19 | for secure (encrypted and authenticated) connections to remote machines. 20 | unlike SSL (aka TLS), SSH2 protocol does not require hierarchical 21 | certificates signed by a powerful central authority. you may know SSH2 as 22 | the protocol that replaced telnet and rsh for secure access to remote 23 | shells, but the protocol also includes the ability to open arbitrary 24 | channels to remote services across the encrypted tunnel (this is how sftp 25 | works, for example). 26 | 27 | it is written entirely in python (no C or platform-dependent code) and is 28 | released under the GNU LGPL (lesser GPL). 29 | 30 | the package and its API is fairly well documented in the "doc/" folder 31 | that should have come with this archive. 32 | 33 | 34 | Requirements 35 | ------------ 36 | 37 | - Python 2.6 or better - this includes Python 38 | 3.2 and higher as well. 39 | - pycrypto 2.1 or better 40 | - ecdsa 0.9 or better 41 | 42 | If you have setuptools, you can build and install paramiko and all its 43 | dependencies with this command (as root):: 44 | 45 | easy_install ./ 46 | 47 | 48 | Portability 49 | ----------- 50 | 51 | i code and test this library on Linux and MacOS X. for that reason, i'm 52 | pretty sure that it works for all posix platforms, including MacOS. it 53 | should also work on Windows, though i don't test it as frequently there. 54 | if you run into Windows problems, send me a patch: portability is important 55 | to me. 56 | 57 | some python distributions don't include the utf-8 string encodings, for 58 | reasons of space (misdirected as that is). if your distribution is 59 | missing encodings, you'll see an error like this:: 60 | 61 | LookupError: no codec search functions registered: can't find encoding 62 | 63 | this means you need to copy string encodings over from a working system. 64 | (it probably only happens on embedded systems, not normal python 65 | installs.) Valeriy Pogrebitskiy says the best place to look is 66 | ``.../lib/python*/encodings/__init__.py``. 67 | 68 | 69 | Bugs & Support 70 | -------------- 71 | 72 | Please file bug reports at https://github.com/paramiko/paramiko/. There is currently no mailing list but we plan to create a new one ASAP. 73 | 74 | 75 | Kerberos Support 76 | ---------------- 77 | 78 | Paramiko ships with optional Kerberos/GSSAPI support; for info on the extra 79 | dependencies for this, see the 'GSS-API' section on the 'Installation' page of 80 | our main website, http://paramiko.org . 81 | 82 | 83 | Demo 84 | ---- 85 | 86 | several demo scripts come with paramiko to demonstrate how to use it. 87 | probably the simplest demo of all is this:: 88 | 89 | import paramiko, base64 90 | key = paramiko.RSAKey(data=base64.decodestring('AAA...')) 91 | client = paramiko.SSHClient() 92 | client.get_host_keys().add('ssh.example.com', 'ssh-rsa', key) 93 | client.connect('ssh.example.com', username='strongbad', password='thecheat') 94 | stdin, stdout, stderr = client.exec_command('ls') 95 | for line in stdout: 96 | print '... ' + line.strip('\n') 97 | client.close() 98 | 99 | ...which prints out the results of executing ``ls`` on a remote server. 100 | (the host key 'AAA...' should of course be replaced by the actual base64 101 | encoding of the host key. if you skip host key verification, the 102 | connection is not secure!) 103 | 104 | the following example scripts (in demos/) get progressively more detailed: 105 | 106 | :demo_simple.py: 107 | calls invoke_shell() and emulates a terminal/tty through which you can 108 | execute commands interactively on a remote server. think of it as a 109 | poor man's ssh command-line client. 110 | 111 | :demo.py: 112 | same as demo_simple.py, but allows you to authenticiate using a 113 | private key, attempts to use an SSH-agent if present, and uses the long 114 | form of some of the API calls. 115 | 116 | :forward.py: 117 | command-line script to set up port-forwarding across an ssh transport. 118 | (requires python 2.3.) 119 | 120 | :demo_sftp.py: 121 | opens an sftp session and does a few simple file operations. 122 | 123 | :demo_server.py: 124 | an ssh server that listens on port 2200 and accepts a login for 125 | 'robey' (password 'foo'), and pretends to be a BBS. meant to be a 126 | very simple demo of writing an ssh server. 127 | 128 | :demo_keygen.py: 129 | an key generator similar to openssh ssh-keygen(1) program with 130 | paramiko keys generation and progress functions. 131 | 132 | Use 133 | --- 134 | 135 | the demo scripts are probably the best example of how to use this package. 136 | there is also a lot of documentation, generated with Sphinx autodoc, in the doc/ folder. 137 | 138 | there are also unit tests here:: 139 | 140 | $ python ./test.py 141 | 142 | which will verify that most of the core components are working correctly. 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # paramiko-sshfuzz 2 | 3 | See [README](README) for the paramiko readme. 4 | 5 | Paramiko based SSH fuzzer that hooks into paramiko.message raw message packing functionality to fuzz the ssh protocol. 6 | It is based on a decorator driven fuzzing controller. Methods that should be tracked and intercepted are registered to 7 | the fuzzing controller `paramiko.fuzz.FuzzMaster` by decorathing them with `@paramiko.fuzz.FuzzMaster.candidate`. To actually 8 | start fuzzing a `fuzzing_corpus` has to be created. This is basically a valid ssh scenario where the fuzzing controller is 9 | initialized and a few mutation methods for the decorated candidates are defined. An good corpus should try to use all the paramiko 10 | offered ssh features. 11 | Whenever the test corups calls a candidate function, control is handed to the fuzzing controller which then calls the method 12 | specific fuzzing method. This is typically the original function with some random or deterministic factor and some changes to generically inject 13 | faults. 14 | 15 | the fuzzing controller keeps track of unique call graphs in order to exhaustively fuzz every single candidate function. 16 | 17 | 18 | For a working example see demos/fuzzing_corpus.py 19 | 20 | 1. get a `paramiko.fuzz.FuzzMaster` reference 21 | 2. assign your fuzzing function to on of the `paramiko.message` fuzzpoints. 22 | In this case we're defining an `add_string` function that adds an invalid 23 | length-prefix to the raw ssh-string on a random basis. 24 | 25 | ``` 26 | def add_string(self, s): 27 | """ 28 | Add a string to the stream. 29 | 30 | :param str s: string to add 31 | """ 32 | s = common.asbytes(s) 33 | if random.choice([False]*7+[True]): 34 | self.add_int(0xffffffff) 35 | else: 36 | self.add_int(len(s)) 37 | self.packet.write(s) 38 | return self 39 | ``` 40 | 41 | ```python 42 | FuzzMaster = paramiko.fuzz.FuzzMaster 43 | FuzzMaster.MUTATION_PER_RUN=10000 44 | #FuzzMaster.add_fuzzdef("add_byte",add_byte) 45 | FuzzMaster.add_fuzzdef("add_string",add_string) 46 | FuzzMaster.add_fuzzdef("add_int",add_int) 47 | FuzzMaster.add_fuzzdef("add_boolean",add_boolean) 48 | #FuzzMaster.add_fuzzdef("add_adaptive_int",add_adaptive_int) 49 | #FuzzMaster.add_fuzzdef("add_int64",add_int64) 50 | 51 | client = paramiko.SSHClient() 52 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 53 | for i in xrange(100): 54 | try: 55 | client.connect(hostname="host", port=22, 56 | username="user", password="password") 57 | _, sout, _ = client.exec_command("whoami") 58 | print sout.read() 59 | _, sout, _ = client.exec_command("whoami") 60 | print sout.read() 61 | client.invoke_shell() 62 | client.open_sftp() 63 | transport = client.get_transport() 64 | session = transport.open_session() 65 | session.request_x11(auth_cookie="JO") 66 | session.exec_command("whoami") 67 | except paramiko.fuzz.StopFuzzing, sf: 68 | print "STOP FUZZING" 69 | 70 | break 71 | except Exception, e: 72 | print repr(e) 73 | ``` 74 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * Change license to BSD for v1.8 (obtain permission from Robey) 2 | * Pending that, remove preamble from all files, ensure LICENSE is still correct 3 | * Update version stuff: use an execfile'd paramiko/_version.py 4 | -------------------------------------------------------------------------------- /demos/demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2003-2007 Robey Pointer 4 | # 5 | # This file is part of paramiko. 6 | # 7 | # Paramiko is free software; you can redistribute it and/or modify it under the 8 | # terms of the GNU Lesser General Public License as published by the Free 9 | # Software Foundation; either version 2.1 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 15 | # details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | 22 | import base64 23 | from binascii import hexlify 24 | import getpass 25 | import os 26 | import select 27 | import socket 28 | import sys 29 | import time 30 | import traceback 31 | from paramiko.py3compat import input 32 | 33 | import paramiko 34 | try: 35 | import interactive 36 | except ImportError: 37 | from . import interactive 38 | 39 | 40 | def agent_auth(transport, username): 41 | """ 42 | Attempt to authenticate to the given transport using any of the private 43 | keys available from an SSH agent. 44 | """ 45 | 46 | agent = paramiko.Agent() 47 | agent_keys = agent.get_keys() 48 | if len(agent_keys) == 0: 49 | return 50 | 51 | for key in agent_keys: 52 | print('Trying ssh-agent key %s' % hexlify(key.get_fingerprint())) 53 | try: 54 | transport.auth_publickey(username, key) 55 | print('... success!') 56 | return 57 | except paramiko.SSHException: 58 | print('... nope.') 59 | 60 | 61 | def manual_auth(username, hostname): 62 | default_auth = 'p' 63 | auth = input('Auth by (p)assword, (r)sa key, or (d)ss key? [%s] ' % default_auth) 64 | if len(auth) == 0: 65 | auth = default_auth 66 | 67 | if auth == 'r': 68 | default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa') 69 | path = input('RSA key [%s]: ' % default_path) 70 | if len(path) == 0: 71 | path = default_path 72 | try: 73 | key = paramiko.RSAKey.from_private_key_file(path) 74 | except paramiko.PasswordRequiredException: 75 | password = getpass.getpass('RSA key password: ') 76 | key = paramiko.RSAKey.from_private_key_file(path, password) 77 | t.auth_publickey(username, key) 78 | elif auth == 'd': 79 | default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_dsa') 80 | path = input('DSS key [%s]: ' % default_path) 81 | if len(path) == 0: 82 | path = default_path 83 | try: 84 | key = paramiko.DSSKey.from_private_key_file(path) 85 | except paramiko.PasswordRequiredException: 86 | password = getpass.getpass('DSS key password: ') 87 | key = paramiko.DSSKey.from_private_key_file(path, password) 88 | t.auth_publickey(username, key) 89 | else: 90 | pw = getpass.getpass('Password for %s@%s: ' % (username, hostname)) 91 | t.auth_password(username, pw) 92 | 93 | 94 | # setup logging 95 | paramiko.util.log_to_file('demo.log') 96 | 97 | username = '' 98 | if len(sys.argv) > 1: 99 | hostname = sys.argv[1] 100 | if hostname.find('@') >= 0: 101 | username, hostname = hostname.split('@') 102 | else: 103 | hostname = input('Hostname: ') 104 | if len(hostname) == 0: 105 | print('*** Hostname required.') 106 | sys.exit(1) 107 | port = 22 108 | if hostname.find(':') >= 0: 109 | hostname, portstr = hostname.split(':') 110 | port = int(portstr) 111 | 112 | # now connect 113 | try: 114 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 115 | sock.connect((hostname, port)) 116 | except Exception as e: 117 | print('*** Connect failed: ' + str(e)) 118 | traceback.print_exc() 119 | sys.exit(1) 120 | 121 | try: 122 | t = paramiko.Transport(sock) 123 | try: 124 | t.start_client() 125 | except paramiko.SSHException: 126 | print('*** SSH negotiation failed.') 127 | sys.exit(1) 128 | 129 | try: 130 | keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts')) 131 | except IOError: 132 | try: 133 | keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts')) 134 | except IOError: 135 | print('*** Unable to open host keys file') 136 | keys = {} 137 | 138 | # check server's host key -- this is important. 139 | key = t.get_remote_server_key() 140 | if hostname not in keys: 141 | print('*** WARNING: Unknown host key!') 142 | elif key.get_name() not in keys[hostname]: 143 | print('*** WARNING: Unknown host key!') 144 | elif keys[hostname][key.get_name()] != key: 145 | print('*** WARNING: Host key has changed!!!') 146 | sys.exit(1) 147 | else: 148 | print('*** Host key OK.') 149 | 150 | # get username 151 | if username == '': 152 | default_username = getpass.getuser() 153 | username = input('Username [%s]: ' % default_username) 154 | if len(username) == 0: 155 | username = default_username 156 | 157 | agent_auth(t, username) 158 | if not t.is_authenticated(): 159 | manual_auth(username, hostname) 160 | if not t.is_authenticated(): 161 | print('*** Authentication failed. :(') 162 | t.close() 163 | sys.exit(1) 164 | 165 | chan = t.open_session() 166 | chan.get_pty() 167 | chan.invoke_shell() 168 | print('*** Here we go!\n') 169 | interactive.interactive_shell(chan) 170 | chan.close() 171 | t.close() 172 | 173 | except Exception as e: 174 | print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) 175 | traceback.print_exc() 176 | try: 177 | t.close() 178 | except: 179 | pass 180 | sys.exit(1) 181 | 182 | 183 | -------------------------------------------------------------------------------- /demos/demo_keygen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2010 Sofian Brabez 4 | # 5 | # This file is part of paramiko. 6 | # 7 | # Paramiko is free software; you can redistribute it and/or modify it under the 8 | # terms of the GNU Lesser General Public License as published by the Free 9 | # Software Foundation; either version 2.1 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 15 | # details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | import sys 22 | 23 | from binascii import hexlify 24 | from optparse import OptionParser 25 | 26 | from paramiko import DSSKey 27 | from paramiko import RSAKey 28 | from paramiko.ssh_exception import SSHException 29 | from paramiko.py3compat import u 30 | 31 | usage=""" 32 | %prog [-v] [-b bits] -t type [-N new_passphrase] [-f output_keyfile]""" 33 | 34 | default_values = { 35 | "ktype": "dsa", 36 | "bits": 1024, 37 | "filename": "output", 38 | "comment": "" 39 | } 40 | 41 | key_dispatch_table = { 42 | 'dsa': DSSKey, 43 | 'rsa': RSAKey, 44 | } 45 | 46 | def progress(arg=None): 47 | 48 | if not arg: 49 | sys.stdout.write('0%\x08\x08\x08 ') 50 | sys.stdout.flush() 51 | elif arg[0] == 'p': 52 | sys.stdout.write('25%\x08\x08\x08\x08 ') 53 | sys.stdout.flush() 54 | elif arg[0] == 'h': 55 | sys.stdout.write('50%\x08\x08\x08\x08 ') 56 | sys.stdout.flush() 57 | elif arg[0] == 'x': 58 | sys.stdout.write('75%\x08\x08\x08\x08 ') 59 | sys.stdout.flush() 60 | 61 | if __name__ == '__main__': 62 | 63 | phrase=None 64 | pfunc=None 65 | 66 | parser = OptionParser(usage=usage) 67 | parser.add_option("-t", "--type", type="string", dest="ktype", 68 | help="Specify type of key to create (dsa or rsa)", 69 | metavar="ktype", default=default_values["ktype"]) 70 | parser.add_option("-b", "--bits", type="int", dest="bits", 71 | help="Number of bits in the key to create", metavar="bits", 72 | default=default_values["bits"]) 73 | parser.add_option("-N", "--new-passphrase", dest="newphrase", 74 | help="Provide new passphrase", metavar="phrase") 75 | parser.add_option("-P", "--old-passphrase", dest="oldphrase", 76 | help="Provide old passphrase", metavar="phrase") 77 | parser.add_option("-f", "--filename", type="string", dest="filename", 78 | help="Filename of the key file", metavar="filename", 79 | default=default_values["filename"]) 80 | parser.add_option("-q", "--quiet", default=False, action="store_false", 81 | help="Quiet") 82 | parser.add_option("-v", "--verbose", default=False, action="store_true", 83 | help="Verbose") 84 | parser.add_option("-C", "--comment", type="string", dest="comment", 85 | help="Provide a new comment", metavar="comment", 86 | default=default_values["comment"]) 87 | 88 | (options, args) = parser.parse_args() 89 | 90 | if len(sys.argv) == 1: 91 | parser.print_help() 92 | sys.exit(0) 93 | 94 | for o in list(default_values.keys()): 95 | globals()[o] = getattr(options, o, default_values[o.lower()]) 96 | 97 | if options.newphrase: 98 | phrase = getattr(options, 'newphrase') 99 | 100 | if options.verbose: 101 | pfunc = progress 102 | sys.stdout.write("Generating priv/pub %s %d bits key pair (%s/%s.pub)..." % (ktype, bits, filename, filename)) 103 | sys.stdout.flush() 104 | 105 | if ktype == 'dsa' and bits > 1024: 106 | raise SSHException("DSA Keys must be 1024 bits") 107 | 108 | if ktype not in key_dispatch_table: 109 | raise SSHException("Unknown %s algorithm to generate keys pair" % ktype) 110 | 111 | # generating private key 112 | prv = key_dispatch_table[ktype].generate(bits=bits, progress_func=pfunc) 113 | prv.write_private_key_file(filename, password=phrase) 114 | 115 | # generating public key 116 | pub = key_dispatch_table[ktype](filename=filename, password=phrase) 117 | with open("%s.pub" % filename, 'w') as f: 118 | f.write("%s %s" % (pub.get_name(), pub.get_base64())) 119 | if options.comment: 120 | f.write(" %s" % comment) 121 | 122 | if options.verbose: 123 | print("done.") 124 | 125 | hash = u(hexlify(pub.get_fingerprint())) 126 | print("Fingerprint: %d %s %s.pub (%s)" % (bits, ":".join([ hash[i:2+i] for i in range(0, len(hash), 2)]), filename, ktype.upper())) 127 | -------------------------------------------------------------------------------- /demos/demo_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2003-2007 Robey Pointer 4 | # 5 | # This file is part of paramiko. 6 | # 7 | # Paramiko is free software; you can redistribute it and/or modify it under the 8 | # terms of the GNU Lesser General Public License as published by the Free 9 | # Software Foundation; either version 2.1 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 15 | # details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | import base64 22 | from binascii import hexlify 23 | import os 24 | import socket 25 | import sys 26 | import threading 27 | import traceback 28 | 29 | import paramiko 30 | from paramiko.py3compat import b, u, decodebytes 31 | 32 | 33 | # setup logging 34 | paramiko.util.log_to_file('demo_server.log') 35 | 36 | host_key = paramiko.RSAKey(filename='test_rsa.key') 37 | #host_key = paramiko.DSSKey(filename='test_dss.key') 38 | 39 | print('Read key: ' + u(hexlify(host_key.get_fingerprint()))) 40 | 41 | 42 | class Server (paramiko.ServerInterface): 43 | # 'data' is the output of base64.encodestring(str(key)) 44 | # (using the "user_rsa_key" files) 45 | data = (b'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp' 46 | b'fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC' 47 | b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT' 48 | b'UWT10hcuO4Ks8=') 49 | good_pub_key = paramiko.RSAKey(data=decodebytes(data)) 50 | 51 | def __init__(self): 52 | self.event = threading.Event() 53 | 54 | def check_channel_request(self, kind, chanid): 55 | if kind == 'session': 56 | return paramiko.OPEN_SUCCEEDED 57 | return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED 58 | 59 | def check_auth_password(self, username, password): 60 | if (username == 'robey') and (password == 'foo'): 61 | return paramiko.AUTH_SUCCESSFUL 62 | return paramiko.AUTH_FAILED 63 | 64 | def check_auth_publickey(self, username, key): 65 | print('Auth attempt with key: ' + u(hexlify(key.get_fingerprint()))) 66 | if (username == 'robey') and (key == self.good_pub_key): 67 | return paramiko.AUTH_SUCCESSFUL 68 | return paramiko.AUTH_FAILED 69 | 70 | def check_auth_gssapi_with_mic(self, username, 71 | gss_authenticated=paramiko.AUTH_FAILED, 72 | cc_file=None): 73 | """ 74 | .. note:: 75 | We are just checking in `AuthHandler` that the given user is a 76 | valid krb5 principal! We don't check if the krb5 principal is 77 | allowed to log in on the server, because there is no way to do that 78 | in python. So if you develop your own SSH server with paramiko for 79 | a certain platform like Linux, you should call ``krb5_kuserok()`` in 80 | your local kerberos library to make sure that the krb5_principal 81 | has an account on the server and is allowed to log in as a user. 82 | 83 | .. seealso:: 84 | `krb5_kuserok() man page 85 | `_ 86 | """ 87 | if gss_authenticated == paramiko.AUTH_SUCCESSFUL: 88 | return paramiko.AUTH_SUCCESSFUL 89 | return paramiko.AUTH_FAILED 90 | 91 | def check_auth_gssapi_keyex(self, username, 92 | gss_authenticated=paramiko.AUTH_FAILED, 93 | cc_file=None): 94 | if gss_authenticated == paramiko.AUTH_SUCCESSFUL: 95 | return paramiko.AUTH_SUCCESSFUL 96 | return paramiko.AUTH_FAILED 97 | 98 | def enable_auth_gssapi(self): 99 | UseGSSAPI = True 100 | GSSAPICleanupCredentials = False 101 | return UseGSSAPI 102 | 103 | def get_allowed_auths(self, username): 104 | return 'gssapi-keyex,gssapi-with-mic,password,publickey' 105 | 106 | def check_channel_shell_request(self, channel): 107 | self.event.set() 108 | return True 109 | 110 | def check_channel_pty_request(self, channel, term, width, height, pixelwidth, 111 | pixelheight, modes): 112 | return True 113 | 114 | 115 | DoGSSAPIKeyExchange = True 116 | 117 | # now connect 118 | try: 119 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 120 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 121 | sock.bind(('', 2200)) 122 | except Exception as e: 123 | print('*** Bind failed: ' + str(e)) 124 | traceback.print_exc() 125 | sys.exit(1) 126 | 127 | try: 128 | sock.listen(100) 129 | print('Listening for connection ...') 130 | client, addr = sock.accept() 131 | except Exception as e: 132 | print('*** Listen/accept failed: ' + str(e)) 133 | traceback.print_exc() 134 | sys.exit(1) 135 | 136 | print('Got a connection!') 137 | 138 | try: 139 | t = paramiko.Transport(client, gss_kex=DoGSSAPIKeyExchange) 140 | t.set_gss_host(socket.getfqdn("")) 141 | try: 142 | t.load_server_moduli() 143 | except: 144 | print('(Failed to load moduli -- gex will be unsupported.)') 145 | raise 146 | t.add_server_key(host_key) 147 | server = Server() 148 | try: 149 | t.start_server(server=server) 150 | except paramiko.SSHException: 151 | print('*** SSH negotiation failed.') 152 | sys.exit(1) 153 | 154 | # wait for auth 155 | chan = t.accept(20) 156 | if chan is None: 157 | print('*** No channel.') 158 | sys.exit(1) 159 | print('Authenticated!') 160 | 161 | server.event.wait(10) 162 | if not server.event.is_set(): 163 | print('*** Client never asked for a shell.') 164 | sys.exit(1) 165 | 166 | chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n') 167 | chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n') 168 | chan.send('Happy birthday to Robot Dave!\r\n\r\n') 169 | chan.send('Username: ') 170 | f = chan.makefile('rU') 171 | username = f.readline().strip('\r\n') 172 | chan.send('\r\nI don\'t like you, ' + username + '.\r\n') 173 | chan.close() 174 | 175 | except Exception as e: 176 | print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e)) 177 | traceback.print_exc() 178 | try: 179 | t.close() 180 | except: 181 | pass 182 | sys.exit(1) 183 | 184 | -------------------------------------------------------------------------------- /demos/demo_sftp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2003-2007 Robey Pointer 4 | # 5 | # This file is part of paramiko. 6 | # 7 | # Paramiko is free software; you can redistribute it and/or modify it under the 8 | # terms of the GNU Lesser General Public License as published by the Free 9 | # Software Foundation; either version 2.1 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 15 | # details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | # based on code provided by raymond mosteller (thanks!) 22 | 23 | import base64 24 | import getpass 25 | import os 26 | import socket 27 | import sys 28 | import traceback 29 | 30 | import paramiko 31 | from paramiko.py3compat import input 32 | 33 | 34 | # setup logging 35 | paramiko.util.log_to_file('demo_sftp.log') 36 | 37 | # Paramiko client configuration 38 | UseGSSAPI = True # enable GSS-API / SSPI authentication 39 | DoGSSAPIKeyExchange = True 40 | Port = 22 41 | 42 | # get hostname 43 | username = '' 44 | if len(sys.argv) > 1: 45 | hostname = sys.argv[1] 46 | if hostname.find('@') >= 0: 47 | username, hostname = hostname.split('@') 48 | else: 49 | hostname = input('Hostname: ') 50 | if len(hostname) == 0: 51 | print('*** Hostname required.') 52 | sys.exit(1) 53 | 54 | if hostname.find(':') >= 0: 55 | hostname, portstr = hostname.split(':') 56 | Port = int(portstr) 57 | 58 | 59 | # get username 60 | if username == '': 61 | default_username = getpass.getuser() 62 | username = input('Username [%s]: ' % default_username) 63 | if len(username) == 0: 64 | username = default_username 65 | if not UseGSSAPI: 66 | password = getpass.getpass('Password for %s@%s: ' % (username, hostname)) 67 | else: 68 | password = None 69 | 70 | 71 | # get host key, if we know one 72 | hostkeytype = None 73 | hostkey = None 74 | try: 75 | host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts')) 76 | except IOError: 77 | try: 78 | # try ~/ssh/ too, because windows can't have a folder named ~/.ssh/ 79 | host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts')) 80 | except IOError: 81 | print('*** Unable to open host keys file') 82 | host_keys = {} 83 | 84 | if hostname in host_keys: 85 | hostkeytype = host_keys[hostname].keys()[0] 86 | hostkey = host_keys[hostname][hostkeytype] 87 | print('Using host key of type %s' % hostkeytype) 88 | 89 | 90 | # now, connect and use paramiko Transport to negotiate SSH2 across the connection 91 | try: 92 | t = paramiko.Transport((hostname, Port)) 93 | t.connect(hostkey, username, password, gss_host=socket.getfqdn(hostname), 94 | gss_auth=UseGSSAPI, gss_kex=DoGSSAPIKeyExchange) 95 | sftp = paramiko.SFTPClient.from_transport(t) 96 | 97 | # dirlist on remote host 98 | dirlist = sftp.listdir('.') 99 | print("Dirlist: %s" % dirlist) 100 | 101 | # copy this demo onto the server 102 | try: 103 | sftp.mkdir("demo_sftp_folder") 104 | except IOError: 105 | print('(assuming demo_sftp_folder/ already exists)') 106 | with sftp.open('demo_sftp_folder/README', 'w') as f: 107 | f.write('This was created by demo_sftp.py.\n') 108 | with open('demo_sftp.py', 'r') as f: 109 | data = f.read() 110 | sftp.open('demo_sftp_folder/demo_sftp.py', 'w').write(data) 111 | print('created demo_sftp_folder/ on the server') 112 | 113 | # copy the README back here 114 | with sftp.open('demo_sftp_folder/README', 'r') as f: 115 | data = f.read() 116 | with open('README_demo_sftp', 'w') as f: 117 | f.write(data) 118 | print('copied README back here') 119 | 120 | # BETTER: use the get() and put() methods 121 | sftp.put('demo_sftp.py', 'demo_sftp_folder/demo_sftp.py') 122 | sftp.get('demo_sftp_folder/README', 'README_demo_sftp') 123 | 124 | t.close() 125 | 126 | except Exception as e: 127 | print('*** Caught exception: %s: %s' % (e.__class__, e)) 128 | traceback.print_exc() 129 | try: 130 | t.close() 131 | except: 132 | pass 133 | sys.exit(1) 134 | -------------------------------------------------------------------------------- /demos/demo_simple.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2003-2007 Robey Pointer 4 | # 5 | # This file is part of paramiko. 6 | # 7 | # Paramiko is free software; you can redistribute it and/or modify it under the 8 | # terms of the GNU Lesser General Public License as published by the Free 9 | # Software Foundation; either version 2.1 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 15 | # details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | 22 | import base64 23 | import getpass 24 | import os 25 | import socket 26 | import sys 27 | import traceback 28 | from paramiko.py3compat import input 29 | 30 | import paramiko 31 | try: 32 | import interactive 33 | except ImportError: 34 | from . import interactive 35 | 36 | 37 | # setup logging 38 | paramiko.util.log_to_file('demo_simple.log') 39 | # Paramiko client configuration 40 | UseGSSAPI = True # enable GSS-API / SSPI authentication 41 | DoGSSAPIKeyExchange = True 42 | port = 22 43 | 44 | # get hostname 45 | username = '' 46 | if len(sys.argv) > 1: 47 | hostname = sys.argv[1] 48 | if hostname.find('@') >= 0: 49 | username, hostname = hostname.split('@') 50 | else: 51 | hostname = input('Hostname: ') 52 | if len(hostname) == 0: 53 | print('*** Hostname required.') 54 | sys.exit(1) 55 | 56 | if hostname.find(':') >= 0: 57 | hostname, portstr = hostname.split(':') 58 | port = int(portstr) 59 | 60 | 61 | # get username 62 | if username == '': 63 | default_username = getpass.getuser() 64 | username = input('Username [%s]: ' % default_username) 65 | if len(username) == 0: 66 | username = default_username 67 | if not UseGSSAPI or (not UseGSSAPI and not DoGSSAPIKeyExchange): 68 | password = getpass.getpass('Password for %s@%s: ' % (username, hostname)) 69 | 70 | 71 | # now, connect and use paramiko Client to negotiate SSH2 across the connection 72 | try: 73 | client = paramiko.SSHClient() 74 | client.load_system_host_keys() 75 | client.set_missing_host_key_policy(paramiko.WarningPolicy()) 76 | print('*** Connecting...') 77 | if not UseGSSAPI or (not UseGSSAPI and not DoGSSAPIKeyExchange): 78 | client.connect(hostname, port, username, password) 79 | else: 80 | # SSPI works only with the FQDN of the target host 81 | hostname = socket.getfqdn(hostname) 82 | try: 83 | client.connect(hostname, port, username, gss_auth=UseGSSAPI, 84 | gss_kex=DoGSSAPIKeyExchange) 85 | except Exception: 86 | password = getpass.getpass('Password for %s@%s: ' % (username, hostname)) 87 | client.connect(hostname, port, username, password) 88 | 89 | chan = client.invoke_shell() 90 | print(repr(client.get_transport())) 91 | print('*** Here we go!\n') 92 | interactive.interactive_shell(chan) 93 | chan.close() 94 | client.close() 95 | 96 | except Exception as e: 97 | print('*** Caught exception: %s: %s' % (e.__class__, e)) 98 | traceback.print_exc() 99 | try: 100 | client.close() 101 | except: 102 | pass 103 | sys.exit(1) 104 | -------------------------------------------------------------------------------- /demos/fuzzing_corpus.py: -------------------------------------------------------------------------------- 1 | import logging 2 | logging.basicConfig(loglevel=logging.DEBUG) 3 | logging.getLogger("paramiko.fuzz").setLevel(logging.INFO) 4 | 5 | import paramiko.fuzz 6 | import paramiko 7 | from paramiko import util, common 8 | from paramiko import Message 9 | import struct 10 | zero_byte = '\x00' 11 | one_byte = '\x01' 12 | max_byte = '\xff' 13 | 14 | import random 15 | 16 | def add_bytes(self, b): 17 | """ 18 | Write bytes to the stream, without any formatting. 19 | 20 | :param str b: bytes to add 21 | """ 22 | self.packet.write(b) 23 | return self 24 | 25 | def add_byte(self, b): 26 | """ 27 | Write a single byte to the stream, without any formatting. 28 | 29 | :param str b: byte to add 30 | """ 31 | self.packet.write(b) 32 | return self 33 | 34 | def add_boolean(self, b): 35 | """ 36 | Add a boolean value to the stream. 37 | 38 | :param bool b: boolean value to add 39 | """ 40 | if random.choice([False]*7+[True]): 41 | b = random.choice(xrange(2,0xff)) 42 | self.packet.write(str(b)) 43 | else: 44 | if b: 45 | self.packet.write(one_byte) 46 | else: 47 | self.packet.write(zero_byte) 48 | return self 49 | 50 | def add_int(self, n): 51 | """ 52 | Add an integer to the stream. 53 | 54 | :param int n: integer to add 55 | """ 56 | if random.choice([False]*7+[True]): 57 | n = random.choice([0,0xffffffff,0xffffffff/2]) 58 | 59 | self.packet.write(struct.pack('>I', n)) 60 | return self 61 | 62 | def add_adaptive_int(self, n): 63 | """ 64 | Add an integer to the stream. 65 | 66 | :param int n: integer to add 67 | """ 68 | if n >= Message.big_int: 69 | self.packet.write(max_byte) 70 | self.add_string(util.deflate_long(n)) 71 | else: 72 | self.packet.write(struct.pack('>I', n)) 73 | return self 74 | 75 | def add_int64(self, n): 76 | """ 77 | Add a 64-bit int to the stream. 78 | 79 | :param long n: long int to add 80 | """ 81 | self.packet.write(struct.pack('>Q', n)) 82 | return self 83 | 84 | def add_string(self, s): 85 | """ 86 | Add a string to the stream. 87 | 88 | :param str s: string to add 89 | """ 90 | s = common.asbytes(s) 91 | if random.choice([False]*7+[True]): 92 | self.add_int(0xffffffff) 93 | else: 94 | self.add_int(len(s)) 95 | self.packet.write(s) 96 | return self 97 | 98 | FuzzMaster = paramiko.fuzz.FuzzMaster 99 | FuzzMaster.MUTATION_PER_RUN=10000 100 | #FuzzMaster.add_fuzzdef("add_byte",add_byte) 101 | FuzzMaster.add_fuzzdef("add_string",add_string) 102 | FuzzMaster.add_fuzzdef("add_int",add_int) 103 | FuzzMaster.add_fuzzdef("add_boolean",add_boolean) 104 | #FuzzMaster.add_fuzzdef("add_adaptive_int",add_adaptive_int) 105 | #FuzzMaster.add_fuzzdef("add_int64",add_int64) 106 | 107 | client = paramiko.SSHClient() 108 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 109 | for i in xrange(100): 110 | try: 111 | client.connect(hostname="host", port=22, 112 | username="user", password="password") 113 | _, sout, _ = client.exec_command("whoami") 114 | print sout.read() 115 | _, sout, _ = client.exec_command("whoami") 116 | print sout.read() 117 | client.invoke_shell() 118 | client.open_sftp() 119 | transport = client.get_transport() 120 | session = transport.open_session() 121 | session.request_x11(auth_cookie="JO") 122 | session.exec_command("whoami") 123 | except paramiko.fuzz.StopFuzzing, sf: 124 | print "STOP FUZZING" 125 | 126 | break 127 | except Exception, e: 128 | print repr(e) -------------------------------------------------------------------------------- /demos/interactive.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | 20 | import socket 21 | import sys 22 | from paramiko.py3compat import u 23 | 24 | # windows does not have termios... 25 | try: 26 | import termios 27 | import tty 28 | has_termios = True 29 | except ImportError: 30 | has_termios = False 31 | 32 | 33 | def interactive_shell(chan): 34 | if has_termios: 35 | posix_shell(chan) 36 | else: 37 | windows_shell(chan) 38 | 39 | 40 | def posix_shell(chan): 41 | import select 42 | 43 | oldtty = termios.tcgetattr(sys.stdin) 44 | try: 45 | tty.setraw(sys.stdin.fileno()) 46 | tty.setcbreak(sys.stdin.fileno()) 47 | chan.settimeout(0.0) 48 | 49 | while True: 50 | r, w, e = select.select([chan, sys.stdin], [], []) 51 | if chan in r: 52 | try: 53 | x = u(chan.recv(1024)) 54 | if len(x) == 0: 55 | sys.stdout.write('\r\n*** EOF\r\n') 56 | break 57 | sys.stdout.write(x) 58 | sys.stdout.flush() 59 | except socket.timeout: 60 | pass 61 | if sys.stdin in r: 62 | x = sys.stdin.read(1) 63 | if len(x) == 0: 64 | break 65 | chan.send(x) 66 | 67 | finally: 68 | termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty) 69 | 70 | 71 | # thanks to Mike Looijmans for this code 72 | def windows_shell(chan): 73 | import threading 74 | 75 | sys.stdout.write("Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n") 76 | 77 | def writeall(sock): 78 | while True: 79 | data = sock.recv(256) 80 | if not data: 81 | sys.stdout.write('\r\n*** EOF ***\r\n\r\n') 82 | sys.stdout.flush() 83 | break 84 | sys.stdout.write(data) 85 | sys.stdout.flush() 86 | 87 | writer = threading.Thread(target=writeall, args=(chan,)) 88 | writer.start() 89 | 90 | try: 91 | while True: 92 | d = sys.stdin.read(1) 93 | if not d: 94 | break 95 | chan.send(d) 96 | except EOFError: 97 | # user hit ^Z or F6 98 | pass 99 | -------------------------------------------------------------------------------- /demos/rforward.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (C) 2008 Robey Pointer 4 | # 5 | # This file is part of paramiko. 6 | # 7 | # Paramiko is free software; you can redistribute it and/or modify it under the 8 | # terms of the GNU Lesser General Public License as published by the Free 9 | # Software Foundation; either version 2.1 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 15 | # details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | """ 22 | Sample script showing how to do remote port forwarding over paramiko. 23 | 24 | This script connects to the requested SSH server and sets up remote port 25 | forwarding (the openssh -R option) from a remote port through a tunneled 26 | connection to a destination reachable from the local machine. 27 | """ 28 | 29 | import getpass 30 | import os 31 | import socket 32 | import select 33 | import sys 34 | import threading 35 | from optparse import OptionParser 36 | 37 | import paramiko 38 | 39 | SSH_PORT = 22 40 | DEFAULT_PORT = 4000 41 | 42 | g_verbose = True 43 | 44 | 45 | def handler(chan, host, port): 46 | sock = socket.socket() 47 | try: 48 | sock.connect((host, port)) 49 | except Exception as e: 50 | verbose('Forwarding request to %s:%d failed: %r' % (host, port, e)) 51 | return 52 | 53 | verbose('Connected! Tunnel open %r -> %r -> %r' % (chan.origin_addr, 54 | chan.getpeername(), (host, port))) 55 | while True: 56 | r, w, x = select.select([sock, chan], [], []) 57 | if sock in r: 58 | data = sock.recv(1024) 59 | if len(data) == 0: 60 | break 61 | chan.send(data) 62 | if chan in r: 63 | data = chan.recv(1024) 64 | if len(data) == 0: 65 | break 66 | sock.send(data) 67 | chan.close() 68 | sock.close() 69 | verbose('Tunnel closed from %r' % (chan.origin_addr,)) 70 | 71 | 72 | def reverse_forward_tunnel(server_port, remote_host, remote_port, transport): 73 | transport.request_port_forward('', server_port) 74 | while True: 75 | chan = transport.accept(1000) 76 | if chan is None: 77 | continue 78 | thr = threading.Thread(target=handler, args=(chan, remote_host, remote_port)) 79 | thr.setDaemon(True) 80 | thr.start() 81 | 82 | 83 | def verbose(s): 84 | if g_verbose: 85 | print(s) 86 | 87 | 88 | HELP = """\ 89 | Set up a reverse forwarding tunnel across an SSH server, using paramiko. A 90 | port on the SSH server (given with -p) is forwarded across an SSH session 91 | back to the local machine, and out to a remote site reachable from this 92 | network. This is similar to the openssh -R option. 93 | """ 94 | 95 | 96 | def get_host_port(spec, default_port): 97 | "parse 'hostname:22' into a host and port, with the port optional" 98 | args = (spec.split(':', 1) + [default_port])[:2] 99 | args[1] = int(args[1]) 100 | return args[0], args[1] 101 | 102 | 103 | def parse_options(): 104 | global g_verbose 105 | 106 | parser = OptionParser(usage='usage: %prog [options] [:]', 107 | version='%prog 1.0', description=HELP) 108 | parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True, 109 | help='squelch all informational output') 110 | parser.add_option('-p', '--remote-port', action='store', type='int', dest='port', 111 | default=DEFAULT_PORT, 112 | help='port on server to forward (default: %d)' % DEFAULT_PORT) 113 | parser.add_option('-u', '--user', action='store', type='string', dest='user', 114 | default=getpass.getuser(), 115 | help='username for SSH authentication (default: %s)' % getpass.getuser()) 116 | parser.add_option('-K', '--key', action='store', type='string', dest='keyfile', 117 | default=None, 118 | help='private key file to use for SSH authentication') 119 | parser.add_option('', '--no-key', action='store_false', dest='look_for_keys', default=True, 120 | help='don\'t look for or use a private key file') 121 | parser.add_option('-P', '--password', action='store_true', dest='readpass', default=False, 122 | help='read password (for key or password auth) from stdin') 123 | parser.add_option('-r', '--remote', action='store', type='string', dest='remote', default=None, metavar='host:port', 124 | help='remote host and port to forward to') 125 | options, args = parser.parse_args() 126 | 127 | if len(args) != 1: 128 | parser.error('Incorrect number of arguments.') 129 | if options.remote is None: 130 | parser.error('Remote address required (-r).') 131 | 132 | g_verbose = options.verbose 133 | server_host, server_port = get_host_port(args[0], SSH_PORT) 134 | remote_host, remote_port = get_host_port(options.remote, SSH_PORT) 135 | return options, (server_host, server_port), (remote_host, remote_port) 136 | 137 | 138 | def main(): 139 | options, server, remote = parse_options() 140 | 141 | password = None 142 | if options.readpass: 143 | password = getpass.getpass('Enter SSH password: ') 144 | 145 | client = paramiko.SSHClient() 146 | client.load_system_host_keys() 147 | client.set_missing_host_key_policy(paramiko.WarningPolicy()) 148 | 149 | verbose('Connecting to ssh host %s:%d ...' % (server[0], server[1])) 150 | try: 151 | client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile, 152 | look_for_keys=options.look_for_keys, password=password) 153 | except Exception as e: 154 | print('*** Failed to connect to %s:%d: %r' % (server[0], server[1], e)) 155 | sys.exit(1) 156 | 157 | verbose('Now forwarding remote port %d to %s:%d ...' % (options.port, remote[0], remote[1])) 158 | 159 | try: 160 | reverse_forward_tunnel(options.port, remote[0], remote[1], client.get_transport()) 161 | except KeyboardInterrupt: 162 | print('C-c: Port forwarding stopped.') 163 | sys.exit(0) 164 | 165 | 166 | if __name__ == '__main__': 167 | main() 168 | -------------------------------------------------------------------------------- /demos/test_rsa.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz 3 | oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/ 4 | d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB 5 | gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0 6 | EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon 7 | soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H 8 | tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU 9 | avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA 10 | 4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g 11 | H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv 12 | qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV 13 | HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc 14 | nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /demos/user_rsa_key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXQIBAAKBgQDI7iK3d8eWYZlYloat94c5VjtFY7c/0zuGl8C7uMnZ3t6i2G99 3 | 66hEW0nCFSZkOW5F0XKEVj+EUCHvo8koYC6wiohAqWQnEwIoOoh7GSAcB8gP/qaq 4 | +adIl/Rvlby/mHakj+y05LBND6nFWHAn1y1gOFFKUXSJNRZPXSFy47gqzwIBIwKB 5 | gQCbANjz7q/pCXZLp1Hz6tYHqOvlEmjK1iabB1oqafrMpJ0eibUX/u+FMHq6StR5 6 | M5413BaDWHokPdEJUnabfWXXR3SMlBUKrck0eAer1O8m78yxu3OEdpRk+znVo4DL 7 | guMeCdJB/qcF0kEsx+Q8HP42MZU1oCmk3PbfXNFwaHbWuwJBAOQ/ry/hLD7AqB8x 8 | DmCM82A9E59ICNNlHOhxpJoh6nrNTPCsBAEu/SmqrL8mS6gmbRKUaya5Lx1pkxj2 9 | s/kWOokCQQDhXCcYXjjWiIfxhl6Rlgkk1vmI0l6785XSJNv4P7pXjGmShXfIzroh 10 | S8uWK3tL0GELY7+UAKDTUEVjjQdGxYSXAkEA3bo1JzKCwJ3lJZ1ebGuqmADRO6UP 11 | 40xH977aadfN1mEI6cusHmgpISl0nG5YH7BMsvaT+bs1FUH8m+hXDzoqOwJBAK3Z 12 | X/za+KV/REya2z0b+GzgWhkXUGUa/owrEBdHGriQ47osclkUgPUdNqcLmaDilAF4 13 | 1Z4PHPrI5RJIONAx+JECQQC/fChqjBgFpk6iJ+BOdSexQpgfxH/u/457W10Y43HR 14 | soS+8btbHqjQkowQ/2NTlUfWvqIlfxs6ZbFsIp/HrhZL 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /demos/user_rsa_key.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hpfAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMCKDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iTUWT10hcuO4Ks8= robey@ralph.lag.net 2 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | # Older junk 2 | tox>=1.4,<1.5 3 | # For newer tasks like building Sphinx docs. 4 | invoke>=0.11.1 5 | invocations>=0.11.0 6 | sphinx>=1.1.3 7 | alabaster>=0.7.5 8 | releases>=1.0.0 9 | semantic_version>=2.4,<2.5 10 | wheel==0.24 11 | twine==1.5 12 | -------------------------------------------------------------------------------- /images/paramiko-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintinweb/paramiko-sshfuzz/ebc5715a4d61b05964b3ddecbe37a3996fb3f84f/images/paramiko-banner.png -------------------------------------------------------------------------------- /images/paramiko-banner.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintinweb/paramiko-sshfuzz/ebc5715a4d61b05964b3ddecbe37a3996fb3f84f/images/paramiko-banner.psd -------------------------------------------------------------------------------- /images/paramiko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintinweb/paramiko-sshfuzz/ebc5715a4d61b05964b3ddecbe37a3996fb3f84f/images/paramiko.png -------------------------------------------------------------------------------- /paramiko/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2011 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | import sys 20 | from paramiko._version import __version__, __version_info__ 21 | 22 | if sys.version_info < (2, 6): 23 | raise RuntimeError('You need Python 2.6+ for this module.') 24 | 25 | 26 | __author__ = "Jeff Forcier " 27 | __license__ = "GNU Lesser General Public License (LGPL)" 28 | 29 | 30 | from paramiko.transport import SecurityOptions, Transport 31 | from paramiko.client import SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy, WarningPolicy 32 | from paramiko.auth_handler import AuthHandler 33 | from paramiko.ssh_gss import GSSAuth, GSS_AUTH_AVAILABLE 34 | from paramiko.channel import Channel, ChannelFile 35 | from paramiko.ssh_exception import SSHException, PasswordRequiredException, \ 36 | BadAuthenticationType, ChannelException, BadHostKeyException, \ 37 | AuthenticationException, ProxyCommandFailure 38 | from paramiko.server import ServerInterface, SubsystemHandler, InteractiveQuery 39 | from paramiko.rsakey import RSAKey 40 | from paramiko.dsskey import DSSKey 41 | from paramiko.ecdsakey import ECDSAKey 42 | from paramiko.sftp import SFTPError, BaseSFTP 43 | from paramiko.sftp_client import SFTP, SFTPClient 44 | from paramiko.sftp_server import SFTPServer 45 | from paramiko.sftp_attr import SFTPAttributes 46 | from paramiko.sftp_handle import SFTPHandle 47 | from paramiko.sftp_si import SFTPServerInterface 48 | from paramiko.sftp_file import SFTPFile 49 | from paramiko.message import Message 50 | from paramiko.packet import Packetizer 51 | from paramiko.file import BufferedFile 52 | from paramiko.agent import Agent, AgentKey 53 | from paramiko.pkey import PKey 54 | from paramiko.hostkeys import HostKeys 55 | from paramiko.config import SSHConfig 56 | from paramiko.proxy import ProxyCommand 57 | 58 | from paramiko.common import AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, \ 59 | OPEN_SUCCEEDED, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED, \ 60 | OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE 61 | 62 | from paramiko.sftp import SFTP_OK, SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE, \ 63 | SFTP_BAD_MESSAGE, SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, SFTP_OP_UNSUPPORTED 64 | 65 | from paramiko.common import io_sleep 66 | 67 | __all__ = [ 'Transport', 68 | 'SSHClient', 69 | 'MissingHostKeyPolicy', 70 | 'AutoAddPolicy', 71 | 'RejectPolicy', 72 | 'WarningPolicy', 73 | 'SecurityOptions', 74 | 'SubsystemHandler', 75 | 'Channel', 76 | 'PKey', 77 | 'RSAKey', 78 | 'DSSKey', 79 | 'Message', 80 | 'SSHException', 81 | 'AuthenticationException', 82 | 'PasswordRequiredException', 83 | 'BadAuthenticationType', 84 | 'ChannelException', 85 | 'BadHostKeyException', 86 | 'ProxyCommand', 87 | 'ProxyCommandFailure', 88 | 'SFTP', 89 | 'SFTPFile', 90 | 'SFTPHandle', 91 | 'SFTPClient', 92 | 'SFTPServer', 93 | 'SFTPError', 94 | 'SFTPAttributes', 95 | 'SFTPServerInterface', 96 | 'ServerInterface', 97 | 'BufferedFile', 98 | 'Agent', 99 | 'AgentKey', 100 | 'HostKeys', 101 | 'SSHConfig', 102 | 'util', 103 | 'io_sleep' ] 104 | -------------------------------------------------------------------------------- /paramiko/_version.py: -------------------------------------------------------------------------------- 1 | __version_info__ = (1, 16, 0) 2 | __version__ = '.'.join(map(str, __version_info__)) 3 | -------------------------------------------------------------------------------- /paramiko/ber.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | from paramiko.common import max_byte, zero_byte 19 | from paramiko.py3compat import b, byte_ord, byte_chr, long 20 | 21 | import paramiko.util as util 22 | 23 | 24 | class BERException (Exception): 25 | pass 26 | 27 | 28 | class BER(object): 29 | """ 30 | Robey's tiny little attempt at a BER decoder. 31 | """ 32 | 33 | def __init__(self, content=bytes()): 34 | self.content = b(content) 35 | self.idx = 0 36 | 37 | def asbytes(self): 38 | return self.content 39 | 40 | def __str__(self): 41 | return self.asbytes() 42 | 43 | def __repr__(self): 44 | return 'BER(\'' + repr(self.content) + '\')' 45 | 46 | def decode(self): 47 | return self.decode_next() 48 | 49 | def decode_next(self): 50 | if self.idx >= len(self.content): 51 | return None 52 | ident = byte_ord(self.content[self.idx]) 53 | self.idx += 1 54 | if (ident & 31) == 31: 55 | # identifier > 30 56 | ident = 0 57 | while self.idx < len(self.content): 58 | t = byte_ord(self.content[self.idx]) 59 | self.idx += 1 60 | ident = (ident << 7) | (t & 0x7f) 61 | if not (t & 0x80): 62 | break 63 | if self.idx >= len(self.content): 64 | return None 65 | # now fetch length 66 | size = byte_ord(self.content[self.idx]) 67 | self.idx += 1 68 | if size & 0x80: 69 | # more complimicated... 70 | # FIXME: theoretically should handle indefinite-length (0x80) 71 | t = size & 0x7f 72 | if self.idx + t > len(self.content): 73 | return None 74 | size = util.inflate_long(self.content[self.idx: self.idx + t], True) 75 | self.idx += t 76 | if self.idx + size > len(self.content): 77 | # can't fit 78 | return None 79 | data = self.content[self.idx: self.idx + size] 80 | self.idx += size 81 | # now switch on id 82 | if ident == 0x30: 83 | # sequence 84 | return self.decode_sequence(data) 85 | elif ident == 2: 86 | # int 87 | return util.inflate_long(data) 88 | else: 89 | # 1: boolean (00 false, otherwise true) 90 | raise BERException('Unknown ber encoding type %d (robey is lazy)' % ident) 91 | 92 | @staticmethod 93 | def decode_sequence(data): 94 | out = [] 95 | ber = BER(data) 96 | while True: 97 | x = ber.decode_next() 98 | if x is None: 99 | break 100 | out.append(x) 101 | return out 102 | 103 | def encode_tlv(self, ident, val): 104 | # no need to support ident > 31 here 105 | self.content += byte_chr(ident) 106 | if len(val) > 0x7f: 107 | lenstr = util.deflate_long(len(val)) 108 | self.content += byte_chr(0x80 + len(lenstr)) + lenstr 109 | else: 110 | self.content += byte_chr(len(val)) 111 | self.content += val 112 | 113 | def encode(self, x): 114 | if type(x) is bool: 115 | if x: 116 | self.encode_tlv(1, max_byte) 117 | else: 118 | self.encode_tlv(1, zero_byte) 119 | elif (type(x) is int) or (type(x) is long): 120 | self.encode_tlv(2, util.deflate_long(x)) 121 | elif type(x) is str: 122 | self.encode_tlv(4, x) 123 | elif (type(x) is list) or (type(x) is tuple): 124 | self.encode_tlv(0x30, self.encode_sequence(x)) 125 | else: 126 | raise BERException('Unknown type for encoding: %s' % repr(type(x))) 127 | 128 | @staticmethod 129 | def encode_sequence(data): 130 | ber = BER() 131 | for item in data: 132 | ber.encode(item) 133 | return ber.asbytes() 134 | -------------------------------------------------------------------------------- /paramiko/compress.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Compression implementations for a Transport. 21 | """ 22 | 23 | import zlib 24 | 25 | 26 | class ZlibCompressor (object): 27 | def __init__(self): 28 | self.z = zlib.compressobj(9) 29 | 30 | def __call__(self, data): 31 | return self.z.compress(data) + self.z.flush(zlib.Z_FULL_FLUSH) 32 | 33 | 34 | class ZlibDecompressor (object): 35 | def __init__(self): 36 | self.z = zlib.decompressobj() 37 | 38 | def __call__(self, data): 39 | return self.z.decompress(data) 40 | -------------------------------------------------------------------------------- /paramiko/dsskey.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | DSS keys. 21 | """ 22 | 23 | import os 24 | from hashlib import sha1 25 | 26 | from Crypto.PublicKey import DSA 27 | 28 | from paramiko import util 29 | from paramiko.common import zero_byte 30 | from paramiko.py3compat import long 31 | from paramiko.ssh_exception import SSHException 32 | from paramiko.message import Message 33 | from paramiko.ber import BER, BERException 34 | from paramiko.pkey import PKey 35 | 36 | 37 | class DSSKey (PKey): 38 | """ 39 | Representation of a DSS key which can be used to sign an verify SSH2 40 | data. 41 | """ 42 | 43 | def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None): 44 | self.p = None 45 | self.q = None 46 | self.g = None 47 | self.y = None 48 | self.x = None 49 | if file_obj is not None: 50 | self._from_private_key(file_obj, password) 51 | return 52 | if filename is not None: 53 | self._from_private_key_file(filename, password) 54 | return 55 | if (msg is None) and (data is not None): 56 | msg = Message(data) 57 | if vals is not None: 58 | self.p, self.q, self.g, self.y = vals 59 | else: 60 | if msg is None: 61 | raise SSHException('Key object may not be empty') 62 | if msg.get_text() != 'ssh-dss': 63 | raise SSHException('Invalid key') 64 | self.p = msg.get_mpint() 65 | self.q = msg.get_mpint() 66 | self.g = msg.get_mpint() 67 | self.y = msg.get_mpint() 68 | self.size = util.bit_length(self.p) 69 | 70 | def asbytes(self): 71 | m = Message() 72 | m.add_string('ssh-dss') 73 | m.add_mpint(self.p) 74 | m.add_mpint(self.q) 75 | m.add_mpint(self.g) 76 | m.add_mpint(self.y) 77 | return m.asbytes() 78 | 79 | def __str__(self): 80 | return self.asbytes() 81 | 82 | def __hash__(self): 83 | h = hash(self.get_name()) 84 | h = h * 37 + hash(self.p) 85 | h = h * 37 + hash(self.q) 86 | h = h * 37 + hash(self.g) 87 | h = h * 37 + hash(self.y) 88 | # h might be a long by now... 89 | return hash(h) 90 | 91 | def get_name(self): 92 | return 'ssh-dss' 93 | 94 | def get_bits(self): 95 | return self.size 96 | 97 | def can_sign(self): 98 | return self.x is not None 99 | 100 | def sign_ssh_data(self, data): 101 | digest = sha1(data).digest() 102 | dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x))) 103 | # generate a suitable k 104 | qsize = len(util.deflate_long(self.q, 0)) 105 | while True: 106 | k = util.inflate_long(os.urandom(qsize), 1) 107 | if (k > 2) and (k < self.q): 108 | break 109 | r, s = dss.sign(util.inflate_long(digest, 1), k) 110 | m = Message() 111 | m.add_string('ssh-dss') 112 | # apparently, in rare cases, r or s may be shorter than 20 bytes! 113 | rstr = util.deflate_long(r, 0) 114 | sstr = util.deflate_long(s, 0) 115 | if len(rstr) < 20: 116 | rstr = zero_byte * (20 - len(rstr)) + rstr 117 | if len(sstr) < 20: 118 | sstr = zero_byte * (20 - len(sstr)) + sstr 119 | m.add_string(rstr + sstr) 120 | return m 121 | 122 | def verify_ssh_sig(self, data, msg): 123 | if len(msg.asbytes()) == 40: 124 | # spies.com bug: signature has no header 125 | sig = msg.asbytes() 126 | else: 127 | kind = msg.get_text() 128 | if kind != 'ssh-dss': 129 | return 0 130 | sig = msg.get_binary() 131 | 132 | # pull out (r, s) which are NOT encoded as mpints 133 | sigR = util.inflate_long(sig[:20], 1) 134 | sigS = util.inflate_long(sig[20:], 1) 135 | sigM = util.inflate_long(sha1(data).digest(), 1) 136 | 137 | dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q))) 138 | return dss.verify(sigM, (sigR, sigS)) 139 | 140 | def _encode_key(self): 141 | if self.x is None: 142 | raise SSHException('Not enough key information') 143 | keylist = [0, self.p, self.q, self.g, self.y, self.x] 144 | try: 145 | b = BER() 146 | b.encode(keylist) 147 | except BERException: 148 | raise SSHException('Unable to create ber encoding of key') 149 | return b.asbytes() 150 | 151 | def write_private_key_file(self, filename, password=None): 152 | self._write_private_key_file('DSA', filename, self._encode_key(), password) 153 | 154 | def write_private_key(self, file_obj, password=None): 155 | self._write_private_key('DSA', file_obj, self._encode_key(), password) 156 | 157 | @staticmethod 158 | def generate(bits=1024, progress_func=None): 159 | """ 160 | Generate a new private DSS key. This factory function can be used to 161 | generate a new host key or authentication key. 162 | 163 | :param int bits: number of bits the generated key should be. 164 | :param function progress_func: 165 | an optional function to call at key points in key generation (used 166 | by ``pyCrypto.PublicKey``). 167 | :return: new `.DSSKey` private key 168 | """ 169 | dsa = DSA.generate(bits, os.urandom, progress_func) 170 | key = DSSKey(vals=(dsa.p, dsa.q, dsa.g, dsa.y)) 171 | key.x = dsa.x 172 | return key 173 | 174 | ### internals... 175 | 176 | def _from_private_key_file(self, filename, password): 177 | data = self._read_private_key_file('DSA', filename, password) 178 | self._decode_key(data) 179 | 180 | def _from_private_key(self, file_obj, password): 181 | data = self._read_private_key('DSA', file_obj, password) 182 | self._decode_key(data) 183 | 184 | def _decode_key(self, data): 185 | # private key file contains: 186 | # DSAPrivateKey = { version = 0, p, q, g, y, x } 187 | try: 188 | keylist = BER(data).decode() 189 | except BERException as e: 190 | raise SSHException('Unable to parse key file: ' + str(e)) 191 | if (type(keylist) is not list) or (len(keylist) < 6) or (keylist[0] != 0): 192 | raise SSHException('not a valid DSA private key file (bad ber encoding)') 193 | self.p = keylist[1] 194 | self.q = keylist[2] 195 | self.g = keylist[3] 196 | self.y = keylist[4] 197 | self.x = keylist[5] 198 | self.size = util.bit_length(self.p) 199 | -------------------------------------------------------------------------------- /paramiko/ecdsakey.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | ECDSA keys 21 | """ 22 | 23 | import binascii 24 | from hashlib import sha256 25 | 26 | from ecdsa import SigningKey, VerifyingKey, der, curves 27 | 28 | from paramiko.common import four_byte, one_byte 29 | from paramiko.message import Message 30 | from paramiko.pkey import PKey 31 | from paramiko.py3compat import byte_chr, u 32 | from paramiko.ssh_exception import SSHException 33 | 34 | 35 | class ECDSAKey (PKey): 36 | """ 37 | Representation of an ECDSA key which can be used to sign and verify SSH2 38 | data. 39 | """ 40 | 41 | def __init__(self, msg=None, data=None, filename=None, password=None, 42 | vals=None, file_obj=None, validate_point=True): 43 | self.verifying_key = None 44 | self.signing_key = None 45 | if file_obj is not None: 46 | self._from_private_key(file_obj, password) 47 | return 48 | if filename is not None: 49 | self._from_private_key_file(filename, password) 50 | return 51 | if (msg is None) and (data is not None): 52 | msg = Message(data) 53 | if vals is not None: 54 | self.signing_key, self.verifying_key = vals 55 | else: 56 | if msg is None: 57 | raise SSHException('Key object may not be empty') 58 | if msg.get_text() != 'ecdsa-sha2-nistp256': 59 | raise SSHException('Invalid key') 60 | curvename = msg.get_text() 61 | if curvename != 'nistp256': 62 | raise SSHException("Can't handle curve of type %s" % curvename) 63 | 64 | pointinfo = msg.get_binary() 65 | if pointinfo[0:1] != four_byte: 66 | raise SSHException('Point compression is being used: %s' % 67 | binascii.hexlify(pointinfo)) 68 | self.verifying_key = VerifyingKey.from_string(pointinfo[1:], 69 | curve=curves.NIST256p, 70 | validate_point=validate_point) 71 | self.size = 256 72 | 73 | def asbytes(self): 74 | key = self.verifying_key 75 | m = Message() 76 | m.add_string('ecdsa-sha2-nistp256') 77 | m.add_string('nistp256') 78 | 79 | point_str = four_byte + key.to_string() 80 | 81 | m.add_string(point_str) 82 | return m.asbytes() 83 | 84 | def __str__(self): 85 | return self.asbytes() 86 | 87 | def __hash__(self): 88 | h = hash(self.get_name()) 89 | h = h * 37 + hash(self.verifying_key.pubkey.point.x()) 90 | h = h * 37 + hash(self.verifying_key.pubkey.point.y()) 91 | return hash(h) 92 | 93 | def get_name(self): 94 | return 'ecdsa-sha2-nistp256' 95 | 96 | def get_bits(self): 97 | return self.size 98 | 99 | def can_sign(self): 100 | return self.signing_key is not None 101 | 102 | def sign_ssh_data(self, data): 103 | sig = self.signing_key.sign_deterministic( 104 | data, sigencode=self._sigencode, hashfunc=sha256) 105 | m = Message() 106 | m.add_string('ecdsa-sha2-nistp256') 107 | m.add_string(sig) 108 | return m 109 | 110 | def verify_ssh_sig(self, data, msg): 111 | if msg.get_text() != 'ecdsa-sha2-nistp256': 112 | return False 113 | sig = msg.get_binary() 114 | 115 | # verify the signature by SHA'ing the data and encrypting it 116 | # using the public key. 117 | hash_obj = sha256(data).digest() 118 | return self.verifying_key.verify_digest(sig, hash_obj, 119 | sigdecode=self._sigdecode) 120 | 121 | def write_private_key_file(self, filename, password=None): 122 | key = self.signing_key or self.verifying_key 123 | self._write_private_key_file('EC', filename, key.to_der(), password) 124 | 125 | def write_private_key(self, file_obj, password=None): 126 | key = self.signing_key or self.verifying_key 127 | self._write_private_key('EC', file_obj, key.to_der(), password) 128 | 129 | @staticmethod 130 | def generate(curve=curves.NIST256p, progress_func=None): 131 | """ 132 | Generate a new private ECDSA key. This factory function can be used to 133 | generate a new host key or authentication key. 134 | 135 | :param function progress_func: Not used for this type of key. 136 | :returns: A new private key (`.ECDSAKey`) object 137 | """ 138 | signing_key = SigningKey.generate(curve) 139 | key = ECDSAKey(vals=(signing_key, signing_key.get_verifying_key())) 140 | return key 141 | 142 | ### internals... 143 | 144 | def _from_private_key_file(self, filename, password): 145 | data = self._read_private_key_file('EC', filename, password) 146 | self._decode_key(data) 147 | 148 | def _from_private_key(self, file_obj, password): 149 | data = self._read_private_key('EC', file_obj, password) 150 | self._decode_key(data) 151 | 152 | ALLOWED_PADDINGS = [one_byte, byte_chr(2) * 2, byte_chr(3) * 3, byte_chr(4) * 4, 153 | byte_chr(5) * 5, byte_chr(6) * 6, byte_chr(7) * 7] 154 | 155 | def _decode_key(self, data): 156 | s, padding = der.remove_sequence(data) 157 | if padding: 158 | if padding not in self.ALLOWED_PADDINGS: 159 | raise ValueError("weird padding: %s" % u(binascii.hexlify(data))) 160 | data = data[:-len(padding)] 161 | key = SigningKey.from_der(data) 162 | self.signing_key = key 163 | self.verifying_key = key.get_verifying_key() 164 | self.size = 256 165 | 166 | def _sigencode(self, r, s, order): 167 | msg = Message() 168 | msg.add_mpint(r) 169 | msg.add_mpint(s) 170 | return msg.asbytes() 171 | 172 | def _sigdecode(self, sig, order): 173 | msg = Message(sig) 174 | r = msg.get_mpint() 175 | s = msg.get_mpint() 176 | return r, s 177 | -------------------------------------------------------------------------------- /paramiko/fuzz.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import hashlib 3 | import logging 4 | 5 | class StopFuzzing(Exception):pass 6 | logger = logging.getLogger("paramiko.fuzz") 7 | 8 | class FuzzControl(object): 9 | def __init__(self): 10 | self.MUTATE_INT = True 11 | self.MUTATE_STR = True 12 | self.MUTATE_BYTE = True 13 | self.MUTATION_PER_RUN = 5 14 | self.signatures_func = {} 15 | self.signatures_invocations = {} 16 | self.fuzz_methods = {} # name: func 17 | self.reset() 18 | logger.debug("--init--") 19 | 20 | def reset(self): 21 | self.mutations = 0 22 | logger.info("--reset--") 23 | 24 | def add_fuzzdef(self, fname, f): 25 | self.fuzz_methods[fname] = f 26 | 27 | def hash_sig(self, seq): 28 | return hashlib.sha256(''.join(str(e) for e in seq)).hexdigest() 29 | 30 | def print_trace(self): 31 | for x in inspect.stack(): 32 | logger.debug(x) 33 | logger.debug("-------") 34 | 35 | def candidate(self, f): 36 | signature = tuple([self.hash_sig(frame) for frame in inspect.stack()]) 37 | self.signatures_func.setdefault(signature,0) 38 | logger.info("adding static candidate: %s"%f) 39 | self.print_trace() 40 | 41 | def mutate_candidate(*args, **kwargs): 42 | signature = tuple([self.hash_sig(frame) for frame in inspect.stack()]) 43 | self.signatures_invocations.setdefault(signature,0) 44 | logger.info("adding dynamic candidate: %s"%f) 45 | self.print_trace() 46 | if self.mutations >= self.MUTATION_PER_RUN: 47 | raise StopFuzzing() 48 | if self.fuzz_methods.has_key(f.func_name) \ 49 | and self.signatures_invocations[signature]==0 \ 50 | and self.mutations < self.MUTATION_PER_RUN: 51 | self.mutations += 1 52 | # mutate 53 | logger.info("--WHOOP WHOOP MUTATE! %s - %s"%(f.func_name,repr(signature))) 54 | return self.fuzz_methods[f.func_name](*args, **kwargs) 55 | return f(*args, **kwargs) 56 | return mutate_candidate 57 | 58 | FuzzMaster = FuzzControl() 59 | logger.info("FuzzControl init.") -------------------------------------------------------------------------------- /paramiko/kex_group1.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Standard SSH key exchange ("kex" if you wanna sound cool). Diffie-Hellman of 21 | 1024 bit key halves, using a known "p" prime and "g" generator. 22 | """ 23 | 24 | import os 25 | from hashlib import sha1 26 | 27 | from paramiko import util 28 | from paramiko.common import max_byte, zero_byte 29 | from paramiko.message import Message 30 | from paramiko.py3compat import byte_chr, long, byte_mask 31 | from paramiko.ssh_exception import SSHException 32 | 33 | 34 | _MSG_KEXDH_INIT, _MSG_KEXDH_REPLY = range(30, 32) 35 | c_MSG_KEXDH_INIT, c_MSG_KEXDH_REPLY = [byte_chr(c) for c in range(30, 32)] 36 | 37 | b7fffffffffffffff = byte_chr(0x7f) + max_byte * 7 38 | b0000000000000000 = zero_byte * 8 39 | 40 | 41 | class KexGroup1(object): 42 | 43 | # draft-ietf-secsh-transport-09.txt, page 17 44 | P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF 45 | G = 2 46 | 47 | name = 'diffie-hellman-group1-sha1' 48 | hash_algo = sha1 49 | 50 | def __init__(self, transport): 51 | self.transport = transport 52 | self.x = long(0) 53 | self.e = long(0) 54 | self.f = long(0) 55 | 56 | def start_kex(self): 57 | self._generate_x() 58 | if self.transport.server_mode: 59 | # compute f = g^x mod p, but don't send it yet 60 | self.f = pow(self.G, self.x, self.P) 61 | self.transport._expect_packet(_MSG_KEXDH_INIT) 62 | return 63 | # compute e = g^x mod p (where g=2), and send it 64 | self.e = pow(self.G, self.x, self.P) 65 | m = Message() 66 | m.add_byte(c_MSG_KEXDH_INIT) 67 | m.add_mpint(self.e) 68 | self.transport._send_message(m) 69 | self.transport._expect_packet(_MSG_KEXDH_REPLY) 70 | 71 | def parse_next(self, ptype, m): 72 | if self.transport.server_mode and (ptype == _MSG_KEXDH_INIT): 73 | return self._parse_kexdh_init(m) 74 | elif not self.transport.server_mode and (ptype == _MSG_KEXDH_REPLY): 75 | return self._parse_kexdh_reply(m) 76 | raise SSHException('KexGroup1 asked to handle packet type %d' % ptype) 77 | 78 | ### internals... 79 | 80 | def _generate_x(self): 81 | # generate an "x" (1 < x < q), where q is (p-1)/2. 82 | # p is a 128-byte (1024-bit) number, where the first 64 bits are 1. 83 | # therefore q can be approximated as a 2^1023. we drop the subset of 84 | # potential x where the first 63 bits are 1, because some of those will be 85 | # larger than q (but this is a tiny tiny subset of potential x). 86 | while 1: 87 | x_bytes = os.urandom(128) 88 | x_bytes = byte_mask(x_bytes[0], 0x7f) + x_bytes[1:] 89 | if (x_bytes[:8] != b7fffffffffffffff and 90 | x_bytes[:8] != b0000000000000000): 91 | break 92 | self.x = util.inflate_long(x_bytes) 93 | 94 | def _parse_kexdh_reply(self, m): 95 | # client mode 96 | host_key = m.get_string() 97 | self.f = m.get_mpint() 98 | if (self.f < 1) or (self.f > self.P - 1): 99 | raise SSHException('Server kex "f" is out of range') 100 | sig = m.get_binary() 101 | K = pow(self.f, self.x, self.P) 102 | # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K) 103 | hm = Message() 104 | hm.add(self.transport.local_version, self.transport.remote_version, 105 | self.transport.local_kex_init, self.transport.remote_kex_init) 106 | hm.add_string(host_key) 107 | hm.add_mpint(self.e) 108 | hm.add_mpint(self.f) 109 | hm.add_mpint(K) 110 | self.transport._set_K_H(K, sha1(hm.asbytes()).digest()) 111 | self.transport._verify_key(host_key, sig) 112 | self.transport._activate_outbound() 113 | 114 | def _parse_kexdh_init(self, m): 115 | # server mode 116 | self.e = m.get_mpint() 117 | if (self.e < 1) or (self.e > self.P - 1): 118 | raise SSHException('Client kex "e" is out of range') 119 | K = pow(self.e, self.x, self.P) 120 | key = self.transport.get_server_key().asbytes() 121 | # okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K) 122 | hm = Message() 123 | hm.add(self.transport.remote_version, self.transport.local_version, 124 | self.transport.remote_kex_init, self.transport.local_kex_init) 125 | hm.add_string(key) 126 | hm.add_mpint(self.e) 127 | hm.add_mpint(self.f) 128 | hm.add_mpint(K) 129 | H = sha1(hm.asbytes()).digest() 130 | self.transport._set_K_H(K, H) 131 | # sign it 132 | sig = self.transport.get_server_key().sign_ssh_data(H) 133 | # send reply 134 | m = Message() 135 | m.add_byte(c_MSG_KEXDH_REPLY) 136 | m.add_string(key) 137 | m.add_mpint(self.f) 138 | m.add_string(sig) 139 | self.transport._send_message(m) 140 | self.transport._activate_outbound() 141 | -------------------------------------------------------------------------------- /paramiko/kex_group14.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013 Torsten Landschoff 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Standard SSH key exchange ("kex" if you wanna sound cool). Diffie-Hellman of 21 | 2048 bit key halves, using a known "p" prime and "g" generator. 22 | """ 23 | 24 | from paramiko.kex_group1 import KexGroup1 25 | from hashlib import sha1 26 | 27 | 28 | class KexGroup14(KexGroup1): 29 | 30 | # http://tools.ietf.org/html/rfc3526#section-3 31 | P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF 32 | G = 2 33 | 34 | name = 'diffie-hellman-group14-sha1' 35 | hash_algo = sha1 36 | -------------------------------------------------------------------------------- /paramiko/pipe.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Abstraction of a one-way pipe where the read end can be used in 21 | `select.select`. Normally this is trivial, but Windows makes it nearly 22 | impossible. 23 | 24 | The pipe acts like an Event, which can be set or cleared. When set, the pipe 25 | will trigger as readable in `select `. 26 | """ 27 | 28 | import sys 29 | import os 30 | import socket 31 | from paramiko.py3compat import b 32 | 33 | 34 | def make_pipe(): 35 | if sys.platform[:3] != 'win': 36 | p = PosixPipe() 37 | else: 38 | p = WindowsPipe() 39 | return p 40 | 41 | 42 | class PosixPipe (object): 43 | def __init__(self): 44 | self._rfd, self._wfd = os.pipe() 45 | self._set = False 46 | self._forever = False 47 | self._closed = False 48 | 49 | def close(self): 50 | os.close(self._rfd) 51 | os.close(self._wfd) 52 | # used for unit tests: 53 | self._closed = True 54 | 55 | def fileno(self): 56 | return self._rfd 57 | 58 | def clear(self): 59 | if not self._set or self._forever: 60 | return 61 | os.read(self._rfd, 1) 62 | self._set = False 63 | 64 | def set(self): 65 | if self._set or self._closed: 66 | return 67 | self._set = True 68 | os.write(self._wfd, b'*') 69 | 70 | def set_forever(self): 71 | self._forever = True 72 | self.set() 73 | 74 | 75 | class WindowsPipe (object): 76 | """ 77 | On Windows, only an OS-level "WinSock" may be used in select(), but reads 78 | and writes must be to the actual socket object. 79 | """ 80 | def __init__(self): 81 | serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 82 | serv.bind(('127.0.0.1', 0)) 83 | serv.listen(1) 84 | 85 | # need to save sockets in _rsock/_wsock so they don't get closed 86 | self._rsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 87 | self._rsock.connect(('127.0.0.1', serv.getsockname()[1])) 88 | 89 | self._wsock, addr = serv.accept() 90 | serv.close() 91 | self._set = False 92 | self._forever = False 93 | self._closed = False 94 | 95 | def close(self): 96 | self._rsock.close() 97 | self._wsock.close() 98 | # used for unit tests: 99 | self._closed = True 100 | 101 | def fileno(self): 102 | return self._rsock.fileno() 103 | 104 | def clear (self): 105 | if not self._set or self._forever: 106 | return 107 | self._rsock.recv(1) 108 | self._set = False 109 | 110 | def set (self): 111 | if self._set or self._closed: 112 | return 113 | self._set = True 114 | self._wsock.send(b'*') 115 | 116 | def set_forever (self): 117 | self._forever = True 118 | self.set() 119 | 120 | 121 | class OrPipe (object): 122 | def __init__(self, pipe): 123 | self._set = False 124 | self._partner = None 125 | self._pipe = pipe 126 | 127 | def set(self): 128 | self._set = True 129 | if not self._partner._set: 130 | self._pipe.set() 131 | 132 | def clear(self): 133 | self._set = False 134 | if not self._partner._set: 135 | self._pipe.clear() 136 | 137 | 138 | def make_or_pipe(pipe): 139 | """ 140 | wraps a pipe into two pipe-like objects which are "or"d together to 141 | affect the real pipe. if either returned pipe is set, the wrapped pipe 142 | is set. when both are cleared, the wrapped pipe is cleared. 143 | """ 144 | p1 = OrPipe(pipe) 145 | p2 = OrPipe(pipe) 146 | p1._partner = p2 147 | p2._partner = p1 148 | return p1, p2 149 | 150 | -------------------------------------------------------------------------------- /paramiko/primes.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Utility functions for dealing with primes. 21 | """ 22 | 23 | import os 24 | 25 | from paramiko import util 26 | from paramiko.py3compat import byte_mask, long 27 | from paramiko.ssh_exception import SSHException 28 | from paramiko.common import * 29 | 30 | 31 | def _roll_random(n): 32 | """returns a random # from 0 to N-1""" 33 | bits = util.bit_length(n - 1) 34 | byte_count = (bits + 7) // 8 35 | hbyte_mask = pow(2, bits % 8) - 1 36 | 37 | # so here's the plan: 38 | # we fetch as many random bits as we'd need to fit N-1, and if the 39 | # generated number is >= N, we try again. in the worst case (N-1 is a 40 | # power of 2), we have slightly better than 50% odds of getting one that 41 | # fits, so i can't guarantee that this loop will ever finish, but the odds 42 | # of it looping forever should be infinitesimal. 43 | while True: 44 | x = os.urandom(byte_count) 45 | if hbyte_mask > 0: 46 | x = byte_mask(x[0], hbyte_mask) + x[1:] 47 | num = util.inflate_long(x, 1) 48 | if num < n: 49 | break 50 | return num 51 | 52 | 53 | class ModulusPack (object): 54 | """ 55 | convenience object for holding the contents of the /etc/ssh/moduli file, 56 | on systems that have such a file. 57 | """ 58 | 59 | def __init__(self): 60 | # pack is a hash of: bits -> [ (generator, modulus) ... ] 61 | self.pack = {} 62 | self.discarded = [] 63 | 64 | def _parse_modulus(self, line): 65 | timestamp, mod_type, tests, tries, size, generator, modulus = line.split() 66 | mod_type = int(mod_type) 67 | tests = int(tests) 68 | tries = int(tries) 69 | size = int(size) 70 | generator = int(generator) 71 | modulus = long(modulus, 16) 72 | 73 | # weed out primes that aren't at least: 74 | # type 2 (meets basic structural requirements) 75 | # test 4 (more than just a small-prime sieve) 76 | # tries < 100 if test & 4 (at least 100 tries of miller-rabin) 77 | if (mod_type < 2) or (tests < 4) or ((tests & 4) and (tests < 8) and (tries < 100)): 78 | self.discarded.append((modulus, 'does not meet basic requirements')) 79 | return 80 | if generator == 0: 81 | generator = 2 82 | 83 | # there's a bug in the ssh "moduli" file (yeah, i know: shock! dismay! 84 | # call cnn!) where it understates the bit lengths of these primes by 1. 85 | # this is okay. 86 | bl = util.bit_length(modulus) 87 | if (bl != size) and (bl != size + 1): 88 | self.discarded.append((modulus, 'incorrectly reported bit length %d' % size)) 89 | return 90 | if bl not in self.pack: 91 | self.pack[bl] = [] 92 | self.pack[bl].append((generator, modulus)) 93 | 94 | def read_file(self, filename): 95 | """ 96 | :raises IOError: passed from any file operations that fail. 97 | """ 98 | self.pack = {} 99 | with open(filename, 'r') as f: 100 | for line in f: 101 | line = line.strip() 102 | if (len(line) == 0) or (line[0] == '#'): 103 | continue 104 | try: 105 | self._parse_modulus(line) 106 | except: 107 | continue 108 | 109 | def get_modulus(self, min, prefer, max): 110 | bitsizes = sorted(self.pack.keys()) 111 | if len(bitsizes) == 0: 112 | raise SSHException('no moduli available') 113 | good = -1 114 | # find nearest bitsize >= preferred 115 | for b in bitsizes: 116 | if (b >= prefer) and (b < max) and (b < good or good == -1): 117 | good = b 118 | # if that failed, find greatest bitsize >= min 119 | if good == -1: 120 | for b in bitsizes: 121 | if (b >= min) and (b < max) and (b > good): 122 | good = b 123 | if good == -1: 124 | # their entire (min, max) range has no intersection with our range. 125 | # if their range is below ours, pick the smallest. otherwise pick 126 | # the largest. it'll be out of their range requirement either way, 127 | # but we'll be sending them the closest one we have. 128 | good = bitsizes[0] 129 | if min > good: 130 | good = bitsizes[-1] 131 | # now pick a random modulus of this bitsize 132 | n = _roll_random(len(self.pack[good])) 133 | return self.pack[good][n] 134 | -------------------------------------------------------------------------------- /paramiko/proxy.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2012 Yipit, Inc 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | 20 | from datetime import datetime 21 | import os 22 | from shlex import split as shlsplit 23 | import signal 24 | from subprocess import Popen, PIPE 25 | from select import select 26 | import socket 27 | import time 28 | 29 | from paramiko.ssh_exception import ProxyCommandFailure 30 | from paramiko.util import ClosingContextManager 31 | 32 | 33 | class ProxyCommand(ClosingContextManager): 34 | """ 35 | Wraps a subprocess running ProxyCommand-driven programs. 36 | 37 | This class implements a the socket-like interface needed by the 38 | `.Transport` and `.Packetizer` classes. Using this class instead of a 39 | regular socket makes it possible to talk with a Popen'd command that will 40 | proxy traffic between the client and a server hosted in another machine. 41 | 42 | Instances of this class may be used as context managers. 43 | """ 44 | def __init__(self, command_line): 45 | """ 46 | Create a new CommandProxy instance. The instance created by this 47 | class can be passed as an argument to the `.Transport` class. 48 | 49 | :param str command_line: 50 | the command that should be executed and used as the proxy. 51 | """ 52 | self.cmd = shlsplit(command_line) 53 | self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 54 | self.timeout = None 55 | self.buffer = [] 56 | 57 | def send(self, content): 58 | """ 59 | Write the content received from the SSH client to the standard 60 | input of the forked command. 61 | 62 | :param str content: string to be sent to the forked command 63 | """ 64 | try: 65 | self.process.stdin.write(content) 66 | except IOError as e: 67 | # There was a problem with the child process. It probably 68 | # died and we can't proceed. The best option here is to 69 | # raise an exception informing the user that the informed 70 | # ProxyCommand is not working. 71 | raise ProxyCommandFailure(' '.join(self.cmd), e.strerror) 72 | return len(content) 73 | 74 | def recv(self, size): 75 | """ 76 | Read from the standard output of the forked program. 77 | 78 | :param int size: how many chars should be read 79 | 80 | :return: the length of the read content, as an `int` 81 | """ 82 | try: 83 | start = time.time() 84 | while len(self.buffer) < size: 85 | select_timeout = None 86 | if self.timeout is not None: 87 | elapsed = (time.time() - start) 88 | if elapsed >= self.timeout: 89 | raise socket.timeout() 90 | select_timeout = self.timeout - elapsed 91 | 92 | r, w, x = select( 93 | [self.process.stdout], [], [], select_timeout) 94 | if r and r[0] == self.process.stdout: 95 | b = os.read( 96 | self.process.stdout.fileno(), size - len(self.buffer)) 97 | # Store in class-level buffer for persistence across 98 | # timeouts; this makes us act more like a real socket 99 | # (where timeouts don't actually drop data.) 100 | self.buffer.extend(b) 101 | result = ''.join(self.buffer) 102 | self.buffer = [] 103 | return result 104 | except socket.timeout: 105 | raise # socket.timeout is a subclass of IOError 106 | except IOError as e: 107 | raise ProxyCommandFailure(' '.join(self.cmd), e.strerror) 108 | 109 | def close(self): 110 | os.kill(self.process.pid, signal.SIGTERM) 111 | 112 | def settimeout(self, timeout): 113 | self.timeout = timeout 114 | -------------------------------------------------------------------------------- /paramiko/py3compat.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import base64 3 | 4 | __all__ = ['PY2', 'string_types', 'integer_types', 'text_type', 'bytes_types', 'bytes', 'long', 'input', 5 | 'decodebytes', 'encodebytes', 'bytestring', 'byte_ord', 'byte_chr', 'byte_mask', 6 | 'b', 'u', 'b2s', 'StringIO', 'BytesIO', 'is_callable', 'MAXSIZE', 'next', 'builtins'] 7 | 8 | PY2 = sys.version_info[0] < 3 9 | 10 | if PY2: 11 | string_types = basestring 12 | text_type = unicode 13 | bytes_types = str 14 | bytes = str 15 | integer_types = (int, long) 16 | long = long 17 | input = raw_input 18 | decodebytes = base64.decodestring 19 | encodebytes = base64.encodestring 20 | 21 | import __builtin__ as builtins 22 | 23 | 24 | def bytestring(s): # NOQA 25 | if isinstance(s, unicode): 26 | return s.encode('utf-8') 27 | return s 28 | 29 | 30 | byte_ord = ord # NOQA 31 | byte_chr = chr # NOQA 32 | 33 | 34 | def byte_mask(c, mask): 35 | return chr(ord(c) & mask) 36 | 37 | 38 | def b(s, encoding='utf8'): # NOQA 39 | """cast unicode or bytes to bytes""" 40 | if isinstance(s, str): 41 | return s 42 | elif isinstance(s, unicode): 43 | return s.encode(encoding) 44 | elif isinstance(s, buffer): 45 | return s 46 | else: 47 | raise TypeError("Expected unicode or bytes, got %r" % s) 48 | 49 | 50 | def u(s, encoding='utf8'): # NOQA 51 | """cast bytes or unicode to unicode""" 52 | if isinstance(s, str): 53 | return s.decode(encoding) 54 | elif isinstance(s, unicode): 55 | return s 56 | elif isinstance(s, buffer): 57 | return s.decode(encoding) 58 | else: 59 | raise TypeError("Expected unicode or bytes, got %r" % s) 60 | 61 | 62 | def b2s(s): 63 | return s 64 | 65 | 66 | try: 67 | import cStringIO 68 | 69 | StringIO = cStringIO.StringIO # NOQA 70 | except ImportError: 71 | import StringIO 72 | 73 | StringIO = StringIO.StringIO # NOQA 74 | 75 | BytesIO = StringIO 76 | 77 | 78 | def is_callable(c): # NOQA 79 | return callable(c) 80 | 81 | 82 | def get_next(c): # NOQA 83 | return c.next 84 | 85 | 86 | def next(c): 87 | return c.next() 88 | 89 | # It's possible to have sizeof(long) != sizeof(Py_ssize_t). 90 | class X(object): 91 | def __len__(self): 92 | return 1 << 31 93 | 94 | 95 | try: 96 | len(X()) 97 | except OverflowError: 98 | # 32-bit 99 | MAXSIZE = int((1 << 31) - 1) # NOQA 100 | else: 101 | # 64-bit 102 | MAXSIZE = int((1 << 63) - 1) # NOQA 103 | del X 104 | else: 105 | import collections 106 | import struct 107 | import builtins 108 | string_types = str 109 | text_type = str 110 | bytes = bytes 111 | bytes_types = bytes 112 | integer_types = int 113 | class long(int): 114 | pass 115 | input = input 116 | decodebytes = base64.decodebytes 117 | encodebytes = base64.encodebytes 118 | 119 | def bytestring(s): 120 | return s 121 | 122 | def byte_ord(c): 123 | # In case we're handed a string instead of an int. 124 | if not isinstance(c, int): 125 | c = ord(c) 126 | return c 127 | 128 | def byte_chr(c): 129 | assert isinstance(c, int) 130 | return struct.pack('B', c) 131 | 132 | def byte_mask(c, mask): 133 | assert isinstance(c, int) 134 | return struct.pack('B', c & mask) 135 | 136 | def b(s, encoding='utf8'): 137 | """cast unicode or bytes to bytes""" 138 | if isinstance(s, bytes): 139 | return s 140 | elif isinstance(s, str): 141 | return s.encode(encoding) 142 | else: 143 | raise TypeError("Expected unicode or bytes, got %r" % s) 144 | 145 | def u(s, encoding='utf8'): 146 | """cast bytes or unicode to unicode""" 147 | if isinstance(s, bytes): 148 | return s.decode(encoding) 149 | elif isinstance(s, str): 150 | return s 151 | else: 152 | raise TypeError("Expected unicode or bytes, got %r" % s) 153 | 154 | def b2s(s): 155 | return s.decode() if isinstance(s, bytes) else s 156 | 157 | import io 158 | StringIO = io.StringIO # NOQA 159 | BytesIO = io.BytesIO # NOQA 160 | 161 | def is_callable(c): 162 | return isinstance(c, collections.Callable) 163 | 164 | def get_next(c): 165 | return c.__next__ 166 | 167 | next = next 168 | 169 | MAXSIZE = sys.maxsize # NOQA 170 | -------------------------------------------------------------------------------- /paramiko/resource.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Resource manager. 21 | """ 22 | 23 | import weakref 24 | 25 | 26 | class ResourceManager (object): 27 | """ 28 | A registry of objects and resources that should be closed when those 29 | objects are deleted. 30 | 31 | This is meant to be a safer alternative to Python's ``__del__`` method, 32 | which can cause reference cycles to never be collected. Objects registered 33 | with the ResourceManager can be collected but still free resources when 34 | they die. 35 | 36 | Resources are registered using `register`, and when an object is garbage 37 | collected, each registered resource is closed by having its ``close()`` 38 | method called. Multiple resources may be registered per object, but a 39 | resource will only be closed once, even if multiple objects register it. 40 | (The last object to register it wins.) 41 | """ 42 | 43 | def __init__(self): 44 | self._table = {} 45 | 46 | def register(self, obj, resource): 47 | """ 48 | Register a resource to be closed with an object is collected. 49 | 50 | When the given ``obj`` is garbage-collected by the Python interpreter, 51 | the ``resource`` will be closed by having its ``close()`` method called. 52 | Any exceptions are ignored. 53 | 54 | :param object obj: the object to track 55 | :param object resource: 56 | the resource to close when the object is collected 57 | """ 58 | def callback(ref): 59 | try: 60 | resource.close() 61 | except: 62 | pass 63 | del self._table[id(resource)] 64 | 65 | # keep the weakref in a table so it sticks around long enough to get 66 | # its callback called. :) 67 | self._table[id(resource)] = weakref.ref(obj, callback) 68 | 69 | 70 | # singleton 71 | ResourceManager = ResourceManager() 72 | -------------------------------------------------------------------------------- /paramiko/rsakey.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | RSA keys. 21 | """ 22 | 23 | import os 24 | from hashlib import sha1 25 | 26 | from Crypto.PublicKey import RSA 27 | 28 | from paramiko import util 29 | from paramiko.common import max_byte, zero_byte, one_byte 30 | from paramiko.message import Message 31 | from paramiko.ber import BER, BERException 32 | from paramiko.pkey import PKey 33 | from paramiko.py3compat import long 34 | from paramiko.ssh_exception import SSHException 35 | 36 | SHA1_DIGESTINFO = b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14' 37 | 38 | 39 | class RSAKey (PKey): 40 | """ 41 | Representation of an RSA key which can be used to sign and verify SSH2 42 | data. 43 | """ 44 | 45 | def __init__(self, msg=None, data=None, filename=None, password=None, vals=None, file_obj=None): 46 | self.n = None 47 | self.e = None 48 | self.d = None 49 | self.p = None 50 | self.q = None 51 | if file_obj is not None: 52 | self._from_private_key(file_obj, password) 53 | return 54 | if filename is not None: 55 | self._from_private_key_file(filename, password) 56 | return 57 | if (msg is None) and (data is not None): 58 | msg = Message(data) 59 | if vals is not None: 60 | self.e, self.n = vals 61 | else: 62 | if msg is None: 63 | raise SSHException('Key object may not be empty') 64 | if msg.get_text() != 'ssh-rsa': 65 | raise SSHException('Invalid key') 66 | self.e = msg.get_mpint() 67 | self.n = msg.get_mpint() 68 | self.size = util.bit_length(self.n) 69 | 70 | def asbytes(self): 71 | m = Message() 72 | m.add_string('ssh-rsa') 73 | m.add_mpint(self.e) 74 | m.add_mpint(self.n) 75 | return m.asbytes() 76 | 77 | def __str__(self): 78 | return self.asbytes() 79 | 80 | def __hash__(self): 81 | h = hash(self.get_name()) 82 | h = h * 37 + hash(self.e) 83 | h = h * 37 + hash(self.n) 84 | return hash(h) 85 | 86 | def get_name(self): 87 | return 'ssh-rsa' 88 | 89 | def get_bits(self): 90 | return self.size 91 | 92 | def can_sign(self): 93 | return self.d is not None 94 | 95 | def sign_ssh_data(self, data): 96 | digest = sha1(data).digest() 97 | rsa = RSA.construct((long(self.n), long(self.e), long(self.d))) 98 | sig = util.deflate_long(rsa.sign(self._pkcs1imify(digest), bytes())[0], 0) 99 | m = Message() 100 | m.add_string('ssh-rsa') 101 | m.add_string(sig) 102 | return m 103 | 104 | def verify_ssh_sig(self, data, msg): 105 | if msg.get_text() != 'ssh-rsa': 106 | return False 107 | sig = util.inflate_long(msg.get_binary(), True) 108 | # verify the signature by SHA'ing the data and encrypting it using the 109 | # public key. some wackiness ensues where we "pkcs1imify" the 20-byte 110 | # hash into a string as long as the RSA key. 111 | hash_obj = util.inflate_long(self._pkcs1imify(sha1(data).digest()), True) 112 | rsa = RSA.construct((long(self.n), long(self.e))) 113 | return rsa.verify(hash_obj, (sig,)) 114 | 115 | def _encode_key(self): 116 | if (self.p is None) or (self.q is None): 117 | raise SSHException('Not enough key info to write private key file') 118 | keylist = [0, self.n, self.e, self.d, self.p, self.q, 119 | self.d % (self.p - 1), self.d % (self.q - 1), 120 | util.mod_inverse(self.q, self.p)] 121 | try: 122 | b = BER() 123 | b.encode(keylist) 124 | except BERException: 125 | raise SSHException('Unable to create ber encoding of key') 126 | return b.asbytes() 127 | 128 | def write_private_key_file(self, filename, password=None): 129 | self._write_private_key_file('RSA', filename, self._encode_key(), password) 130 | 131 | def write_private_key(self, file_obj, password=None): 132 | self._write_private_key('RSA', file_obj, self._encode_key(), password) 133 | 134 | @staticmethod 135 | def generate(bits, progress_func=None): 136 | """ 137 | Generate a new private RSA key. This factory function can be used to 138 | generate a new host key or authentication key. 139 | 140 | :param int bits: number of bits the generated key should be. 141 | :param function progress_func: 142 | an optional function to call at key points in key generation (used 143 | by ``pyCrypto.PublicKey``). 144 | :return: new `.RSAKey` private key 145 | """ 146 | rsa = RSA.generate(bits, os.urandom, progress_func) 147 | key = RSAKey(vals=(rsa.e, rsa.n)) 148 | key.d = rsa.d 149 | key.p = rsa.p 150 | key.q = rsa.q 151 | return key 152 | 153 | ### internals... 154 | 155 | def _pkcs1imify(self, data): 156 | """ 157 | turn a 20-byte SHA1 hash into a blob of data as large as the key's N, 158 | using PKCS1's \"emsa-pkcs1-v1_5\" encoding. totally bizarre. 159 | """ 160 | size = len(util.deflate_long(self.n, 0)) 161 | filler = max_byte * (size - len(SHA1_DIGESTINFO) - len(data) - 3) 162 | return zero_byte + one_byte + filler + zero_byte + SHA1_DIGESTINFO + data 163 | 164 | def _from_private_key_file(self, filename, password): 165 | data = self._read_private_key_file('RSA', filename, password) 166 | self._decode_key(data) 167 | 168 | def _from_private_key(self, file_obj, password): 169 | data = self._read_private_key('RSA', file_obj, password) 170 | self._decode_key(data) 171 | 172 | def _decode_key(self, data): 173 | # private key file contains: 174 | # RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p } 175 | try: 176 | keylist = BER(data).decode() 177 | except BERException: 178 | raise SSHException('Unable to parse key file') 179 | if (type(keylist) is not list) or (len(keylist) < 4) or (keylist[0] != 0): 180 | raise SSHException('Not a valid RSA private key file (bad ber encoding)') 181 | self.n = keylist[1] 182 | self.e = keylist[2] 183 | self.d = keylist[3] 184 | # not really needed 185 | self.p = keylist[4] 186 | self.q = keylist[5] 187 | self.size = util.bit_length(self.n) 188 | -------------------------------------------------------------------------------- /paramiko/sftp.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | import select 20 | import socket 21 | import struct 22 | 23 | from paramiko import util 24 | from paramiko.common import asbytes, DEBUG 25 | from paramiko.message import Message 26 | from paramiko.py3compat import byte_chr, byte_ord 27 | 28 | 29 | CMD_INIT, CMD_VERSION, CMD_OPEN, CMD_CLOSE, CMD_READ, CMD_WRITE, CMD_LSTAT, CMD_FSTAT, \ 30 | CMD_SETSTAT, CMD_FSETSTAT, CMD_OPENDIR, CMD_READDIR, CMD_REMOVE, CMD_MKDIR, \ 31 | CMD_RMDIR, CMD_REALPATH, CMD_STAT, CMD_RENAME, CMD_READLINK, CMD_SYMLINK = range(1, 21) 32 | CMD_STATUS, CMD_HANDLE, CMD_DATA, CMD_NAME, CMD_ATTRS = range(101, 106) 33 | CMD_EXTENDED, CMD_EXTENDED_REPLY = range(200, 202) 34 | 35 | SFTP_OK = 0 36 | SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE, SFTP_BAD_MESSAGE, \ 37 | SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, SFTP_OP_UNSUPPORTED = range(1, 9) 38 | 39 | SFTP_DESC = ['Success', 40 | 'End of file', 41 | 'No such file', 42 | 'Permission denied', 43 | 'Failure', 44 | 'Bad message', 45 | 'No connection', 46 | 'Connection lost', 47 | 'Operation unsupported'] 48 | 49 | SFTP_FLAG_READ = 0x1 50 | SFTP_FLAG_WRITE = 0x2 51 | SFTP_FLAG_APPEND = 0x4 52 | SFTP_FLAG_CREATE = 0x8 53 | SFTP_FLAG_TRUNC = 0x10 54 | SFTP_FLAG_EXCL = 0x20 55 | 56 | _VERSION = 3 57 | 58 | 59 | # for debugging 60 | CMD_NAMES = { 61 | CMD_INIT: 'init', 62 | CMD_VERSION: 'version', 63 | CMD_OPEN: 'open', 64 | CMD_CLOSE: 'close', 65 | CMD_READ: 'read', 66 | CMD_WRITE: 'write', 67 | CMD_LSTAT: 'lstat', 68 | CMD_FSTAT: 'fstat', 69 | CMD_SETSTAT: 'setstat', 70 | CMD_FSETSTAT: 'fsetstat', 71 | CMD_OPENDIR: 'opendir', 72 | CMD_READDIR: 'readdir', 73 | CMD_REMOVE: 'remove', 74 | CMD_MKDIR: 'mkdir', 75 | CMD_RMDIR: 'rmdir', 76 | CMD_REALPATH: 'realpath', 77 | CMD_STAT: 'stat', 78 | CMD_RENAME: 'rename', 79 | CMD_READLINK: 'readlink', 80 | CMD_SYMLINK: 'symlink', 81 | CMD_STATUS: 'status', 82 | CMD_HANDLE: 'handle', 83 | CMD_DATA: 'data', 84 | CMD_NAME: 'name', 85 | CMD_ATTRS: 'attrs', 86 | CMD_EXTENDED: 'extended', 87 | CMD_EXTENDED_REPLY: 'extended_reply' 88 | } 89 | 90 | 91 | class SFTPError (Exception): 92 | pass 93 | 94 | 95 | class BaseSFTP (object): 96 | def __init__(self): 97 | self.logger = util.get_logger('paramiko.sftp') 98 | self.sock = None 99 | self.ultra_debug = False 100 | 101 | ### internals... 102 | 103 | def _send_version(self): 104 | self._send_packet(CMD_INIT, struct.pack('>I', _VERSION)) 105 | t, data = self._read_packet() 106 | if t != CMD_VERSION: 107 | raise SFTPError('Incompatible sftp protocol') 108 | version = struct.unpack('>I', data[:4])[0] 109 | # if version != _VERSION: 110 | # raise SFTPError('Incompatible sftp protocol') 111 | return version 112 | 113 | def _send_server_version(self): 114 | # winscp will freak out if the server sends version info before the 115 | # client finishes sending INIT. 116 | t, data = self._read_packet() 117 | if t != CMD_INIT: 118 | raise SFTPError('Incompatible sftp protocol') 119 | version = struct.unpack('>I', data[:4])[0] 120 | # advertise that we support "check-file" 121 | extension_pairs = ['check-file', 'md5,sha1'] 122 | msg = Message() 123 | msg.add_int(_VERSION) 124 | msg.add(*extension_pairs) 125 | self._send_packet(CMD_VERSION, msg) 126 | return version 127 | 128 | def _log(self, level, msg, *args): 129 | self.logger.log(level, msg, *args) 130 | 131 | def _write_all(self, out): 132 | while len(out) > 0: 133 | n = self.sock.send(out) 134 | if n <= 0: 135 | raise EOFError() 136 | if n == len(out): 137 | return 138 | out = out[n:] 139 | return 140 | 141 | def _read_all(self, n): 142 | out = bytes() 143 | while n > 0: 144 | if isinstance(self.sock, socket.socket): 145 | # sometimes sftp is used directly over a socket instead of 146 | # through a paramiko channel. in this case, check periodically 147 | # if the socket is closed. (for some reason, recv() won't ever 148 | # return or raise an exception, but calling select on a closed 149 | # socket will.) 150 | while True: 151 | read, write, err = select.select([self.sock], [], [], 0.1) 152 | if len(read) > 0: 153 | x = self.sock.recv(n) 154 | break 155 | else: 156 | x = self.sock.recv(n) 157 | 158 | if len(x) == 0: 159 | raise EOFError() 160 | out += x 161 | n -= len(x) 162 | return out 163 | 164 | def _send_packet(self, t, packet): 165 | #self._log(DEBUG2, 'write: %s (len=%d)' % (CMD_NAMES.get(t, '0x%02x' % t), len(packet))) 166 | packet = asbytes(packet) 167 | out = struct.pack('>I', len(packet) + 1) + byte_chr(t) + packet 168 | if self.ultra_debug: 169 | self._log(DEBUG, util.format_binary(out, 'OUT: ')) 170 | self._write_all(out) 171 | 172 | def _read_packet(self): 173 | x = self._read_all(4) 174 | # most sftp servers won't accept packets larger than about 32k, so 175 | # anything with the high byte set (> 16MB) is just garbage. 176 | if byte_ord(x[0]): 177 | raise SFTPError('Garbage packet received') 178 | size = struct.unpack('>I', x)[0] 179 | data = self._read_all(size) 180 | if self.ultra_debug: 181 | self._log(DEBUG, util.format_binary(data, 'IN: ')) 182 | if size > 0: 183 | t = byte_ord(data[0]) 184 | #self._log(DEBUG2, 'read: %s (len=%d)' % (CMD_NAMES.get(t), '0x%02x' % t, len(data)-1)) 185 | return t, data[1:] 186 | return 0, bytes() 187 | -------------------------------------------------------------------------------- /paramiko/ssh_exception.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | import socket 20 | 21 | 22 | class SSHException (Exception): 23 | """ 24 | Exception raised by failures in SSH2 protocol negotiation or logic errors. 25 | """ 26 | pass 27 | 28 | 29 | class AuthenticationException (SSHException): 30 | """ 31 | Exception raised when authentication failed for some reason. It may be 32 | possible to retry with different credentials. (Other classes specify more 33 | specific reasons.) 34 | 35 | .. versionadded:: 1.6 36 | """ 37 | pass 38 | 39 | 40 | class PasswordRequiredException (AuthenticationException): 41 | """ 42 | Exception raised when a password is needed to unlock a private key file. 43 | """ 44 | pass 45 | 46 | 47 | class BadAuthenticationType (AuthenticationException): 48 | """ 49 | Exception raised when an authentication type (like password) is used, but 50 | the server isn't allowing that type. (It may only allow public-key, for 51 | example.) 52 | 53 | :ivar list allowed_types: 54 | list of allowed authentication types provided by the server (possible 55 | values are: ``"none"``, ``"password"``, and ``"publickey"``). 56 | 57 | .. versionadded:: 1.1 58 | """ 59 | allowed_types = [] 60 | 61 | def __init__(self, explanation, types): 62 | AuthenticationException.__init__(self, explanation) 63 | self.allowed_types = types 64 | # for unpickling 65 | self.args = (explanation, types, ) 66 | 67 | def __str__(self): 68 | return SSHException.__str__(self) + ' (allowed_types=%r)' % self.allowed_types 69 | 70 | 71 | class PartialAuthentication (AuthenticationException): 72 | """ 73 | An internal exception thrown in the case of partial authentication. 74 | """ 75 | allowed_types = [] 76 | 77 | def __init__(self, types): 78 | AuthenticationException.__init__(self, 'partial authentication') 79 | self.allowed_types = types 80 | # for unpickling 81 | self.args = (types, ) 82 | 83 | 84 | class ChannelException (SSHException): 85 | """ 86 | Exception raised when an attempt to open a new `.Channel` fails. 87 | 88 | :ivar int code: the error code returned by the server 89 | 90 | .. versionadded:: 1.6 91 | """ 92 | def __init__(self, code, text): 93 | SSHException.__init__(self, text) 94 | self.code = code 95 | # for unpickling 96 | self.args = (code, text, ) 97 | 98 | 99 | class BadHostKeyException (SSHException): 100 | """ 101 | The host key given by the SSH server did not match what we were expecting. 102 | 103 | :ivar str hostname: the hostname of the SSH server 104 | :ivar PKey got_key: the host key presented by the server 105 | :ivar PKey expected_key: the host key expected 106 | 107 | .. versionadded:: 1.6 108 | """ 109 | def __init__(self, hostname, got_key, expected_key): 110 | SSHException.__init__(self, 111 | 'Host key for server %s does not match : got %s expected %s' % ( 112 | hostname, 113 | got_key.get_base64(), 114 | expected_key.get_base64())) 115 | self.hostname = hostname 116 | self.key = got_key 117 | self.expected_key = expected_key 118 | # for unpickling 119 | self.args = (hostname, got_key, expected_key, ) 120 | 121 | 122 | class ProxyCommandFailure (SSHException): 123 | """ 124 | The "ProxyCommand" found in the .ssh/config file returned an error. 125 | 126 | :ivar str command: The command line that is generating this exception. 127 | :ivar str error: The error captured from the proxy command output. 128 | """ 129 | def __init__(self, command, error): 130 | SSHException.__init__(self, 131 | '"ProxyCommand (%s)" returned non-zero exit status: %s' % ( 132 | command, error 133 | ) 134 | ) 135 | self.error = error 136 | # for unpickling 137 | self.args = (command, error, ) 138 | 139 | 140 | class NoValidConnectionsError(socket.error): 141 | """ 142 | Multiple connection attempts were made and no families succeeded. 143 | 144 | This exception class wraps multiple "real" underlying connection errors, 145 | all of which represent failed connection attempts. Because these errors are 146 | not guaranteed to all be of the same error type (i.e. different errno, 147 | `socket.error` subclass, message, etc) we expose a single unified error 148 | message and a ``None`` errno so that instances of this class match most 149 | normal handling of `socket.error` objects. 150 | 151 | To see the wrapped exception objects, access the ``errors`` attribute. 152 | ``errors`` is a dict whose keys are address tuples (e.g. ``('127.0.0.1', 153 | 22)``) and whose values are the exception encountered trying to connect to 154 | that address. 155 | 156 | It is implied/assumed that all the errors given to a single instance of 157 | this class are from connecting to the same hostname + port (and thus that 158 | the differences are in the resolution of the hostname - e.g. IPv4 vs v6). 159 | """ 160 | def __init__(self, errors): 161 | """ 162 | :param dict errors: 163 | The errors dict to store, as described by class docstring. 164 | """ 165 | addrs = errors.keys() 166 | body = ', '.join([x[0] for x in addrs[:-1]]) 167 | tail = addrs[-1][0] 168 | msg = "Unable to connect to port {0} on {1} or {2}" 169 | super(NoValidConnectionsError, self).__init__( 170 | None, # stand-in for errno 171 | msg.format(addrs[0][1], body, tail) 172 | ) 173 | self.errors = errors 174 | -------------------------------------------------------------------------------- /paramiko/win_pageant.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2005 John Arbash-Meinel 2 | # Modified up by: Todd Whiteman 3 | # 4 | # This file is part of paramiko. 5 | # 6 | # Paramiko is free software; you can redistribute it and/or modify it under the 7 | # terms of the GNU Lesser General Public License as published by the Free 8 | # Software Foundation; either version 2.1 of the License, or (at your option) 9 | # any later version. 10 | # 11 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 12 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 13 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 14 | # details. 15 | # 16 | # You should have received a copy of the GNU Lesser General Public License 17 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 18 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 19 | 20 | """ 21 | Functions for communicating with Pageant, the basic windows ssh agent program. 22 | """ 23 | 24 | import array 25 | import ctypes.wintypes 26 | import platform 27 | import struct 28 | from paramiko.util import * 29 | from paramiko.py3compat import b 30 | 31 | try: 32 | import _thread as thread # Python 3.x 33 | except ImportError: 34 | import thread # Python 2.5-2.7 35 | 36 | from . import _winapi 37 | 38 | 39 | _AGENT_COPYDATA_ID = 0x804e50ba 40 | _AGENT_MAX_MSGLEN = 8192 41 | # Note: The WM_COPYDATA value is pulled from win32con, as a workaround 42 | # so we do not have to import this huge library just for this one variable. 43 | win32con_WM_COPYDATA = 74 44 | 45 | 46 | def _get_pageant_window_object(): 47 | return ctypes.windll.user32.FindWindowA(b('Pageant'), b('Pageant')) 48 | 49 | 50 | def can_talk_to_agent(): 51 | """ 52 | Check to see if there is a "Pageant" agent we can talk to. 53 | 54 | This checks both if we have the required libraries (win32all or ctypes) 55 | and if there is a Pageant currently running. 56 | """ 57 | return bool(_get_pageant_window_object()) 58 | 59 | 60 | ULONG_PTR = ctypes.c_uint64 if platform.architecture()[0] == '64bit' else ctypes.c_uint32 61 | 62 | 63 | class COPYDATASTRUCT(ctypes.Structure): 64 | """ 65 | ctypes implementation of 66 | http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010%28v=vs.85%29.aspx 67 | """ 68 | _fields_ = [ 69 | ('num_data', ULONG_PTR), 70 | ('data_size', ctypes.wintypes.DWORD), 71 | ('data_loc', ctypes.c_void_p), 72 | ] 73 | 74 | 75 | def _query_pageant(msg): 76 | """ 77 | Communication with the Pageant process is done through a shared 78 | memory-mapped file. 79 | """ 80 | hwnd = _get_pageant_window_object() 81 | if not hwnd: 82 | # Raise a failure to connect exception, pageant isn't running anymore! 83 | return None 84 | 85 | # create a name for the mmap 86 | map_name = 'PageantRequest%08x' % thread.get_ident() 87 | 88 | pymap = _winapi.MemoryMap(map_name, _AGENT_MAX_MSGLEN, 89 | _winapi.get_security_attributes_for_user(), 90 | ) 91 | with pymap: 92 | pymap.write(msg) 93 | # Create an array buffer containing the mapped filename 94 | char_buffer = array.array("b", b(map_name) + zero_byte) 95 | char_buffer_address, char_buffer_size = char_buffer.buffer_info() 96 | # Create a string to use for the SendMessage function call 97 | cds = COPYDATASTRUCT(_AGENT_COPYDATA_ID, char_buffer_size, 98 | char_buffer_address) 99 | 100 | response = ctypes.windll.user32.SendMessageA(hwnd, 101 | win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds)) 102 | 103 | if response > 0: 104 | pymap.seek(0) 105 | datalen = pymap.read(4) 106 | retlen = struct.unpack('>I', datalen)[0] 107 | return datalen + pymap.read(retlen) 108 | return None 109 | 110 | 111 | class PageantConnection(object): 112 | """ 113 | Mock "connection" to an agent which roughly approximates the behavior of 114 | a unix local-domain socket (as used by Agent). Requests are sent to the 115 | pageant daemon via special Windows magick, and responses are buffered back 116 | for subsequent reads. 117 | """ 118 | 119 | def __init__(self): 120 | self._response = None 121 | 122 | def send(self, data): 123 | self._response = _query_pageant(data) 124 | 125 | def recv(self, n): 126 | if self._response is None: 127 | return '' 128 | ret = self._response[:n] 129 | self._response = self._response[n:] 130 | if self._response == '': 131 | self._response = None 132 | return ret 133 | 134 | def close(self): 135 | pass 136 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2008 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA. 18 | 19 | 20 | longdesc = ''' 21 | This is a library for making SSH2 connections (client or server). 22 | Emphasis is on using SSH2 as an alternative to SSL for making secure 23 | connections between python scripts. All major ciphers and hash methods 24 | are supported. SFTP client and server mode are both supported too. 25 | 26 | Required packages: 27 | pyCrypto 28 | 29 | To install the `in-development version 30 | `_, use 31 | `pip install paramiko==dev`. 32 | ''' 33 | 34 | # if someday we want to *require* setuptools, uncomment this: 35 | # (it will cause setuptools to be automatically downloaded) 36 | #import ez_setup 37 | #ez_setup.use_setuptools() 38 | 39 | import sys 40 | try: 41 | from setuptools import setup 42 | kw = { 43 | 'install_requires': [ 44 | 'pycrypto >= 2.1, != 2.4', 45 | 'ecdsa >= 0.11', 46 | ], 47 | } 48 | except ImportError: 49 | from distutils.core import setup 50 | kw = {} 51 | 52 | if sys.platform == 'darwin': 53 | import setup_helper 54 | setup_helper.install_custom_make_tarball() 55 | 56 | 57 | # Version info -- read without importing 58 | _locals = {} 59 | with open('paramiko/_version.py') as fp: 60 | exec(fp.read(), None, _locals) 61 | version = _locals['__version__'] 62 | 63 | 64 | setup( 65 | name = "paramiko", 66 | version = version, 67 | description = "SSH2 protocol library", 68 | long_description = longdesc, 69 | author = "Jeff Forcier", 70 | author_email = "jeff@bitprophet.org", 71 | url = "https://github.com/paramiko/paramiko/", 72 | packages = [ 'paramiko' ], 73 | license = 'LGPL', 74 | platforms = 'Posix; MacOS X; Windows', 75 | classifiers = [ 76 | 'Development Status :: 5 - Production/Stable', 77 | 'Intended Audience :: Developers', 78 | 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 79 | 'Operating System :: OS Independent', 80 | 'Topic :: Internet', 81 | 'Topic :: Security :: Cryptography', 82 | 'Programming Language :: Python', 83 | 'Programming Language :: Python :: 2', 84 | 'Programming Language :: Python :: 2.6', 85 | 'Programming Language :: Python :: 2.7', 86 | 'Programming Language :: Python :: 3', 87 | 'Programming Language :: Python :: 3.2', 88 | 'Programming Language :: Python :: 3.3', 89 | 'Programming Language :: Python :: 3.4', 90 | 'Programming Language :: Python :: 3.5', 91 | ], 92 | **kw 93 | ) 94 | -------------------------------------------------------------------------------- /setup_helper.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | # Note: Despite the copyright notice, this was submitted by John 20 | # Arbash Meinel. Thanks John! 21 | 22 | 23 | """A small set of helper functions for dealing with setup issues""" 24 | 25 | import os 26 | import tarfile 27 | 28 | from distutils import log 29 | import distutils.archive_util 30 | from distutils.dir_util import mkpath 31 | from distutils.spawn import spawn 32 | 33 | try: 34 | from pwd import getpwnam 35 | except ImportError: 36 | getpwnam = None 37 | 38 | try: 39 | from grp import getgrnam 40 | except ImportError: 41 | getgrnam = None 42 | 43 | def _get_gid(name): 44 | """Returns a gid, given a group name.""" 45 | if getgrnam is None or name is None: 46 | return None 47 | try: 48 | result = getgrnam(name) 49 | except KeyError: 50 | result = None 51 | if result is not None: 52 | return result[2] 53 | return None 54 | 55 | def _get_uid(name): 56 | """Returns an uid, given a user name.""" 57 | if getpwnam is None or name is None: 58 | return None 59 | try: 60 | result = getpwnam(name) 61 | except KeyError: 62 | result = None 63 | if result is not None: 64 | return result[2] 65 | return None 66 | 67 | def make_tarball(base_name, base_dir, compress='gzip', verbose=0, dry_run=0, 68 | owner=None, group=None): 69 | """Create a tar file from all the files under 'base_dir'. 70 | This file may be compressed. 71 | 72 | :param compress: Compression algorithms. Supported algorithms are: 73 | 'gzip': (the default) 74 | 'compress' 75 | 'bzip2' 76 | None 77 | For 'gzip' and 'bzip2' the internal tarfile module will be used. 78 | For 'compress' the .tar will be created using tarfile, and then 79 | we will spawn 'compress' afterwards. 80 | The output tar file will be named 'base_name' + ".tar", 81 | possibly plus the appropriate compression extension (".gz", 82 | ".bz2" or ".Z"). Return the output filename. 83 | """ 84 | # XXX GNU tar 1.13 has a nifty option to add a prefix directory. 85 | # It's pretty new, though, so we certainly can't require it -- 86 | # but it would be nice to take advantage of it to skip the 87 | # "create a tree of hardlinks" step! (Would also be nice to 88 | # detect GNU tar to use its 'z' option and save a step.) 89 | 90 | compress_ext = { 'gzip': ".gz", 91 | 'bzip2': '.bz2', 92 | 'compress': ".Z" } 93 | 94 | # flags for compression program, each element of list will be an argument 95 | tarfile_compress_flag = {'gzip':'gz', 'bzip2':'bz2'} 96 | compress_flags = {'compress': ["-f"]} 97 | 98 | if compress is not None and compress not in compress_ext.keys(): 99 | raise ValueError("bad value for 'compress': must be None, 'gzip'," 100 | "'bzip2' or 'compress'") 101 | 102 | archive_name = base_name + ".tar" 103 | if compress and compress in tarfile_compress_flag: 104 | archive_name += compress_ext[compress] 105 | 106 | mode = 'w:' + tarfile_compress_flag.get(compress, '') 107 | 108 | mkpath(os.path.dirname(archive_name), dry_run=dry_run) 109 | log.info('Creating tar file %s with mode %s' % (archive_name, mode)) 110 | 111 | uid = _get_uid(owner) 112 | gid = _get_gid(group) 113 | 114 | def _set_uid_gid(tarinfo): 115 | if gid is not None: 116 | tarinfo.gid = gid 117 | tarinfo.gname = group 118 | if uid is not None: 119 | tarinfo.uid = uid 120 | tarinfo.uname = owner 121 | return tarinfo 122 | 123 | if not dry_run: 124 | tar = tarfile.open(archive_name, mode=mode) 125 | # This recursively adds everything underneath base_dir 126 | try: 127 | try: 128 | # Support for the `filter' parameter was added in Python 2.7, 129 | # earlier versions will raise TypeError. 130 | tar.add(base_dir, filter=_set_uid_gid) 131 | except TypeError: 132 | tar.add(base_dir) 133 | finally: 134 | tar.close() 135 | 136 | if compress and compress not in tarfile_compress_flag: 137 | spawn([compress] + compress_flags[compress] + [archive_name], 138 | dry_run=dry_run) 139 | return archive_name + compress_ext[compress] 140 | else: 141 | return archive_name 142 | 143 | 144 | _custom_formats = { 145 | 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), 146 | 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), 147 | 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), 148 | 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), 149 | } 150 | 151 | # Hack in and insert ourselves into the distutils code base 152 | def install_custom_make_tarball(): 153 | distutils.archive_util.ARCHIVE_FORMATS.update(_custom_formats) 154 | 155 | -------------------------------------------------------------------------------- /sites/docs/api/agent.rst: -------------------------------------------------------------------------------- 1 | SSH agents 2 | ========== 3 | 4 | .. automodule:: paramiko.agent 5 | :inherited-members: 6 | :no-special-members: 7 | -------------------------------------------------------------------------------- /sites/docs/api/buffered_pipe.rst: -------------------------------------------------------------------------------- 1 | Buffered pipes 2 | ============== 3 | 4 | .. automodule:: paramiko.buffered_pipe 5 | -------------------------------------------------------------------------------- /sites/docs/api/channel.rst: -------------------------------------------------------------------------------- 1 | Channel 2 | ======= 3 | 4 | .. automodule:: paramiko.channel 5 | -------------------------------------------------------------------------------- /sites/docs/api/client.rst: -------------------------------------------------------------------------------- 1 | Client 2 | ====== 3 | 4 | .. automodule:: paramiko.client 5 | :member-order: bysource 6 | -------------------------------------------------------------------------------- /sites/docs/api/config.rst: -------------------------------------------------------------------------------- 1 | Configuration 2 | ============= 3 | 4 | .. automodule:: paramiko.config 5 | :member-order: bysource 6 | -------------------------------------------------------------------------------- /sites/docs/api/file.rst: -------------------------------------------------------------------------------- 1 | Buffered files 2 | ============== 3 | 4 | .. automodule:: paramiko.file 5 | -------------------------------------------------------------------------------- /sites/docs/api/hostkeys.rst: -------------------------------------------------------------------------------- 1 | Host keys / ``known_hosts`` files 2 | ================================= 3 | 4 | .. automodule:: paramiko.hostkeys 5 | :member-order: bysource 6 | -------------------------------------------------------------------------------- /sites/docs/api/kex_gss.rst: -------------------------------------------------------------------------------- 1 | GSS-API key exchange 2 | ==================== 3 | 4 | .. automodule:: paramiko.kex_gss 5 | :member-order: bysource 6 | -------------------------------------------------------------------------------- /sites/docs/api/keys.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Key handling 3 | ============ 4 | 5 | Parent key class 6 | ================ 7 | 8 | .. automodule:: paramiko.pkey 9 | 10 | DSA (DSS) 11 | ========= 12 | 13 | .. automodule:: paramiko.dsskey 14 | 15 | RSA 16 | === 17 | 18 | .. automodule:: paramiko.rsakey 19 | 20 | ECDSA 21 | ===== 22 | 23 | .. automodule:: paramiko.ecdsakey 24 | -------------------------------------------------------------------------------- /sites/docs/api/message.rst: -------------------------------------------------------------------------------- 1 | Message 2 | ======= 3 | 4 | .. automodule:: paramiko.message 5 | -------------------------------------------------------------------------------- /sites/docs/api/packet.rst: -------------------------------------------------------------------------------- 1 | Packetizer 2 | ========== 3 | 4 | .. automodule:: paramiko.packet 5 | -------------------------------------------------------------------------------- /sites/docs/api/pipe.rst: -------------------------------------------------------------------------------- 1 | Cross-platform pipe implementations 2 | =================================== 3 | 4 | .. automodule:: paramiko.pipe 5 | -------------------------------------------------------------------------------- /sites/docs/api/proxy.rst: -------------------------------------------------------------------------------- 1 | ``ProxyCommand`` support 2 | ======================== 3 | 4 | .. automodule:: paramiko.proxy 5 | -------------------------------------------------------------------------------- /sites/docs/api/server.rst: -------------------------------------------------------------------------------- 1 | Server implementation 2 | ===================== 3 | 4 | .. automodule:: paramiko.server 5 | :member-order: bysource 6 | -------------------------------------------------------------------------------- /sites/docs/api/sftp.rst: -------------------------------------------------------------------------------- 1 | SFTP 2 | ==== 3 | 4 | .. automodule:: paramiko.sftp 5 | .. automodule:: paramiko.sftp_client 6 | .. automodule:: paramiko.sftp_server 7 | .. automodule:: paramiko.sftp_attr 8 | .. automodule:: paramiko.sftp_file 9 | :inherited-members: 10 | :no-special-members: 11 | :show-inheritance: 12 | .. automodule:: paramiko.sftp_handle 13 | .. automodule:: paramiko.sftp_si 14 | -------------------------------------------------------------------------------- /sites/docs/api/ssh_exception.rst: -------------------------------------------------------------------------------- 1 | Exceptions 2 | ========== 3 | 4 | .. automodule:: paramiko.ssh_exception 5 | -------------------------------------------------------------------------------- /sites/docs/api/ssh_gss.rst: -------------------------------------------------------------------------------- 1 | GSS-API authentication 2 | ====================== 3 | 4 | .. automodule:: paramiko.ssh_gss 5 | :member-order: bysource 6 | 7 | .. autoclass:: _SSH_GSSAuth 8 | :member-order: bysource 9 | 10 | .. autoclass:: _SSH_GSSAPI 11 | :member-order: bysource 12 | 13 | .. autoclass:: _SSH_SSPI 14 | :member-order: bysource 15 | -------------------------------------------------------------------------------- /sites/docs/api/transport.rst: -------------------------------------------------------------------------------- 1 | Transport 2 | ========= 3 | 4 | .. automodule:: paramiko.transport 5 | :member-order: bysource 6 | -------------------------------------------------------------------------------- /sites/docs/conf.py: -------------------------------------------------------------------------------- 1 | # Obtain shared config values 2 | import os, sys 3 | sys.path.append(os.path.abspath('..')) 4 | sys.path.append(os.path.abspath('../..')) 5 | from shared_conf import * 6 | 7 | # Enable autodoc, intersphinx 8 | extensions.extend(['sphinx.ext.autodoc']) 9 | 10 | # Autodoc settings 11 | autodoc_default_flags = ['members', 'special-members'] 12 | 13 | # Sister-site links to WWW 14 | html_theme_options['extra_nav_links'] = { 15 | "Main website": 'http://www.paramiko.org', 16 | } 17 | -------------------------------------------------------------------------------- /sites/docs/index.rst: -------------------------------------------------------------------------------- 1 | ==================================== 2 | Welcome to Paramiko's documentation! 3 | ==================================== 4 | 5 | This site covers Paramiko's usage & API documentation. For basic info on what 6 | Paramiko is, including its public changelog & how the project is maintained, 7 | please see `the main project website `_. 8 | 9 | 10 | API documentation 11 | ================= 12 | 13 | The high-level client API starts with creation of an `.SSHClient` object. For 14 | more direct control, pass a socket (or socket-like object) to a `.Transport`, 15 | and use `start_server <.Transport.start_server>` or `start_client 16 | <.Transport.start_client>` to negotiate with the remote host as either a server 17 | or client. 18 | 19 | As a client, you are responsible for authenticating using a password or private 20 | key, and checking the server's host key. (Key signature and verification is 21 | done by paramiko, but you will need to provide private keys and check that the 22 | content of a public key matches what you expected to see.) 23 | 24 | As a server, you are responsible for deciding which users, passwords, and keys 25 | to allow, and what kind of channels to allow. 26 | 27 | Once you have finished, either side may request flow-controlled `channels 28 | <.Channel>` to the other side, which are Python objects that act like sockets, 29 | but send and receive data over the encrypted session. 30 | 31 | For details, please see the following tables of contents (which are organized 32 | by area of interest.) 33 | 34 | 35 | Core SSH protocol classes 36 | ------------------------- 37 | 38 | .. toctree:: 39 | api/channel 40 | api/client 41 | api/message 42 | api/packet 43 | api/transport 44 | 45 | 46 | Authentication & keys 47 | --------------------- 48 | 49 | .. toctree:: 50 | api/agent 51 | api/hostkeys 52 | api/keys 53 | api/ssh_gss 54 | api/kex_gss 55 | 56 | 57 | Other primary functions 58 | ----------------------- 59 | 60 | .. toctree:: 61 | api/config 62 | api/proxy 63 | api/server 64 | api/sftp 65 | 66 | 67 | Miscellany 68 | ---------- 69 | 70 | .. toctree:: 71 | api/buffered_pipe 72 | api/file 73 | api/pipe 74 | api/ssh_exception 75 | -------------------------------------------------------------------------------- /sites/shared_conf.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | import alabaster 4 | 5 | 6 | # Alabaster theme + mini-extension 7 | html_theme_path = [alabaster.get_path()] 8 | extensions = ['alabaster', 'sphinx.ext.intersphinx'] 9 | # Paths relative to invoking conf.py - not this shared file 10 | html_theme = 'alabaster' 11 | html_theme_options = { 12 | 'description': "A Python implementation of SSHv2.", 13 | 'github_user': 'paramiko', 14 | 'github_repo': 'paramiko', 15 | 'analytics_id': 'UA-18486793-2', 16 | 'travis_button': True, 17 | } 18 | html_sidebars = { 19 | '**': [ 20 | 'about.html', 21 | 'navigation.html', 22 | 'searchbox.html', 23 | 'donate.html', 24 | ] 25 | } 26 | 27 | # Everything intersphinx's to Python 28 | intersphinx_mapping = { 29 | 'python': ('http://docs.python.org/2.6', None), 30 | } 31 | 32 | # Regular settings 33 | project = 'Paramiko' 34 | year = datetime.now().year 35 | copyright = '%d Jeff Forcier' % year 36 | master_doc = 'index' 37 | templates_path = ['_templates'] 38 | exclude_trees = ['_build'] 39 | source_suffix = '.rst' 40 | default_role = 'obj' 41 | -------------------------------------------------------------------------------- /sites/www/_templates/rss.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ title }} 6 | {{ link }} 7 | {{ description }} 8 | {{ date }} 9 | {% for link, title, desc, date in posts %} 10 | 11 | {{ link }} 12 | {{ link }} 13 | <![CDATA[{{ title }}]]> 14 | 15 | {{ date }} 16 | 17 | {% endfor %} 18 | 19 | 20 | -------------------------------------------------------------------------------- /sites/www/conf.py: -------------------------------------------------------------------------------- 1 | # Obtain shared config values 2 | import sys 3 | import os 4 | from os.path import abspath, join, dirname 5 | 6 | sys.path.append(abspath(join(dirname(__file__), '..'))) 7 | from shared_conf import * 8 | 9 | # Releases changelog extension 10 | extensions.append('releases') 11 | # Paramiko 1.x tags start with 'v'. Meh. 12 | releases_release_uri = "https://github.com/paramiko/paramiko/tree/v%s" 13 | releases_issue_uri = "https://github.com/paramiko/paramiko/issues/%s" 14 | 15 | # Default is 'local' building, but reference the public docs site when building 16 | # under RTD. 17 | target = join(dirname(__file__), '..', 'docs', '_build') 18 | if os.environ.get('READTHEDOCS') == 'True': 19 | target = 'http://docs.paramiko.org/en/latest/' 20 | intersphinx_mapping['docs'] = (target, None) 21 | 22 | # Sister-site links to API docs 23 | html_theme_options['extra_nav_links'] = { 24 | "API Docs": 'http://docs.paramiko.org', 25 | } 26 | -------------------------------------------------------------------------------- /sites/www/contact.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Contact 3 | ======= 4 | 5 | You can get in touch with the developer & user community in any of the 6 | following ways: 7 | 8 | * IRC: ``#paramiko`` on Freenode 9 | * Mailing list: ``paramiko@librelist.com`` (see `the LibreList homepage 10 | `_ for usage details). 11 | * This website - a blog section is forthcoming. 12 | * Submit contributions on Github - see the :doc:`contributing` page. 13 | -------------------------------------------------------------------------------- /sites/www/contributing.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Contributing 3 | ============ 4 | 5 | How to get the code 6 | =================== 7 | 8 | Our primary Git repository is on Github at `paramiko/paramiko`_; 9 | please follow their instructions for cloning to your local system. (If you 10 | intend to submit patches/pull requests, we recommend forking first, then 11 | cloning your fork. Github has excellent documentation for all this.) 12 | 13 | 14 | How to submit bug reports or new code 15 | ===================================== 16 | 17 | Please see `this project-agnostic contribution guide 18 | `_ - we follow it explicitly. Again, our code 19 | repository and bug tracker is `on Github`_. 20 | 21 | Our current changelog is located in ``sites/www/changelog.rst`` - the top 22 | level files like ``ChangeLog.*`` and ``NEWS`` are historical only. 23 | 24 | 25 | .. _paramiko/paramiko: 26 | .. _on Github: https://github.com/paramiko/paramiko 27 | -------------------------------------------------------------------------------- /sites/www/faq.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Frequently Asked/Answered Questions 3 | =================================== 4 | 5 | Which version should I use? I see multiple active releases. 6 | =========================================================== 7 | 8 | Please see :ref:`the installation docs ` which have an explicit 9 | section about this topic. 10 | 11 | Paramiko doesn't work with my Cisco, Windows or other non-Unix system! 12 | ====================================================================== 13 | 14 | In an ideal world, the developers would love to support every possible target 15 | system. Unfortunately, volunteer development time and access to non-mainstream 16 | platforms are limited, meaning that we can only fully support standard OpenSSH 17 | implementations such as those found on the average Linux distribution (as well 18 | as on Mac OS X and \*BSD.) 19 | 20 | Because of this, **we typically close bug reports for nonstandard SSH 21 | implementations or host systems**. 22 | 23 | However, **closed does not imply locked** - affected users can still post 24 | comments on such tickets - and **we will always consider actual patch 25 | submissions for these issues**, provided they can get +1s from similarly 26 | affected users and are proven to not break existing functionality. 27 | -------------------------------------------------------------------------------- /sites/www/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to Paramiko! 2 | ==================== 3 | 4 | Paramiko is a Python (2.6+, 3.3+) implementation of the SSHv2 protocol [#]_, 5 | providing both client and server functionality. While it leverages a Python C 6 | extension for low level cryptography (`PyCrypto `_), 7 | Paramiko itself is a pure Python interface around SSH networking concepts. 8 | 9 | This website covers project information for Paramiko such as the changelog, 10 | contribution guidelines, development roadmap, news/blog, and so forth. Detailed 11 | usage and API documentation can be found at our code documentation site, 12 | `docs.paramiko.org `_. 13 | 14 | Please see the sidebar to the left to begin. 15 | 16 | .. toctree:: 17 | :hidden: 18 | 19 | changelog 20 | FAQs 21 | installing 22 | contributing 23 | contact 24 | 25 | 26 | .. rubric:: Footnotes 27 | 28 | .. [#] 29 | SSH is defined in :rfc:`4251`, :rfc:`4252`, :rfc:`4253` and :rfc:`4254`. The 30 | primary working implementation of the protocol is the `OpenSSH project 31 | `_. Paramiko implements a large portion of the SSH 32 | feature set, but there are occasional gaps. 33 | -------------------------------------------------------------------------------- /sites/www/installing.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | Installing 3 | ========== 4 | 5 | .. _paramiko-itself: 6 | 7 | Paramiko itself 8 | =============== 9 | 10 | The recommended way to get Paramiko is to **install the latest stable release** 11 | via `pip `_:: 12 | 13 | $ pip install paramiko 14 | 15 | .. note:: 16 | Users who want the bleeding edge can install the development version via 17 | ``pip install paramiko==dev``. 18 | 19 | We currently support **Python 2.6, 2.7 and 3.3+** (Python **3.2** should also 20 | work but has a less-strong compatibility guarantee from us.) Users on Python 21 | 2.5 or older are urged to upgrade. 22 | 23 | Paramiko has two hard dependencies: the pure-Python ECDSA module ``ecdsa``, and the 24 | PyCrypto C extension. ``ecdsa`` is easily installable from wherever you 25 | obtained Paramiko's package; PyCrypto may require more work. Read on for 26 | details. 27 | 28 | If you need GSS-API / SSPI support, see :ref:`the below subsection on it 29 | ` for details on additional dependencies. 30 | 31 | .. _release-lines: 32 | 33 | Release lines 34 | ------------- 35 | 36 | Users desiring stability may wish to pin themselves to a specific release line 37 | once they first start using Paramiko; to assist in this, we guarantee bugfixes 38 | for the last 2-3 releases including the latest stable one. 39 | 40 | If you're unsure which version to install, we have suggestions: 41 | 42 | * **Completely new users** should always default to the **latest stable 43 | release** (as above, whatever is newest / whatever shows up with ``pip 44 | install paramiko``.) 45 | * **Users upgrading from a much older version** (e.g. the 1.7.x line) should 46 | probably get the **oldest actively supported line** (see the paragraph above 47 | this list for what that currently is.) 48 | * **Everybody else** is hopefully already "on" a given version and can 49 | carefully upgrade to whichever version they care to, when their release line 50 | stops being supported. 51 | 52 | 53 | PyCrypto 54 | ======== 55 | 56 | `PyCrypto `_ provides the low-level 57 | (C-based) encryption algorithms we need to implement the SSH protocol. There 58 | are a couple gotchas associated with installing PyCrypto: its compatibility 59 | with Python's package tools, and the fact that it is a C-based extension. 60 | 61 | C extension 62 | ----------- 63 | 64 | Unless you are installing from a precompiled source such as a Debian apt 65 | repository or RedHat RPM, or using :ref:`pypm `, you will also need the 66 | ability to build Python C-based modules from source in order to install 67 | PyCrypto. Users on **Unix-based platforms** such as Ubuntu or Mac OS X will 68 | need the traditional C build toolchain installed (e.g. Developer Tools / XCode 69 | Tools on the Mac, or the ``build-essential`` package on Ubuntu or Debian Linux 70 | -- basically, anything with ``gcc``, ``make`` and so forth) as well as the 71 | Python development libraries, often named ``python-dev`` or similar. 72 | 73 | For **Windows** users we recommend using :ref:`pypm`, installing a C 74 | development environment such as `Cygwin `_ or obtaining a 75 | precompiled Win32 PyCrypto package from `voidspace's Python modules page 76 | `_. 77 | 78 | .. note:: 79 | Some Windows users whose Python is 64-bit have found that the PyCrypto 80 | dependency ``winrandom`` may not install properly, leading to ImportErrors. 81 | In this scenario, you'll probably need to compile ``winrandom`` yourself 82 | via e.g. MS Visual Studio. See `Fabric #194 83 | `_ for info. 84 | 85 | 86 | .. _pypm: 87 | 88 | ActivePython and PyPM 89 | ===================== 90 | 91 | Windows users who already have ActiveState's `ActivePython 92 | `_ distribution installed 93 | may find Paramiko is best installed with `its package manager, PyPM 94 | `_. Below is example output from an 95 | installation of Paramiko via ``pypm``:: 96 | 97 | C:\> pypm install paramiko 98 | The following packages will be installed into "%APPDATA%\Python" (2.7): 99 | paramiko-1.7.8 pycrypto-2.4 100 | Get: [pypm-free.activestate.com] paramiko 1.7.8 101 | Get: [pypm-free.activestate.com] pycrypto 2.4 102 | Installing paramiko-1.7.8 103 | Installing pycrypto-2.4 104 | C:\> 105 | 106 | 107 | .. _gssapi: 108 | 109 | Optional dependencies for GSS-API / SSPI / Kerberos 110 | =================================================== 111 | 112 | In order to use GSS-API/Kerberos & related functionality, a couple of 113 | additional dependencies are required (these are not listed in our ``setup.py`` 114 | due to their infrequent utility & non-platform-agnostic requirements): 115 | 116 | * It hopefully goes without saying but **all platforms** need **a working 117 | installation of GSS-API itself**, e.g. Heimdal. 118 | * **All platforms** need `pyasn1 `_ 119 | ``0.1.7`` or better. 120 | * **Unix** needs `python-gssapi `_ 121 | ``0.6.1`` or better. 122 | 123 | .. note:: This library appears to only function on Python 2.7 and up. 124 | 125 | * **Windows** needs `pywin32 `_ ``2.1.8`` 126 | or better. 127 | 128 | .. note:: 129 | If you use Microsoft SSPI for kerberos authentication and credential 130 | delegation, make sure that the target host is trusted for delegation in the 131 | active directory configuration. For details see: 132 | http://technet.microsoft.com/en-us/library/cc738491%28v=ws.10%29.aspx 133 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | from os import mkdir 2 | from os.path import join 3 | from shutil import rmtree, copytree 4 | 5 | from invoke import Collection, ctask as task 6 | from invocations.docs import docs, www, sites 7 | from invocations.packaging import publish 8 | 9 | 10 | # Until we move to spec-based testing 11 | @task 12 | def test(ctx, coverage=False, flags=""): 13 | if "--verbose" not in flags.split(): 14 | flags += " --verbose" 15 | runner = "python" 16 | if coverage: 17 | runner = "coverage run --source=paramiko" 18 | ctx.run("{0} test.py {1}".format(runner, flags), pty=True) 19 | 20 | 21 | @task 22 | def coverage(ctx): 23 | ctx.run("coverage run --source=paramiko test.py --verbose") 24 | 25 | 26 | # Until we stop bundling docs w/ releases. Need to discover use cases first. 27 | @task 28 | def release(ctx, sdist=True, wheel=True): 29 | """ 30 | Wraps invocations.packaging.release to add baked-in docs folder. 31 | """ 32 | # Build docs first. Use terribad workaround pending invoke #146 33 | ctx.run("inv docs") 34 | # Move the built docs into where Epydocs used to live 35 | target = 'docs' 36 | rmtree(target, ignore_errors=True) 37 | # TODO: make it easier to yank out this config val from the docs coll 38 | copytree('sites/docs/_build', target) 39 | # Publish 40 | publish(ctx, sdist=sdist, wheel=wheel) 41 | # Remind 42 | print("\n\nDon't forget to update RTD's versions page for new minor releases!") 43 | 44 | 45 | ns = Collection(test, coverage, release, docs, www, sites) 46 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tintinweb/paramiko-sshfuzz/ebc5715a4d61b05964b3ddecbe37a3996fb3f84f/tests/__init__.py -------------------------------------------------------------------------------- /tests/loop.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2009 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | ... 21 | """ 22 | 23 | import threading, socket 24 | from paramiko.common import asbytes 25 | 26 | 27 | class LoopSocket (object): 28 | """ 29 | A LoopSocket looks like a normal socket, but all data written to it is 30 | delivered on the read-end of another LoopSocket, and vice versa. It's 31 | like a software "socketpair". 32 | """ 33 | 34 | def __init__(self): 35 | self.__in_buffer = bytes() 36 | self.__lock = threading.Lock() 37 | self.__cv = threading.Condition(self.__lock) 38 | self.__timeout = None 39 | self.__mate = None 40 | 41 | def close(self): 42 | self.__unlink() 43 | try: 44 | self.__lock.acquire() 45 | self.__in_buffer = bytes() 46 | finally: 47 | self.__lock.release() 48 | 49 | def send(self, data): 50 | data = asbytes(data) 51 | if self.__mate is None: 52 | # EOF 53 | raise EOFError() 54 | self.__mate.__feed(data) 55 | return len(data) 56 | 57 | def recv(self, n): 58 | self.__lock.acquire() 59 | try: 60 | if self.__mate is None: 61 | # EOF 62 | return bytes() 63 | if len(self.__in_buffer) == 0: 64 | self.__cv.wait(self.__timeout) 65 | if len(self.__in_buffer) == 0: 66 | raise socket.timeout 67 | out = self.__in_buffer[:n] 68 | self.__in_buffer = self.__in_buffer[n:] 69 | return out 70 | finally: 71 | self.__lock.release() 72 | 73 | def settimeout(self, n): 74 | self.__timeout = n 75 | 76 | def link(self, other): 77 | self.__mate = other 78 | self.__mate.__mate = self 79 | 80 | def __feed(self, data): 81 | self.__lock.acquire() 82 | try: 83 | self.__in_buffer += data 84 | self.__cv.notifyAll() 85 | finally: 86 | self.__lock.release() 87 | 88 | def __unlink(self): 89 | m = None 90 | self.__lock.acquire() 91 | try: 92 | if self.__mate is not None: 93 | m = self.__mate 94 | self.__mate = None 95 | finally: 96 | self.__lock.release() 97 | if m is not None: 98 | m.__unlink() 99 | 100 | 101 | -------------------------------------------------------------------------------- /tests/test_buffered_pipe.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2006-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Some unit tests for BufferedPipe. 21 | """ 22 | 23 | import threading 24 | import time 25 | import unittest 26 | from paramiko.buffered_pipe import BufferedPipe, PipeTimeout 27 | from paramiko import pipe 28 | from paramiko.py3compat import b 29 | 30 | 31 | def delay_thread(p): 32 | p.feed('a') 33 | time.sleep(0.5) 34 | p.feed('b') 35 | p.close() 36 | 37 | 38 | def close_thread(p): 39 | time.sleep(0.2) 40 | p.close() 41 | 42 | 43 | class BufferedPipeTest(unittest.TestCase): 44 | def test_1_buffered_pipe(self): 45 | p = BufferedPipe() 46 | self.assertTrue(not p.read_ready()) 47 | p.feed('hello.') 48 | self.assertTrue(p.read_ready()) 49 | data = p.read(6) 50 | self.assertEqual(b'hello.', data) 51 | 52 | p.feed('plus/minus') 53 | self.assertEqual(b'plu', p.read(3)) 54 | self.assertEqual(b's/m', p.read(3)) 55 | self.assertEqual(b'inus', p.read(4)) 56 | 57 | p.close() 58 | self.assertTrue(not p.read_ready()) 59 | self.assertEqual(b'', p.read(1)) 60 | 61 | def test_2_delay(self): 62 | p = BufferedPipe() 63 | self.assertTrue(not p.read_ready()) 64 | threading.Thread(target=delay_thread, args=(p,)).start() 65 | self.assertEqual(b'a', p.read(1, 0.1)) 66 | try: 67 | p.read(1, 0.1) 68 | self.assertTrue(False) 69 | except PipeTimeout: 70 | pass 71 | self.assertEqual(b'b', p.read(1, 1.0)) 72 | self.assertEqual(b'', p.read(1)) 73 | 74 | def test_3_close_while_reading(self): 75 | p = BufferedPipe() 76 | threading.Thread(target=close_thread, args=(p,)).start() 77 | data = p.read(1, 1.0) 78 | self.assertEqual(b'', data) 79 | 80 | def test_4_or_pipe(self): 81 | p = pipe.make_pipe() 82 | p1, p2 = pipe.make_or_pipe(p) 83 | self.assertFalse(p._set) 84 | p1.set() 85 | self.assertTrue(p._set) 86 | p2.set() 87 | self.assertTrue(p._set) 88 | p1.clear() 89 | self.assertTrue(p._set) 90 | p2.clear() 91 | self.assertFalse(p._set) 92 | -------------------------------------------------------------------------------- /tests/test_dss.key: -------------------------------------------------------------------------------- 1 | -----BEGIN DSA PRIVATE KEY----- 2 | MIIBuwIBAAKBgQDngaYDZ30c6/7cJgEEbtl8FgKdwhba1Z7oOrOn4MI/6C42G1bY 3 | wMuqZf4dBCglsdq39SHrcjbE8Vq54gPSOh3g4+uV9Rcg5IOoPLbwp2jQfF6f1FIb 4 | sx7hrDCIqUcQccPSxetPBKmXI9RN8rZLaFuQeTnI65BKM98Ruwvq6SI2LwIVAPDP 5 | hSeawaJI27mKqOfe5PPBSmyHAoGBAJMXxXmPD9sGaQ419DIpmZecJKBUAy9uXD8x 6 | gbgeDpwfDaFJP8owByCKREocPFfi86LjCuQkyUKOfjYMN6iHIf1oEZjB8uJAatUr 7 | FzI0ArXtUqOhwTLwTyFuUojE5own2WYsOAGByvgfyWjsGhvckYNhI4ODpNdPlxQ8 8 | ZamaPGPsAoGARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lYukmn 9 | jO1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+NwacI 10 | BlXa8cMDL7Q/69o0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgECFGI9QPSc 11 | h9pT9XHqn+1rZ4bK+QGA 12 | -----END DSA PRIVATE KEY----- 13 | -------------------------------------------------------------------------------- /tests/test_dss_password.key: -------------------------------------------------------------------------------- 1 | -----BEGIN DSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,78DAEB836ED0A646 4 | 5 | ldWkq9OMlXqWmjIqppNnmNPIUj5uVT12LkBosTApTbibTme3kIJb1uDeG2BShVfY 6 | +vDOTUE9koGPDLsxW1t5At+EVyIDK8aIO0uHteXM5AbBX20LLUWRbRVqZhsMxqQh 7 | 3H3XlHiN+QhaWcb4fFuu18a8SkimTFpDnZuffoCDl/zh/B7XieARTLA805K/ZgVB 8 | BBwflkR2BE053XHrJAIx9BEUlLP76Fo18rvjLZOSeu3s+VnnhqUb5FCt5h50a46u 9 | YXQBbo2r9Zo1ilGMNEXJO0gk5hwGVmTySz53NkPA5HmWt8NIzv5jQHMDy7N+ZykF 10 | uwpP1R5M/ZIFY4Y5h/lvn6IJjQ7VySRPIbpN8o2YJv2OD1Ja80n3tU8Mg77o3o4d 11 | NwKm7cCjlq+FuIBdOsSgsB8FPQRUhW+jpFDxmWN64DM2cEg6RUdptby7WmMp0HwK 12 | 1qyEfxHjLMuDVlD7lASIDBrRlUjPtXEH1DzIYQuYaRZaixFoZ7EY+X73TwmrKFEU 13 | US9ZnQZtRtroRqGwR4fz4wQQsjTl/AmOijlBmi29taJccJsT/THrLQ5plOEd8OMv 14 | 9FsaPJXBU85gaRKo3JZtrw== 15 | -----END DSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /tests/test_ecdsa.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIKB6ty3yVyKEnfF/zprx0qwC76MsMlHY4HXCnqho2eKioAoGCCqGSM49 3 | AwEHoUQDQgAElI9mbdlaS+T9nHxY/59lFnn80EEecZDBHq4gLpccY8Mge5ZTMiMD 4 | ADRvOqQ5R98Sxst765CAqXmRtz8vwoD96g== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /tests/test_ecdsa_password.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: AES-128-CBC,EEB56BC745EDB2DE04FC3FE1F8DA387E 4 | 5 | wdt7QTCa6ahTJLaEPH7NhHyBcxhzrzf93d4UwQOuAhkM6//jKD4lF9fErHBW0f3B 6 | ExberCU3UxfEF3xX2thXiLw47JgeOCeQUlqRFx92p36k6YmfNGX6W8CsZ3d+XodF 7 | Z+pb6m285CiSX+W95NenFMexXFsIpntiCvTifTKJ8os= 8 | -----END EC PRIVATE KEY----- 9 | -------------------------------------------------------------------------------- /tests/test_file.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2009 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Some unit tests for the BufferedFile abstraction. 21 | """ 22 | 23 | import unittest 24 | from paramiko.file import BufferedFile 25 | from paramiko.common import linefeed_byte, crlf, cr_byte 26 | import sys 27 | 28 | 29 | class LoopbackFile (BufferedFile): 30 | """ 31 | BufferedFile object that you can write data into, and then read it back. 32 | """ 33 | def __init__(self, mode='r', bufsize=-1): 34 | BufferedFile.__init__(self) 35 | self._set_mode(mode, bufsize) 36 | self.buffer = bytes() 37 | 38 | def _read(self, size): 39 | if len(self.buffer) == 0: 40 | return None 41 | if size > len(self.buffer): 42 | size = len(self.buffer) 43 | data = self.buffer[:size] 44 | self.buffer = self.buffer[size:] 45 | return data 46 | 47 | def _write(self, data): 48 | self.buffer += data 49 | return len(data) 50 | 51 | 52 | class BufferedFileTest (unittest.TestCase): 53 | 54 | def test_1_simple(self): 55 | f = LoopbackFile('r') 56 | try: 57 | f.write(b'hi') 58 | self.assertTrue(False, 'no exception on write to read-only file') 59 | except: 60 | pass 61 | f.close() 62 | 63 | f = LoopbackFile('w') 64 | try: 65 | f.read(1) 66 | self.assertTrue(False, 'no exception to read from write-only file') 67 | except: 68 | pass 69 | f.close() 70 | 71 | def test_2_readline(self): 72 | f = LoopbackFile('r+U') 73 | f.write(b'First line.\nSecond line.\r\nThird line.\n' + 74 | b'Fourth line.\nFinal line non-terminated.') 75 | 76 | self.assertEqual(f.readline(), 'First line.\n') 77 | # universal newline mode should convert this linefeed: 78 | self.assertEqual(f.readline(), 'Second line.\n') 79 | # truncated line: 80 | self.assertEqual(f.readline(7), 'Third l') 81 | self.assertEqual(f.readline(), 'ine.\n') 82 | # newline should be detected and only the fourth line returned 83 | self.assertEqual(f.readline(39), 'Fourth line.\n') 84 | self.assertEqual(f.readline(), 'Final line non-terminated.') 85 | self.assertEqual(f.readline(), '') 86 | f.close() 87 | try: 88 | f.readline() 89 | self.assertTrue(False, 'no exception on readline of closed file') 90 | except IOError: 91 | pass 92 | self.assertTrue(linefeed_byte in f.newlines) 93 | self.assertTrue(crlf in f.newlines) 94 | self.assertTrue(cr_byte not in f.newlines) 95 | 96 | def test_3_lf(self): 97 | """ 98 | try to trick the linefeed detector. 99 | """ 100 | f = LoopbackFile('r+U') 101 | f.write(b'First line.\r') 102 | self.assertEqual(f.readline(), 'First line.\n') 103 | f.write(b'\nSecond.\r\n') 104 | self.assertEqual(f.readline(), 'Second.\n') 105 | f.close() 106 | self.assertEqual(f.newlines, crlf) 107 | 108 | def test_4_write(self): 109 | """ 110 | verify that write buffering is on. 111 | """ 112 | f = LoopbackFile('r+', 1) 113 | f.write(b'Complete line.\nIncomplete line.') 114 | self.assertEqual(f.readline(), 'Complete line.\n') 115 | self.assertEqual(f.readline(), '') 116 | f.write('..\n') 117 | self.assertEqual(f.readline(), 'Incomplete line...\n') 118 | f.close() 119 | 120 | def test_5_flush(self): 121 | """ 122 | verify that flush will force a write. 123 | """ 124 | f = LoopbackFile('r+', 512) 125 | f.write('Not\nquite\n512 bytes.\n') 126 | self.assertEqual(f.read(1), b'') 127 | f.flush() 128 | self.assertEqual(f.read(5), b'Not\nq') 129 | self.assertEqual(f.read(10), b'uite\n512 b') 130 | self.assertEqual(f.read(9), b'ytes.\n') 131 | self.assertEqual(f.read(3), b'') 132 | f.close() 133 | 134 | def test_6_buffering(self): 135 | """ 136 | verify that flushing happens automatically on buffer crossing. 137 | """ 138 | f = LoopbackFile('r+', 16) 139 | f.write(b'Too small.') 140 | self.assertEqual(f.read(4), b'') 141 | f.write(b' ') 142 | self.assertEqual(f.read(4), b'') 143 | f.write(b'Enough.') 144 | self.assertEqual(f.read(20), b'Too small. Enough.') 145 | f.close() 146 | 147 | def test_7_read_all(self): 148 | """ 149 | verify that read(-1) returns everything left in the file. 150 | """ 151 | f = LoopbackFile('r+', 16) 152 | f.write(b'The first thing you need to do is open your eyes. ') 153 | f.write(b'Then, you need to close them again.\n') 154 | s = f.read(-1) 155 | self.assertEqual(s, b'The first thing you need to do is open your eyes. Then, you ' + 156 | b'need to close them again.\n') 157 | f.close() 158 | 159 | def test_8_buffering(self): 160 | """ 161 | verify that buffered objects can be written 162 | """ 163 | if sys.version_info[0] == 2: 164 | f = LoopbackFile('r+', 16) 165 | f.write(buffer(b'Too small.')) 166 | f.close() 167 | 168 | if __name__ == '__main__': 169 | from unittest import main 170 | main() 171 | 172 | -------------------------------------------------------------------------------- /tests/test_gssapi.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2013-2014 science + computing ag 2 | # Author: Sebastian Deiss 3 | # 4 | # 5 | # This file is part of paramiko. 6 | # 7 | # Paramiko is free software; you can redistribute it and/or modify it under the 8 | # terms of the GNU Lesser General Public License as published by the Free 9 | # Software Foundation; either version 2.1 of the License, or (at your option) 10 | # any later version. 11 | # 12 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 13 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 14 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 15 | # details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public License 18 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 19 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 20 | 21 | """ 22 | Test the used APIs for GSS-API / SSPI authentication 23 | """ 24 | 25 | import unittest 26 | import socket 27 | 28 | 29 | class GSSAPITest(unittest.TestCase): 30 | @staticmethod 31 | def init(hostname=None, srv_mode=False): 32 | global krb5_mech, targ_name, server_mode 33 | krb5_mech = "1.2.840.113554.1.2.2" 34 | targ_name = hostname 35 | server_mode = srv_mode 36 | 37 | def test_1_pyasn1(self): 38 | """ 39 | Test the used methods of pyasn1. 40 | """ 41 | from pyasn1.type.univ import ObjectIdentifier 42 | from pyasn1.codec.der import encoder, decoder 43 | oid = encoder.encode(ObjectIdentifier(krb5_mech)) 44 | mech, __ = decoder.decode(oid) 45 | self.assertEquals(krb5_mech, mech.__str__()) 46 | 47 | def test_2_gssapi_sspi(self): 48 | """ 49 | Test the used methods of python-gssapi or sspi, sspicon from pywin32. 50 | """ 51 | _API = "MIT" 52 | try: 53 | import gssapi 54 | except ImportError: 55 | import sspicon 56 | import sspi 57 | _API = "SSPI" 58 | 59 | c_token = None 60 | gss_ctxt_status = False 61 | mic_msg = b"G'day Mate!" 62 | 63 | if _API == "MIT": 64 | if server_mode: 65 | gss_flags = (gssapi.C_PROT_READY_FLAG, 66 | gssapi.C_INTEG_FLAG, 67 | gssapi.C_MUTUAL_FLAG, 68 | gssapi.C_DELEG_FLAG) 69 | else: 70 | gss_flags = (gssapi.C_PROT_READY_FLAG, 71 | gssapi.C_INTEG_FLAG, 72 | gssapi.C_DELEG_FLAG) 73 | # Initialize a GSS-API context. 74 | ctx = gssapi.Context() 75 | ctx.flags = gss_flags 76 | krb5_oid = gssapi.OID.mech_from_string(krb5_mech) 77 | target_name = gssapi.Name("host@" + targ_name, 78 | gssapi.C_NT_HOSTBASED_SERVICE) 79 | gss_ctxt = gssapi.InitContext(peer_name=target_name, 80 | mech_type=krb5_oid, 81 | req_flags=ctx.flags) 82 | if server_mode: 83 | c_token = gss_ctxt.step(c_token) 84 | gss_ctxt_status = gss_ctxt.established 85 | self.assertEquals(False, gss_ctxt_status) 86 | # Accept a GSS-API context. 87 | gss_srv_ctxt = gssapi.AcceptContext() 88 | s_token = gss_srv_ctxt.step(c_token) 89 | gss_ctxt_status = gss_srv_ctxt.established 90 | self.assertNotEquals(None, s_token) 91 | self.assertEquals(True, gss_ctxt_status) 92 | # Establish the client context 93 | c_token = gss_ctxt.step(s_token) 94 | self.assertEquals(None, c_token) 95 | else: 96 | while not gss_ctxt.established: 97 | c_token = gss_ctxt.step(c_token) 98 | self.assertNotEquals(None, c_token) 99 | # Build MIC 100 | mic_token = gss_ctxt.get_mic(mic_msg) 101 | 102 | if server_mode: 103 | # Check MIC 104 | status = gss_srv_ctxt.verify_mic(mic_msg, mic_token) 105 | self.assertEquals(0, status) 106 | else: 107 | gss_flags = sspicon.ISC_REQ_INTEGRITY |\ 108 | sspicon.ISC_REQ_MUTUAL_AUTH |\ 109 | sspicon.ISC_REQ_DELEGATE 110 | # Initialize a GSS-API context. 111 | target_name = "host/" + socket.getfqdn(targ_name) 112 | gss_ctxt = sspi.ClientAuth("Kerberos", 113 | scflags=gss_flags, 114 | targetspn=target_name) 115 | if server_mode: 116 | error, token = gss_ctxt.authorize(c_token) 117 | c_token = token[0].Buffer 118 | self.assertEquals(0, error) 119 | # Accept a GSS-API context. 120 | gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=target_name) 121 | error, token = gss_srv_ctxt.authorize(c_token) 122 | s_token = token[0].Buffer 123 | # Establish the context. 124 | error, token = gss_ctxt.authorize(s_token) 125 | c_token = token[0].Buffer 126 | self.assertEquals(None, c_token) 127 | self.assertEquals(0, error) 128 | # Build MIC 129 | mic_token = gss_ctxt.sign(mic_msg) 130 | # Check MIC 131 | gss_srv_ctxt.verify(mic_msg, mic_token) 132 | else: 133 | error, token = gss_ctxt.authorize(c_token) 134 | c_token = token[0].Buffer 135 | self.assertNotEquals(0, error) 136 | -------------------------------------------------------------------------------- /tests/test_hostkeys.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2006-2007 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Some unit tests for HostKeys. 21 | """ 22 | 23 | from binascii import hexlify 24 | import os 25 | import unittest 26 | import paramiko 27 | from paramiko.py3compat import decodebytes 28 | 29 | 30 | test_hosts_file = """\ 31 | secure.example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1PD6U2/TVxET6lkpKhOk5r\ 32 | 9q/kAYG6sP9f5zuUYP8i7FOFp/6ncCEbbtg/lB+A3iidyxoSWl+9jtoyyDOOVX4UIDV9G11Ml8om3\ 33 | D+jrpI9cycZHqilK0HmxDeCuxbwyMuaCygU9gS2qoRvNLWZk70OpIKSSpBo0Wl3/XUmz9uhc= 34 | broken.example.com ssh-rsa AAAA 35 | happy.example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA8bP1ZA7DCZDB9J0s50l31M\ 36 | BGQ3GQ/Fc7SX6gkpXkwcZryoi4kNFhHu5LvHcZPdxXV1D+uTMfGS1eyd2Yz/DoNWXNAl8TI0cAsW\ 37 | 5ymME3bQ4J/k1IKxCtz/bAlAqFgKoc+EolMziDYqWIATtW0rYTJvzGAzTmMj80/QpsFH+Pc2M= 38 | """ 39 | 40 | keyblob = b"""\ 41 | AAAAB3NzaC1yc2EAAAABIwAAAIEA8bP1ZA7DCZDB9J0s50l31MBGQ3GQ/Fc7SX6gkpXkwcZryoi4k\ 42 | NFhHu5LvHcZPdxXV1D+uTMfGS1eyd2Yz/DoNWXNAl8TI0cAsW5ymME3bQ4J/k1IKxCtz/bAlAqFgK\ 43 | oc+EolMziDYqWIATtW0rYTJvzGAzTmMj80/QpsFH+Pc2M=""" 44 | 45 | keyblob_dss = b"""\ 46 | AAAAB3NzaC1kc3MAAACBAOeBpgNnfRzr/twmAQRu2XwWAp3CFtrVnug6s6fgwj/oLjYbVtjAy6pl/\ 47 | h0EKCWx2rf1IetyNsTxWrniA9I6HeDj65X1FyDkg6g8tvCnaNB8Xp/UUhuzHuGsMIipRxBxw9LF60\ 48 | 8EqZcj1E3ytktoW5B5OcjrkEoz3xG7C+rpIjYvAAAAFQDwz4UnmsGiSNu5iqjn3uTzwUpshwAAAIE\ 49 | AkxfFeY8P2wZpDjX0MimZl5wkoFQDL25cPzGBuB4OnB8NoUk/yjAHIIpEShw8V+LzouMK5CTJQo5+\ 50 | Ngw3qIch/WgRmMHy4kBq1SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg\ 51 | 4Ok10+XFDxlqZo8Y+wAAACARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lYukmnjO\ 52 | 1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+NwacIBlXa8cMDL7Q/69o\ 53 | 0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgE=""" 54 | 55 | 56 | class HostKeysTest (unittest.TestCase): 57 | 58 | def setUp(self): 59 | with open('hostfile.temp', 'w') as f: 60 | f.write(test_hosts_file) 61 | 62 | def tearDown(self): 63 | os.unlink('hostfile.temp') 64 | 65 | def test_1_load(self): 66 | hostdict = paramiko.HostKeys('hostfile.temp') 67 | self.assertEqual(2, len(hostdict)) 68 | self.assertEqual(1, len(list(hostdict.values())[0])) 69 | self.assertEqual(1, len(list(hostdict.values())[1])) 70 | fp = hexlify(hostdict['secure.example.com']['ssh-rsa'].get_fingerprint()).upper() 71 | self.assertEqual(b'E6684DB30E109B67B70FF1DC5C7F1363', fp) 72 | 73 | def test_2_add(self): 74 | hostdict = paramiko.HostKeys('hostfile.temp') 75 | hh = '|1|BMsIC6cUIP2zBuXR3t2LRcJYjzM=|hpkJMysjTk/+zzUUzxQEa2ieq6c=' 76 | key = paramiko.RSAKey(data=decodebytes(keyblob)) 77 | hostdict.add(hh, 'ssh-rsa', key) 78 | self.assertEqual(3, len(list(hostdict))) 79 | x = hostdict['foo.example.com'] 80 | fp = hexlify(x['ssh-rsa'].get_fingerprint()).upper() 81 | self.assertEqual(b'7EC91BB336CB6D810B124B1353C32396', fp) 82 | self.assertTrue(hostdict.check('foo.example.com', key)) 83 | 84 | def test_3_dict(self): 85 | hostdict = paramiko.HostKeys('hostfile.temp') 86 | self.assertTrue('secure.example.com' in hostdict) 87 | self.assertTrue('not.example.com' not in hostdict) 88 | self.assertTrue('secure.example.com' in hostdict) 89 | self.assertTrue('not.example.com' not in hostdict) 90 | x = hostdict.get('secure.example.com', None) 91 | self.assertTrue(x is not None) 92 | fp = hexlify(x['ssh-rsa'].get_fingerprint()).upper() 93 | self.assertEqual(b'E6684DB30E109B67B70FF1DC5C7F1363', fp) 94 | i = 0 95 | for key in hostdict: 96 | i += 1 97 | self.assertEqual(2, i) 98 | 99 | def test_4_dict_set(self): 100 | hostdict = paramiko.HostKeys('hostfile.temp') 101 | key = paramiko.RSAKey(data=decodebytes(keyblob)) 102 | key_dss = paramiko.DSSKey(data=decodebytes(keyblob_dss)) 103 | hostdict['secure.example.com'] = { 104 | 'ssh-rsa': key, 105 | 'ssh-dss': key_dss 106 | } 107 | hostdict['fake.example.com'] = {} 108 | hostdict['fake.example.com']['ssh-rsa'] = key 109 | 110 | self.assertEqual(3, len(hostdict)) 111 | self.assertEqual(2, len(list(hostdict.values())[0])) 112 | self.assertEqual(1, len(list(hostdict.values())[1])) 113 | self.assertEqual(1, len(list(hostdict.values())[2])) 114 | fp = hexlify(hostdict['secure.example.com']['ssh-rsa'].get_fingerprint()).upper() 115 | self.assertEqual(b'7EC91BB336CB6D810B124B1353C32396', fp) 116 | fp = hexlify(hostdict['secure.example.com']['ssh-dss'].get_fingerprint()).upper() 117 | self.assertEqual(b'4478F0B9A23CC5182009FF755BC1D26C', fp) 118 | -------------------------------------------------------------------------------- /tests/test_kex_gss.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # Copyright (C) 2013-2014 science + computing ag 3 | # Author: Sebastian Deiss 4 | # 5 | # 6 | # This file is part of paramiko. 7 | # 8 | # Paramiko is free software; you can redistribute it and/or modify it under the 9 | # terms of the GNU Lesser General Public License as published by the Free 10 | # Software Foundation; either version 2.1 of the License, or (at your option) 11 | # any later version. 12 | # 13 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 14 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 15 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 16 | # details. 17 | # 18 | # You should have received a copy of the GNU Lesser General Public License 19 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 20 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 | 22 | """ 23 | Unit Tests for the GSS-API / SSPI SSHv2 Diffie-Hellman Key Exchange and user 24 | authentication 25 | """ 26 | 27 | 28 | import socket 29 | import threading 30 | import unittest 31 | 32 | import paramiko 33 | 34 | 35 | class NullServer (paramiko.ServerInterface): 36 | 37 | def get_allowed_auths(self, username): 38 | return 'gssapi-keyex' 39 | 40 | def check_auth_gssapi_keyex(self, username, 41 | gss_authenticated=paramiko.AUTH_FAILED, 42 | cc_file=None): 43 | if gss_authenticated == paramiko.AUTH_SUCCESSFUL: 44 | return paramiko.AUTH_SUCCESSFUL 45 | return paramiko.AUTH_FAILED 46 | 47 | def enable_auth_gssapi(self): 48 | UseGSSAPI = True 49 | return UseGSSAPI 50 | 51 | def check_channel_request(self, kind, chanid): 52 | return paramiko.OPEN_SUCCEEDED 53 | 54 | def check_channel_exec_request(self, channel, command): 55 | if command != 'yes': 56 | return False 57 | return True 58 | 59 | 60 | class GSSKexTest(unittest.TestCase): 61 | @staticmethod 62 | def init(username, hostname): 63 | global krb5_principal, targ_name 64 | krb5_principal = username 65 | targ_name = hostname 66 | 67 | def setUp(self): 68 | self.username = krb5_principal 69 | self.hostname = socket.getfqdn(targ_name) 70 | self.sockl = socket.socket() 71 | self.sockl.bind((targ_name, 0)) 72 | self.sockl.listen(1) 73 | self.addr, self.port = self.sockl.getsockname() 74 | self.event = threading.Event() 75 | thread = threading.Thread(target=self._run) 76 | thread.start() 77 | 78 | def tearDown(self): 79 | for attr in "tc ts socks sockl".split(): 80 | if hasattr(self, attr): 81 | getattr(self, attr).close() 82 | 83 | def _run(self): 84 | self.socks, addr = self.sockl.accept() 85 | self.ts = paramiko.Transport(self.socks, gss_kex=True) 86 | host_key = paramiko.RSAKey.from_private_key_file('tests/test_rsa.key') 87 | self.ts.add_server_key(host_key) 88 | self.ts.set_gss_host(targ_name) 89 | try: 90 | self.ts.load_server_moduli() 91 | except: 92 | print ('(Failed to load moduli -- gex will be unsupported.)') 93 | server = NullServer() 94 | self.ts.start_server(self.event, server) 95 | 96 | def test_1_gsskex_and_auth(self): 97 | """ 98 | Verify that Paramiko can handle SSHv2 GSS-API / SSPI authenticated 99 | Diffie-Hellman Key Exchange and user authentication with the GSS-API 100 | context created during key exchange. 101 | """ 102 | host_key = paramiko.RSAKey.from_private_key_file('tests/test_rsa.key') 103 | public_host_key = paramiko.RSAKey(data=host_key.asbytes()) 104 | 105 | self.tc = paramiko.SSHClient() 106 | self.tc.get_host_keys().add('[%s]:%d' % (self.hostname, self.port), 107 | 'ssh-rsa', public_host_key) 108 | self.tc.connect(self.hostname, self.port, username=self.username, 109 | gss_auth=True, gss_kex=True) 110 | 111 | self.event.wait(1.0) 112 | self.assert_(self.event.is_set()) 113 | self.assert_(self.ts.is_active()) 114 | self.assertEquals(self.username, self.ts.get_username()) 115 | self.assertEquals(True, self.ts.is_authenticated()) 116 | 117 | stdin, stdout, stderr = self.tc.exec_command('yes') 118 | schan = self.ts.accept(1.0) 119 | 120 | schan.send('Hello there.\n') 121 | schan.send_stderr('This is on stderr.\n') 122 | schan.close() 123 | 124 | self.assertEquals('Hello there.\n', stdout.readline()) 125 | self.assertEquals('', stdout.readline()) 126 | self.assertEquals('This is on stderr.\n', stderr.readline()) 127 | self.assertEquals('', stderr.readline()) 128 | 129 | stdin.close() 130 | stdout.close() 131 | stderr.close() 132 | -------------------------------------------------------------------------------- /tests/test_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2009 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Some unit tests for ssh protocol message blocks. 21 | """ 22 | 23 | import unittest 24 | from paramiko.message import Message 25 | from paramiko.common import byte_chr, zero_byte 26 | 27 | 28 | class MessageTest (unittest.TestCase): 29 | 30 | __a = b'\x00\x00\x00\x17\x07\x60\xe0\x90\x00\x00\x00\x01\x71\x00\x00\x00\x05\x68\x65\x6c\x6c\x6f\x00\x00\x03\xe8' + b'x' * 1000 31 | __b = b'\x01\x00\xf3\x00\x3f\x00\x00\x00\x10\x68\x75\x65\x79\x2c\x64\x65\x77\x65\x79\x2c\x6c\x6f\x75\x69\x65' 32 | __c = b'\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\xf5\xe4\xd3\xc2\xb1\x09\x00\x00\x00\x01\x11\x00\x00\x00\x07\x00\xf5\xe4\xd3\xc2\xb1\x09\x00\x00\x00\x06\x9a\x1b\x2c\x3d\x4e\xf7' 33 | __d = b'\x00\x00\x00\x05\xff\x00\x00\x00\x05\x11\x22\x33\x44\x55\xff\x00\x00\x00\x0a\x00\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x63\x61\x74\x00\x00\x00\x03\x61\x2c\x62' 34 | 35 | def test_1_encode(self): 36 | msg = Message() 37 | msg.add_int(23) 38 | msg.add_int(123789456) 39 | msg.add_string('q') 40 | msg.add_string('hello') 41 | msg.add_string('x' * 1000) 42 | self.assertEqual(msg.asbytes(), self.__a) 43 | 44 | msg = Message() 45 | msg.add_boolean(True) 46 | msg.add_boolean(False) 47 | msg.add_byte(byte_chr(0xf3)) 48 | 49 | msg.add_bytes(zero_byte + byte_chr(0x3f)) 50 | msg.add_list(['huey', 'dewey', 'louie']) 51 | self.assertEqual(msg.asbytes(), self.__b) 52 | 53 | msg = Message() 54 | msg.add_int64(5) 55 | msg.add_int64(0xf5e4d3c2b109) 56 | msg.add_mpint(17) 57 | msg.add_mpint(0xf5e4d3c2b109) 58 | msg.add_mpint(-0x65e4d3c2b109) 59 | self.assertEqual(msg.asbytes(), self.__c) 60 | 61 | def test_2_decode(self): 62 | msg = Message(self.__a) 63 | self.assertEqual(msg.get_int(), 23) 64 | self.assertEqual(msg.get_int(), 123789456) 65 | self.assertEqual(msg.get_text(), 'q') 66 | self.assertEqual(msg.get_text(), 'hello') 67 | self.assertEqual(msg.get_text(), 'x' * 1000) 68 | 69 | msg = Message(self.__b) 70 | self.assertEqual(msg.get_boolean(), True) 71 | self.assertEqual(msg.get_boolean(), False) 72 | self.assertEqual(msg.get_byte(), byte_chr(0xf3)) 73 | self.assertEqual(msg.get_bytes(2), zero_byte + byte_chr(0x3f)) 74 | self.assertEqual(msg.get_list(), ['huey', 'dewey', 'louie']) 75 | 76 | msg = Message(self.__c) 77 | self.assertEqual(msg.get_int64(), 5) 78 | self.assertEqual(msg.get_int64(), 0xf5e4d3c2b109) 79 | self.assertEqual(msg.get_mpint(), 17) 80 | self.assertEqual(msg.get_mpint(), 0xf5e4d3c2b109) 81 | self.assertEqual(msg.get_mpint(), -0x65e4d3c2b109) 82 | 83 | def test_3_add(self): 84 | msg = Message() 85 | msg.add(5) 86 | msg.add(0x1122334455) 87 | msg.add(0xf00000000000000000) 88 | msg.add(True) 89 | msg.add('cat') 90 | msg.add(['a', 'b']) 91 | self.assertEqual(msg.asbytes(), self.__d) 92 | 93 | def test_4_misc(self): 94 | msg = Message(self.__d) 95 | self.assertEqual(msg.get_adaptive_int(), 5) 96 | self.assertEqual(msg.get_adaptive_int(), 0x1122334455) 97 | self.assertEqual(msg.get_adaptive_int(), 0xf00000000000000000) 98 | self.assertEqual(msg.get_so_far(), self.__d[:29]) 99 | self.assertEqual(msg.get_remainder(), self.__d[29:]) 100 | msg.rewind() 101 | self.assertEqual(msg.get_adaptive_int(), 5) 102 | self.assertEqual(msg.get_so_far(), self.__d[:4]) 103 | self.assertEqual(msg.get_remainder(), self.__d[4:]) 104 | -------------------------------------------------------------------------------- /tests/test_packetizer.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2009 Robey Pointer 2 | # 3 | # This file is part of paramiko. 4 | # 5 | # Paramiko is free software; you can redistribute it and/or modify it under the 6 | # terms of the GNU Lesser General Public License as published by the Free 7 | # Software Foundation; either version 2.1 of the License, or (at your option) 8 | # any later version. 9 | # 10 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 13 | # details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 17 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 18 | 19 | """ 20 | Some unit tests for the ssh2 protocol in Transport. 21 | """ 22 | 23 | import unittest 24 | from hashlib import sha1 25 | 26 | from tests.loop import LoopSocket 27 | 28 | from Crypto.Cipher import AES 29 | 30 | from paramiko import Message, Packetizer, util 31 | from paramiko.common import byte_chr, zero_byte 32 | 33 | x55 = byte_chr(0x55) 34 | x1f = byte_chr(0x1f) 35 | 36 | 37 | class PacketizerTest (unittest.TestCase): 38 | 39 | def test_1_write(self): 40 | rsock = LoopSocket() 41 | wsock = LoopSocket() 42 | rsock.link(wsock) 43 | p = Packetizer(wsock) 44 | p.set_log(util.get_logger('paramiko.transport')) 45 | p.set_hexdump(True) 46 | cipher = AES.new(zero_byte * 16, AES.MODE_CBC, x55 * 16) 47 | p.set_outbound_cipher(cipher, 16, sha1, 12, x1f * 20) 48 | 49 | # message has to be at least 16 bytes long, so we'll have at least one 50 | # block of data encrypted that contains zero random padding bytes 51 | m = Message() 52 | m.add_byte(byte_chr(100)) 53 | m.add_int(100) 54 | m.add_int(1) 55 | m.add_int(900) 56 | p.send_message(m) 57 | data = rsock.recv(100) 58 | # 32 + 12 bytes of MAC = 44 59 | self.assertEqual(44, len(data)) 60 | self.assertEqual(b'\x43\x91\x97\xbd\x5b\x50\xac\x25\x87\xc2\xc4\x6b\xc7\xe9\x38\xc0', data[:16]) 61 | 62 | def test_2_read(self): 63 | rsock = LoopSocket() 64 | wsock = LoopSocket() 65 | rsock.link(wsock) 66 | p = Packetizer(rsock) 67 | p.set_log(util.get_logger('paramiko.transport')) 68 | p.set_hexdump(True) 69 | cipher = AES.new(zero_byte * 16, AES.MODE_CBC, x55 * 16) 70 | p.set_inbound_cipher(cipher, 16, sha1, 12, x1f * 20) 71 | wsock.send(b'\x43\x91\x97\xbd\x5b\x50\xac\x25\x87\xc2\xc4\x6b\xc7\xe9\x38\xc0\x90\xd2\x16\x56\x0d\x71\x73\x61\x38\x7c\x4c\x3d\xfb\x97\x7d\xe2\x6e\x03\xb1\xa0\xc2\x1c\xd6\x41\x41\x4c\xb4\x59') 72 | cmd, m = p.read_message() 73 | self.assertEqual(100, cmd) 74 | self.assertEqual(100, m.get_int()) 75 | self.assertEqual(1, m.get_int()) 76 | self.assertEqual(900, m.get_int()) 77 | 78 | def test_3_closed(self): 79 | rsock = LoopSocket() 80 | wsock = LoopSocket() 81 | rsock.link(wsock) 82 | p = Packetizer(wsock) 83 | p.set_log(util.get_logger('paramiko.transport')) 84 | p.set_hexdump(True) 85 | cipher = AES.new(zero_byte * 16, AES.MODE_CBC, x55 * 16) 86 | p.set_outbound_cipher(cipher, 16, sha1, 12, x1f * 20) 87 | 88 | # message has to be at least 16 bytes long, so we'll have at least one 89 | # block of data encrypted that contains zero random padding bytes 90 | m = Message() 91 | m.add_byte(byte_chr(100)) 92 | m.add_int(100) 93 | m.add_int(1) 94 | m.add_int(900) 95 | wsock.send = lambda x: 0 96 | from functools import wraps 97 | import errno 98 | import os 99 | import signal 100 | 101 | class TimeoutError(Exception): 102 | pass 103 | 104 | def timeout(seconds=1, error_message=os.strerror(errno.ETIME)): 105 | def decorator(func): 106 | def _handle_timeout(signum, frame): 107 | raise TimeoutError(error_message) 108 | 109 | def wrapper(*args, **kwargs): 110 | signal.signal(signal.SIGALRM, _handle_timeout) 111 | signal.alarm(seconds) 112 | try: 113 | result = func(*args, **kwargs) 114 | finally: 115 | signal.alarm(0) 116 | return result 117 | 118 | return wraps(func)(wrapper) 119 | 120 | return decorator 121 | send = timeout()(p.send_message) 122 | self.assertRaises(EOFError, send, m) 123 | -------------------------------------------------------------------------------- /tests/test_rsa.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz 3 | oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/ 4 | d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB 5 | gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0 6 | EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon 7 | soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H 8 | tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU 9 | avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA 10 | 4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g 11 | H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv 12 | qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV 13 | HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc 14 | nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /tests/test_rsa_password.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: DES-EDE3-CBC,DAA422E8A5A8EFB7 4 | 5 | +nssHGmWl91IcmGiE6DdCIqGvAP04tuLh60wLjWBvdjtF9CjztPnF57xe+6pBk7o 6 | YgF/Ry3ik9ZV9rHNcRXifDKM9crxtYlpUlkM2C0SP89sXaO0P1Q1yCnrtZUwDIKO 7 | BNV8et5X7+AGMFsy/nmv0NFMrbpoG03Dppsloecd29NTRlIXwxHRFyHxy6BdEib/ 8 | Dn0mEVbwg3dTvKrd/sODWR9hRwpDGM9nkEbUNJCh7vMwFKkIZZF8yqFvmGckuO5C 9 | HZkDJ6RkEDYrSZJAavQaiOPF5bu3cHughRfnrIKVrQuTTDiWjwX9Ny8e4p4k7dy7 10 | rLpbPhtxUOUbpOF7T1QxljDi1Tcq3Ebk3kN/ZLPRFnDrJfyUx+m9BXmAa78Wxs/l 11 | KaS8DTkYykd3+EGOeJFjZg2bvgqil4V+5JIt/+MQ5pZ/ui7i4GcH2bvZyGAbrXzP 12 | 3LipSAdN5RG+fViLe3HUtfCx4ZAgtU78TWJrLk2FwKQGglFxKLnswp+IKZb09rZV 13 | uxmG4pPLUnH+mMYdiy5ugzj+5C8iZ0/IstpHVmO6GWROfedpJ82eMztTOtdhfMep 14 | 8Z3HwAwkDtksL7Gq9klb0Wq5+uRlBWetixddAvnmqXNzYhaANWcAF/2a2Hz06Rb0 15 | e6pe/g0Ek5KV+6YI+D+oEblG0Sr+d4NtxtDTmIJKNVkmzlhI2s53bHp6txCb5JWJ 16 | S8mKLPBBBzaNXYd3odDvGXguuxUntWSsD11KyR6B9DXMIfWQW5dT7hp5kTMGlXWJ 17 | lD2hYab13DCCuAkwVTdpzhHYLZyxLYoSu05W6z8SAOs= 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /tests/test_ssh_gss.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2003-2007 Robey Pointer 2 | # Copyright (C) 2013-2014 science + computing ag 3 | # Author: Sebastian Deiss 4 | # 5 | # 6 | # This file is part of paramiko. 7 | # 8 | # Paramiko is free software; you can redistribute it and/or modify it under the 9 | # terms of the GNU Lesser General Public License as published by the Free 10 | # Software Foundation; either version 2.1 of the License, or (at your option) 11 | # any later version. 12 | # 13 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY 14 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 15 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 16 | # details. 17 | # 18 | # You should have received a copy of the GNU Lesser General Public License 19 | # along with Paramiko; if not, write to the Free Software Foundation, Inc., 20 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 | 22 | """ 23 | Unit Tests for the GSS-API / SSPI SSHv2 Authentication (gssapi-with-mic) 24 | """ 25 | 26 | import socket 27 | import threading 28 | import unittest 29 | 30 | import paramiko 31 | 32 | 33 | class NullServer (paramiko.ServerInterface): 34 | 35 | def get_allowed_auths(self, username): 36 | return 'gssapi-with-mic' 37 | 38 | def check_auth_gssapi_with_mic(self, username, 39 | gss_authenticated=paramiko.AUTH_FAILED, 40 | cc_file=None): 41 | if gss_authenticated == paramiko.AUTH_SUCCESSFUL: 42 | return paramiko.AUTH_SUCCESSFUL 43 | return paramiko.AUTH_FAILED 44 | 45 | def enable_auth_gssapi(self): 46 | UseGSSAPI = True 47 | GSSAPICleanupCredentials = True 48 | return UseGSSAPI 49 | 50 | def check_channel_request(self, kind, chanid): 51 | return paramiko.OPEN_SUCCEEDED 52 | 53 | def check_channel_exec_request(self, channel, command): 54 | if command != 'yes': 55 | return False 56 | return True 57 | 58 | 59 | class GSSAuthTest(unittest.TestCase): 60 | @staticmethod 61 | def init(username, hostname): 62 | global krb5_principal, targ_name 63 | krb5_principal = username 64 | targ_name = hostname 65 | 66 | def setUp(self): 67 | self.username = krb5_principal 68 | self.hostname = socket.getfqdn(targ_name) 69 | self.sockl = socket.socket() 70 | self.sockl.bind((targ_name, 0)) 71 | self.sockl.listen(1) 72 | self.addr, self.port = self.sockl.getsockname() 73 | self.event = threading.Event() 74 | thread = threading.Thread(target=self._run) 75 | thread.start() 76 | 77 | def tearDown(self): 78 | for attr in "tc ts socks sockl".split(): 79 | if hasattr(self, attr): 80 | getattr(self, attr).close() 81 | 82 | def _run(self): 83 | self.socks, addr = self.sockl.accept() 84 | self.ts = paramiko.Transport(self.socks) 85 | host_key = paramiko.RSAKey.from_private_key_file('tests/test_rsa.key') 86 | self.ts.add_server_key(host_key) 87 | server = NullServer() 88 | self.ts.start_server(self.event, server) 89 | 90 | def test_1_gss_auth(self): 91 | """ 92 | Verify that Paramiko can handle SSHv2 GSS-API / SSPI authentication 93 | (gssapi-with-mic) in client and server mode. 94 | """ 95 | host_key = paramiko.RSAKey.from_private_key_file('tests/test_rsa.key') 96 | public_host_key = paramiko.RSAKey(data=host_key.asbytes()) 97 | 98 | self.tc = paramiko.SSHClient() 99 | self.tc.get_host_keys().add('[%s]:%d' % (self.hostname, self.port), 100 | 'ssh-rsa', public_host_key) 101 | self.tc.connect(self.hostname, self.port, username=self.username, 102 | gss_auth=True) 103 | 104 | self.event.wait(1.0) 105 | self.assert_(self.event.is_set()) 106 | self.assert_(self.ts.is_active()) 107 | self.assertEquals(self.username, self.ts.get_username()) 108 | self.assertEquals(True, self.ts.is_authenticated()) 109 | 110 | stdin, stdout, stderr = self.tc.exec_command('yes') 111 | schan = self.ts.accept(1.0) 112 | 113 | schan.send('Hello there.\n') 114 | schan.send_stderr('This is on stderr.\n') 115 | schan.close() 116 | 117 | self.assertEquals('Hello there.\n', stdout.readline()) 118 | self.assertEquals('', stdout.readline()) 119 | self.assertEquals('This is on stderr.\n', stderr.readline()) 120 | self.assertEquals('', stderr.readline()) 121 | 122 | stdin.close() 123 | stdout.close() 124 | stderr.close() 125 | -------------------------------------------------------------------------------- /tests/util.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | root_path = os.path.dirname(os.path.realpath(__file__)) 4 | 5 | def test_path(filename): 6 | return os.path.join(root_path, filename) 7 | 8 | -------------------------------------------------------------------------------- /tox-requirements.txt: -------------------------------------------------------------------------------- 1 | # Not sure why tox can't just read setup.py? 2 | pycrypto 3 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py26,py27,py32,py33,py34 3 | 4 | [testenv] 5 | commands = pip install -q -r tox-requirements.txt 6 | python test.py 7 | --------------------------------------------------------------------------------