├── .github
└── workflows
│ └── linux.yml
├── .gitignore
├── LICENSE
├── MANIFEST.in
├── README.rst
├── demos
├── demo.py
├── demo_keygen.py
├── demo_server.py
├── demo_sftp.py
├── demo_simple.py
├── forward.py
├── interactive.py
├── rforward.py
├── test_rsa.key
├── user_rsa_key
└── user_rsa_key.pub
├── dev-requirements.txt
├── doc-requirements.txt
├── 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
├── ed25519key.py
├── file.py
├── hostkeys.py
├── kex_curve25519.py
├── kex_ecdh_nist.py
├── kex_gex.py
├── kex_group1.py
├── kex_group14.py
├── kex_group16.py
├── kex_gss.py
├── message.py
├── packet.py
├── pipe.py
├── pkey.py
├── primes.py
├── proxy.py
├── py3compat.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_openssh.py
└── win_pageant.py
├── setup.cfg
├── setup.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
│ ├── faq.rst
│ ├── index.rst
│ ├── installing.rst
│ └── old_changelog.rst
└── tests
├── __init__.py
├── cert_support
├── test_dss.key
├── test_dss.key-cert.pub
├── test_ecdsa_256.key
├── test_ecdsa_256.key-cert.pub
├── test_ed25519.key
├── test_ed25519.key-cert.pub
├── test_rsa.key
└── test_rsa.key-cert.pub
├── conftest.py
├── loop.py
├── stub_sftp.py
├── test_auth.py
├── test_buffered_pipe.py
├── test_channelfile.py
├── test_client.py
├── test_dss.key
├── test_dss_1k_o.key
├── test_dss_password.key
├── test_ecdsa_256.key
├── test_ecdsa_384.key
├── test_ecdsa_384_o.key
├── test_ecdsa_521.key
├── test_ecdsa_password_256.key
├── test_ecdsa_password_384.key
├── test_ecdsa_password_521.key
├── test_ed25519.key
├── test_ed25519_nopad.key
├── test_ed25519_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.key.pub
├── test_rsa_2k_o.key
├── test_rsa_password.key
├── test_sftp.py
├── test_sftp_big.py
├── test_ssh_exception.py
├── test_ssh_gss.py
├── test_transport.py
├── test_util.py
└── util.py
/.github/workflows/linux.yml:
--------------------------------------------------------------------------------
1 | name: Linux tests
2 |
3 | on:
4 | push: {branches: [master]}
5 | pull_request: {branches: [master]}
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-24.04
10 | timeout-minutes: 30
11 |
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | include:
16 | - {imgtag: "python:2.7-buster", crypto_ver: "2.6.1"}
17 | - {imgtag: "python:2.7-buster", crypto_ver: "3.2.1"}
18 | - {imgtag: "pypy:2.7-7.3.16", crypto_ver: "3.3.2"}
19 | - {imgtag: "python:3.5-buster", crypto_ver: "2.6.1"}
20 | - {imgtag: "python:3.5-buster", crypto_ver: "3.2.1"}
21 | - {imgtag: "python:3.6-bullseye", crypto_ver: "2.9.2"}
22 | - {imgtag: "python:3.6-bullseye", crypto_ver: "3.4.8"}
23 | - {imgtag: "python:3.7-bullseye", crypto_ver: "3.2.1"}
24 | - {imgtag: "python:3.7-bullseye", crypto_ver: "3.4.8"}
25 | - {imgtag: "python:3.8-bookworm", crypto_ver: "3.3.2"}
26 | - {imgtag: "python:3.8-bookworm", crypto_ver: "3.4.8"}
27 | - {imgtag: "python:3.9-bookworm", crypto_ver: "2.9.2"}
28 | - {imgtag: "python:3.9-bookworm", crypto_ver: "36.0.2"}
29 | - {imgtag: "python:3.10-bookworm", crypto_ver: "36.0.2"}
30 | - {imgtag: "python:3.10-bookworm", crypto_ver: "41.0.7"}
31 | - {imgtag: "python:3.11-bookworm", crypto_ver: "38.0.4"}
32 | - {imgtag: "python:3.11-bookworm", crypto_ver: "42.0.8"}
33 | - {imgtag: "python:3.12-bookworm", crypto_ver: "38.0.4"}
34 | - {imgtag: "python:3.12-bookworm", crypto_ver: "44.0.2"}
35 | - {imgtag: "python:3.13-bookworm", crypto_ver: "38.0.4"}
36 | - {imgtag: "python:3.13-bookworm", crypto_ver: "44.0.2"}
37 | - {imgtag: "pypy:3.11-7.3.19", crypto_ver: "44.0.2"}
38 |
39 | container: "${{matrix.imgtag}}"
40 | steps:
41 | - uses: actions/checkout@v3
42 |
43 | - name: Python dependencies
44 | run: |
45 | pip install -r dev-requirements.txt
46 | pip install cryptography==${{matrix.crypto_ver}} PyNaCl
47 | if [ ${{matrix.imgtag}} = python:3.7-bullseye ]; then
48 | export DEBIAN_FRONTEND=noninteractive
49 | apt-get -q -y update
50 | apt-get -q -y install libkrb5-dev krb5-admin-server \
51 | krb5-kdc krb5-user krb5-multidev openssh-server
52 | pip install gssapi==1.5.1 pyasn1==0.4.5 k5test==0.9.2
53 | fi
54 | pip install -e .
55 | pip freeze
56 |
57 | - name: Lint
58 | if: ${{ ! (contains(matrix.imgtag, ':2.7-') || contains(matrix.imgtag, ':3.5-')) }}
59 | run: |
60 | flake8 --version
61 | flake8 --show-source
62 |
63 | - name: Test
64 | run: |
65 | pytest -v
66 |
67 | docs:
68 | runs-on: ubuntu-24.04
69 | timeout-minutes: 30
70 | steps:
71 | - uses: actions/checkout@v3
72 |
73 | - uses: actions/setup-python@v4
74 | with:
75 | python-version: 3.9
76 |
77 | - name: Install dependencies
78 | run: |
79 | pip install -r doc-requirements.txt
80 | pip install -e .
81 | - name: Build docs
82 | run: |
83 | sphinx-build -v -W sites/docs tmpbuild
84 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | /build/
3 | /dist/
4 | demos/*.log
5 | .cache
6 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE
2 | recursive-include tests *.py *.key *.pub
3 | recursive-include demos *.py *.key user_rsa_key user_rsa_key.pub
4 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | ===========
2 | Paramiko-NG
3 | ===========
4 |
5 | This is a fork of `paramiko `_ for more active maintenance.
6 |
7 | This is still imported under the name ``paramiko``, but you can
8 | install with the pip-package-name *paramiko-ng* (default) or *paramiko*
9 | (by setting the environment variable ``PARAMIKO_REPLACE``, see `Installation`_).
10 |
11 | Changelog: `releases `_
12 |
13 | :Paramiko-NG: Python SSH module
14 | :Copyright: Copyright (c) 2003-2009 Robey Pointer
15 | :Copyright: Copyright (c) 2013-2019 Jeff Forcier
16 | :Copyright: Copyright (c) 2019-2022 Pierce Lopez
17 | :License: `LGPL `_
18 | :API docs: https://ploxiln.github.io/paramiko-ng/
19 | :Development: https://github.com/ploxiln/paramiko-ng/
20 |
21 |
22 | What
23 | ----
24 |
25 | "Paramiko" is a combination of the Esperanto words for "paranoid" and
26 | "friend". It's a module for Python 2.7/3.4+ that implements the SSH2 protocol
27 | for secure (encrypted and authenticated) connections to remote machines. Unlike
28 | SSL (aka TLS), SSH2 protocol does not require hierarchical certificates signed
29 | by a powerful central authority. You may know SSH2 as the protocol that
30 | replaced Telnet and rsh for secure access to remote shells, but the protocol
31 | also includes the ability to open arbitrary channels to remote services across
32 | the encrypted tunnel (this is how SFTP works, for example).
33 |
34 | It is written entirely in Python (though it depends on third-party C wrappers
35 | for low level crypto; these are often available precompiled) and is released
36 | under the GNU Lesser General Public License (`LGPL `_).
37 |
38 |
39 | Installation
40 | ------------
41 |
42 | The import name is still just ``paramiko``. Make sure the original *paramiko*
43 | is not installed before installing *paramiko-ng* - otherwise pip may report
44 | success even though *paramiko-ng* was not correctly installed.
45 | (Because the import name is the same, installed files can conflict.)
46 |
47 | The most common way to install is simply::
48 |
49 | pip install paramiko-ng
50 |
51 | You can also install under the original "paramiko" pip-package-name, in order to
52 | satisfy requirements for other packages (replace "2.8.10" with desired version)::
53 |
54 | PARAMIKO_REPLACE=1 pip install "https://github.com/ploxiln/paramiko-ng/archive/2.8.10.tar.gz#egg=paramiko"
55 |
56 | For dependencies and other details, see https://ploxiln.github.io/paramiko-ng/installing.html
57 |
58 |
59 | Portability Issues
60 | ------------------
61 |
62 | Paramiko primarily supports POSIX platforms with standard OpenSSH
63 | implementations, and is most frequently tested on Linux and OS X. Windows is
64 | supported as well, though it may not be as straightforward.
65 |
66 |
67 | Bugs & Support
68 | --------------
69 |
70 | :Bug Reports: `Github `_
71 | :IRC: ``#paramiko`` on Freenode
72 |
73 |
74 | Demo
75 | ----
76 |
77 | Several demo scripts come with Paramiko to demonstrate how to use it.
78 | Probably the simplest demo is this::
79 |
80 | import base64
81 | import paramiko
82 | key = paramiko.RSAKey(data=base64.b64decode(b'AAA...'))
83 | client = paramiko.SSHClient()
84 | client.get_host_keys().add('ssh.example.com', 'ssh-rsa', key)
85 | client.connect('ssh.example.com', username='strongbad', password='thecheat')
86 | stdin, stdout, stderr = client.exec_command('ls')
87 | for line in stdout:
88 | print('... ' + line.strip('\n'))
89 | client.close()
90 |
91 | This prints out the results of executing ``ls`` on a remote server. The host
92 | key ``b'AAA...'`` should of course be replaced by the actual base64 encoding of the
93 | host key. If you skip host key verification, the connection is not secure!
94 |
95 | The following example scripts (in `demos/ `_) get progressively more detailed:
96 |
97 | :demo_simple.py:
98 | Calls invoke_shell() and emulates a terminal/TTY through which you can
99 | execute commands interactively on a remote server. Think of it as a
100 | poor man's SSH command-line client.
101 |
102 | :demo.py:
103 | Same as demo_simple.py, but allows you to authenticate using a private
104 | key, attempts to use an SSH agent if present, and uses the long form of
105 | some of the API calls.
106 |
107 | :forward.py:
108 | Command-line script to set up port-forwarding across an SSH transport.
109 |
110 | :demo_sftp.py:
111 | Opens an SFTP session and does a few simple file operations.
112 |
113 | :demo_server.py:
114 | An SSH server that listens on port 2200 and accepts a login for
115 | 'robey' (password 'foo'), and pretends to be a BBS. Meant to be a
116 | very simple demo of writing an SSH server.
117 |
118 | :demo_keygen.py:
119 | A key generator similar to OpenSSH ``ssh-keygen(1)`` program with
120 | Paramiko keys generation and progress functions.
121 |
122 | Use
123 | ---
124 |
125 | The demo scripts are probably the best example of how to use this package.
126 |
127 | A much easier alternative to using paramiko-ng directly is to use
128 | `fab-classic `_ or
129 | `Fabric `_ 2.x (or, for managed networking equipment,
130 | `netmiko `_). Whereas recent releases of
131 | *fab-classic* depend on *paramiko-ng* directly, to use this with *Fabric* or
132 | *netmiko* you need to install *paramiko-ng* with the pip-package-name
133 | "paramiko", using the ``PARAMIKO_REPLACE`` environment variable as described
134 | above in `Installation`_.
135 |
136 |
137 | There are also unit tests which will verify that most of the components are working correctly::
138 |
139 | $ pip install -r dev-requirements.txt
140 | $ pytest -v
141 |
142 |
--------------------------------------------------------------------------------
/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 getpass
23 | import os
24 | import socket
25 | import sys
26 | import traceback
27 |
28 | from paramiko.py3compat import input
29 | import paramiko
30 | try:
31 | import interactive
32 | except ImportError:
33 | from . import interactive
34 |
35 |
36 | def agent_auth(transport, username):
37 | """
38 | Attempt to authenticate to the given transport using any of the private
39 | keys available from an SSH agent.
40 | """
41 |
42 | agent = paramiko.Agent()
43 | agent_keys = agent.get_keys()
44 | if len(agent_keys) == 0:
45 | return
46 |
47 | for key in agent_keys:
48 | print('Trying ssh-agent key %s' % key.get_fingerprint_sha256_b64())
49 | try:
50 | transport.auth_publickey(username, key)
51 | print('... success!')
52 | return
53 | except paramiko.SSHException:
54 | print('... nope.')
55 |
56 |
57 | def manual_auth(username, hostname):
58 | default_auth = 'p'
59 | auth = input('Auth by (p)assword or (k)ey file? [%s] ' % default_auth)
60 | if len(auth) == 0:
61 | auth = default_auth
62 |
63 | if auth == 'k':
64 | default_path = os.path.join(os.environ['HOME'], '.ssh', 'id_rsa')
65 | path = input('Private key file [%s]: ' % default_path)
66 | if len(path) == 0:
67 | path = default_path
68 | try:
69 | key = paramiko.load_private_key_file(path)
70 | except paramiko.PasswordRequiredException:
71 | password = getpass.getpass('Private key password: ')
72 | key = paramiko.load_private_key_file(path, password)
73 | t.auth_publickey(username, key)
74 | else:
75 | pw = getpass.getpass('Password for %s@%s: ' % (username, hostname))
76 | t.auth_password(username, pw)
77 |
78 |
79 | # setup logging
80 | paramiko.util.log_to_file('demo.log')
81 |
82 | username = ''
83 | if len(sys.argv) > 1:
84 | hostname = sys.argv[1]
85 | if hostname.find('@') >= 0:
86 | username, hostname = hostname.split('@')
87 | else:
88 | hostname = input('Hostname: ')
89 | if len(hostname) == 0:
90 | print('*** Hostname required.')
91 | sys.exit(1)
92 | port = 22
93 | if hostname.find(':') >= 0:
94 | hostname, portstr = hostname.split(':')
95 | port = int(portstr)
96 |
97 | # now connect
98 | try:
99 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
100 | sock.connect((hostname, port))
101 | except Exception as e:
102 | print('*** Connect failed: ' + str(e))
103 | traceback.print_exc()
104 | sys.exit(1)
105 |
106 | try:
107 | t = paramiko.Transport(sock)
108 | try:
109 | t.start_client()
110 | except paramiko.SSHException:
111 | print('*** SSH negotiation failed.')
112 | sys.exit(1)
113 |
114 | try:
115 | keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
116 | except IOError:
117 | try:
118 | keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts'))
119 | except IOError:
120 | print('*** Unable to open host keys file')
121 | keys = {}
122 |
123 | # check server's host key -- this is important.
124 | key = t.get_remote_server_key()
125 | if hostname not in keys:
126 | print('*** WARNING: Unknown host key!')
127 | elif key.get_name() not in keys[hostname]:
128 | print('*** WARNING: Unknown host key!')
129 | elif keys[hostname][key.get_name()] != key:
130 | print('*** WARNING: Host key has changed!!!')
131 | sys.exit(1)
132 | else:
133 | print('*** Host key OK.')
134 |
135 | # get username
136 | if username == '':
137 | default_username = getpass.getuser()
138 | username = input('Username [%s]: ' % default_username)
139 | if len(username) == 0:
140 | username = default_username
141 |
142 | agent_auth(t, username)
143 | if not t.is_authenticated():
144 | manual_auth(username, hostname)
145 | if not t.is_authenticated():
146 | print('*** Authentication failed. :(')
147 | t.close()
148 | sys.exit(1)
149 |
150 | chan = t.open_session()
151 | chan.get_pty()
152 | chan.invoke_shell()
153 | print('*** Here we go!\n')
154 | interactive.interactive_shell(chan)
155 | chan.close()
156 | t.close()
157 |
158 | except Exception as e:
159 | print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e))
160 | traceback.print_exc()
161 | try:
162 | t.close()
163 | except:
164 | pass
165 | sys.exit(1)
166 |
--------------------------------------------------------------------------------
/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 | from optparse import OptionParser
23 |
24 | from paramiko import DSSKey
25 | from paramiko import RSAKey
26 | from paramiko.ssh_exception import SSHException
27 |
28 | usage = """%prog [-v] [-b bits] -t type [-N new_passphrase] [-f output_keyfile]"""
29 |
30 | default_values = {
31 | "ktype": "dsa",
32 | "bits": 1024,
33 | "filename": "output",
34 | "comment": ""
35 | }
36 |
37 | key_dispatch_table = {
38 | 'dsa': DSSKey,
39 | 'rsa': RSAKey,
40 | }
41 |
42 |
43 | def progress(arg=None):
44 | if not arg:
45 | sys.stdout.write('0%\x08\x08\x08 ')
46 | sys.stdout.flush()
47 | elif arg[0] == 'p':
48 | sys.stdout.write('25%\x08\x08\x08\x08 ')
49 | sys.stdout.flush()
50 | elif arg[0] == 'h':
51 | sys.stdout.write('50%\x08\x08\x08\x08 ')
52 | sys.stdout.flush()
53 | elif arg[0] == 'x':
54 | sys.stdout.write('75%\x08\x08\x08\x08 ')
55 | sys.stdout.flush()
56 |
57 |
58 | if __name__ == '__main__':
59 | phrase = None
60 | pfunc = None
61 |
62 | parser = OptionParser(usage=usage)
63 | parser.add_option("-t", "--type", type="string", dest="ktype",
64 | help="Specify type of key to create (dsa or rsa)",
65 | metavar="ktype", default=default_values["ktype"])
66 | parser.add_option("-b", "--bits", type="int", dest="bits",
67 | help="Number of bits in the key to create", metavar="bits",
68 | default=default_values["bits"])
69 | parser.add_option("-N", "--new-passphrase", dest="newphrase",
70 | help="Provide new passphrase", metavar="phrase")
71 | parser.add_option("-P", "--old-passphrase", dest="oldphrase",
72 | help="Provide old passphrase", metavar="phrase")
73 | parser.add_option("-f", "--filename", type="string", dest="filename",
74 | help="Filename of the key file", metavar="filename",
75 | default=default_values["filename"])
76 | parser.add_option("-q", "--quiet", default=False, action="store_false",
77 | help="Quiet")
78 | parser.add_option("-v", "--verbose", default=False, action="store_true",
79 | help="Verbose")
80 | parser.add_option("-C", "--comment", type="string", dest="comment",
81 | help="Provide a new comment", metavar="comment",
82 | default=default_values["comment"])
83 |
84 | (options, args) = parser.parse_args()
85 |
86 | if len(sys.argv) == 1:
87 | parser.print_help()
88 | sys.exit(0)
89 |
90 | ktype = options.ktype
91 | bits = options.bits
92 | filename = options.filename
93 | comment = options.comment
94 |
95 | if options.newphrase:
96 | phrase = getattr(options, 'newphrase')
97 |
98 | if options.verbose:
99 | pfunc = progress
100 | sys.stdout.write("Generating priv/pub %s %d bits key pair (%s/%s.pub)..."
101 | % (ktype, bits, filename, filename))
102 | sys.stdout.flush()
103 |
104 | if ktype == 'dsa' and bits > 1024:
105 | raise SSHException("DSA Keys must be 1024 bits")
106 |
107 | if ktype not in key_dispatch_table:
108 | raise SSHException("Unknown %s algorithm to generate keys pair" % ktype)
109 |
110 | # generating private key
111 | prv = key_dispatch_table[ktype].generate(bits=bits, progress_func=pfunc)
112 | prv.write_private_key_file(filename, password=phrase)
113 |
114 | # generating public key
115 | pub = key_dispatch_table[ktype](filename=filename, password=phrase)
116 | with open("%s.pub" % filename, 'w') as f:
117 | f.write("%s %s" % (pub.get_name(), pub.get_base64()))
118 | if options.comment:
119 | f.write(" %s" % comment)
120 |
121 | if options.verbose:
122 | print("done.")
123 |
124 | print("Fingerprint: %d %s %s.pub (%s)" % (
125 | bits, pub.get_fingerprint_sha256_b64(), filename, ktype.upper()
126 | ))
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 socket
22 | import sys
23 | import threading
24 | import traceback
25 |
26 | import paramiko
27 | from paramiko.py3compat import decodebytes
28 |
29 |
30 | # setup logging
31 | paramiko.util.log_to_file('demo_server.log')
32 |
33 | host_key = paramiko.load_private_key_file('test_rsa.key')
34 |
35 | print("Read key: " + host_key.get_fingerprint_sha256_b64())
36 |
37 |
38 | class Server (paramiko.ServerInterface):
39 | # 'data' is the output of base64.b64encode(key)
40 | # (using the "user_rsa_key" files)
41 | data = (b'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp'
42 | b'fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC'
43 | b'KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT'
44 | b'UWT10hcuO4Ks8=')
45 | good_pub_key = paramiko.RSAKey(data=decodebytes(data))
46 |
47 | def __init__(self):
48 | self.event = threading.Event()
49 |
50 | def check_channel_request(self, kind, chanid):
51 | if kind == 'session':
52 | return paramiko.OPEN_SUCCEEDED
53 | return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
54 |
55 | def check_auth_password(self, username, password):
56 | if (username == 'robey') and (password == 'foo'):
57 | return paramiko.AUTH_SUCCESSFUL
58 | return paramiko.AUTH_FAILED
59 |
60 | def check_auth_publickey(self, username, key):
61 | print("Auth attempt with key: " + key.get_fingerprint_sha256_b64())
62 | if (username == 'robey') and (key == self.good_pub_key):
63 | return paramiko.AUTH_SUCCESSFUL
64 | return paramiko.AUTH_FAILED
65 |
66 | def check_auth_gssapi_with_mic(self, username,
67 | gss_authenticated=paramiko.AUTH_FAILED,
68 | cc_file=None):
69 | """
70 | .. note::
71 | We are just checking in `AuthHandler` that the given user is a
72 | valid krb5 principal! We don't check if the krb5 principal is
73 | allowed to log in on the server, because there is no way to do that
74 | in python. So if you develop your own SSH server with paramiko for
75 | a certain platform like Linux, you should call ``krb5_kuserok()`` in
76 | your local kerberos library to make sure that the krb5_principal
77 | has an account on the server and is allowed to log in as a user.
78 |
79 | .. seealso::
80 | `krb5_kuserok() man page
81 | `_
82 | """
83 | if gss_authenticated == paramiko.AUTH_SUCCESSFUL:
84 | return paramiko.AUTH_SUCCESSFUL
85 | return paramiko.AUTH_FAILED
86 |
87 | def check_auth_gssapi_keyex(self, username,
88 | gss_authenticated=paramiko.AUTH_FAILED,
89 | cc_file=None):
90 | if gss_authenticated == paramiko.AUTH_SUCCESSFUL:
91 | return paramiko.AUTH_SUCCESSFUL
92 | return paramiko.AUTH_FAILED
93 |
94 | def enable_auth_gssapi(self):
95 | return True
96 |
97 | def get_allowed_auths(self, username):
98 | return 'gssapi-keyex,gssapi-with-mic,password,publickey'
99 |
100 | def check_channel_shell_request(self, channel):
101 | self.event.set()
102 | return True
103 |
104 | def check_channel_pty_request(self, channel, term, width, height, pixelwidth,
105 | pixelheight, modes):
106 | return True
107 |
108 |
109 | DoGSSAPIKeyExchange = True
110 |
111 | # now connect
112 | try:
113 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
114 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
115 | sock.bind(('', 2200))
116 | except Exception as e:
117 | print('*** Bind failed: ' + str(e))
118 | traceback.print_exc()
119 | sys.exit(1)
120 |
121 | try:
122 | sock.listen(100)
123 | print('Listening for connection ...')
124 | client, addr = sock.accept()
125 | except Exception as e:
126 | print('*** Listen/accept failed: ' + str(e))
127 | traceback.print_exc()
128 | sys.exit(1)
129 |
130 | print('Got a connection!')
131 |
132 | try:
133 | t = paramiko.Transport(client, gss_kex=DoGSSAPIKeyExchange)
134 | t.set_gss_host(socket.getfqdn(""))
135 | try:
136 | t.load_server_moduli()
137 | except:
138 | print('(Failed to load moduli -- gex will be unsupported.)')
139 | raise
140 | t.add_server_key(host_key)
141 | server = Server()
142 | try:
143 | t.start_server(server=server)
144 | except paramiko.SSHException:
145 | print('*** SSH negotiation failed.')
146 | sys.exit(1)
147 |
148 | # wait for auth
149 | chan = t.accept(20)
150 | if chan is None:
151 | print('*** No channel.')
152 | sys.exit(1)
153 | print('Authenticated!')
154 |
155 | server.event.wait(10)
156 | if not server.event.is_set():
157 | print('*** Client never asked for a shell.')
158 | sys.exit(1)
159 |
160 | chan.send('\r\n\r\nWelcome to my dorky little BBS!\r\n\r\n')
161 | chan.send('We are on fire all the time! Hooray! Candy corn for everyone!\r\n')
162 | chan.send('Happy birthday to Robot Dave!\r\n\r\n')
163 | chan.send('Username: ')
164 | f = chan.makefile('rU')
165 | username = f.readline().strip('\r\n')
166 | chan.send('\r\nI don\'t like you, ' + username + '.\r\n')
167 | chan.close()
168 |
169 | except Exception as e:
170 | print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e))
171 | traceback.print_exc()
172 | try:
173 | t.close()
174 | except:
175 | pass
176 | sys.exit(1)
177 |
--------------------------------------------------------------------------------
/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 getpass
24 | import os
25 | import socket
26 | import sys
27 | import traceback
28 |
29 | import paramiko
30 | from paramiko.py3compat import input
31 |
32 |
33 | # setup logging
34 | paramiko.util.log_to_file('demo_sftp.log')
35 |
36 | # Paramiko client configuration
37 | UseGSSAPI = True # enable GSS-API / SSPI authentication
38 | DoGSSAPIKeyExchange = True
39 | Port = 22
40 |
41 | # get hostname
42 | username = ''
43 | if len(sys.argv) > 1:
44 | hostname = sys.argv[1]
45 | if hostname.find('@') >= 0:
46 | username, hostname = hostname.split('@')
47 | else:
48 | hostname = input('Hostname: ')
49 | if len(hostname) == 0:
50 | print('*** Hostname required.')
51 | sys.exit(1)
52 |
53 | if hostname.find(':') >= 0:
54 | hostname, portstr = hostname.split(':')
55 | Port = int(portstr)
56 |
57 |
58 | # get username
59 | if username == '':
60 | default_username = getpass.getuser()
61 | username = input('Username [%s]: ' % default_username)
62 | if len(username) == 0:
63 | username = default_username
64 | if not UseGSSAPI:
65 | password = getpass.getpass('Password for %s@%s: ' % (username, hostname))
66 | else:
67 | password = None
68 |
69 |
70 | # get host key, if we know one
71 | hostkeytype = None
72 | hostkey = None
73 | try:
74 | host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
75 | except IOError:
76 | try:
77 | # try ~/ssh/ too, because windows can't have a folder named ~/.ssh/
78 | host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/ssh/known_hosts'))
79 | except IOError:
80 | print('*** Unable to open host keys file')
81 | host_keys = {}
82 |
83 | if hostname in host_keys:
84 | hostkeytype = host_keys[hostname].keys()[0]
85 | hostkey = host_keys[hostname][hostkeytype]
86 | print('Using host key of type %s' % hostkeytype)
87 |
88 |
89 | # now, connect and use paramiko Transport to negotiate SSH2 across the connection
90 | try:
91 | t = paramiko.Transport((hostname, Port))
92 | t.connect(hostkey, username, password, gss_host=socket.getfqdn(hostname),
93 | gss_auth=UseGSSAPI, gss_kex=DoGSSAPIKeyExchange)
94 | sftp = paramiko.SFTPClient.from_transport(t)
95 |
96 | # dirlist on remote host
97 | dirlist = sftp.listdir('.')
98 | print("Dirlist: %s" % dirlist)
99 |
100 | # copy this demo onto the server
101 | try:
102 | sftp.mkdir("demo_sftp_folder")
103 | except IOError:
104 | print('(assuming demo_sftp_folder/ already exists)')
105 | with sftp.open('demo_sftp_folder/README', 'w') as f:
106 | f.write('This was created by demo_sftp.py.\n')
107 | with open('demo_sftp.py', 'r') as f:
108 | data = f.read()
109 | sftp.open('demo_sftp_folder/demo_sftp.py', 'w').write(data)
110 | print('created demo_sftp_folder/ on the server')
111 |
112 | # copy the README back here
113 | with sftp.open('demo_sftp_folder/README', 'r') as f:
114 | data = f.read()
115 | with open('README_demo_sftp', 'w') as f:
116 | f.write(data)
117 | print('copied README back here')
118 |
119 | # BETTER: use the get() and put() methods
120 | sftp.put('demo_sftp.py', 'demo_sftp_folder/demo_sftp.py')
121 | sftp.get('demo_sftp_folder/README', 'README_demo_sftp')
122 |
123 | t.close()
124 |
125 | except Exception as e:
126 | print('*** Caught exception: %s: %s' % (e.__class__, e))
127 | traceback.print_exc()
128 | try:
129 | t.close()
130 | except:
131 | pass
132 | sys.exit(1)
133 |
--------------------------------------------------------------------------------
/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 getpass
23 | import sys
24 | import traceback
25 | from paramiko.py3compat import input
26 |
27 | import paramiko
28 | try:
29 | import interactive
30 | except ImportError:
31 | from . import interactive
32 |
33 |
34 | # setup logging
35 | paramiko.util.log_to_file('demo_simple.log')
36 | # Paramiko client configuration
37 | UseGSSAPI = paramiko.GSS_AUTH_AVAILABLE # enable "gssapi-with-mic" authentication, if supported
38 | DoGSSAPIKeyExchange = paramiko.GSS_AUTH_AVAILABLE # enable "gssapi-kex" key exchange, if supported
39 | # UseGSSAPI = False
40 | # DoGSSAPIKeyExchange = False
41 | port = 22
42 |
43 | # get hostname
44 | username = ''
45 | if len(sys.argv) > 1:
46 | hostname = sys.argv[1]
47 | if hostname.find('@') >= 0:
48 | username, hostname = hostname.split('@')
49 | else:
50 | hostname = input('Hostname: ')
51 | if len(hostname) == 0:
52 | print('*** Hostname required.')
53 | sys.exit(1)
54 |
55 | if hostname.find(':') >= 0:
56 | hostname, portstr = hostname.split(':')
57 | port = int(portstr)
58 |
59 |
60 | # get username
61 | if username == '':
62 | default_username = getpass.getuser()
63 | username = input('Username [%s]: ' % default_username)
64 | if len(username) == 0:
65 | username = default_username
66 | if not UseGSSAPI and not DoGSSAPIKeyExchange:
67 | password = getpass.getpass('Password for %s@%s: ' % (username, hostname))
68 |
69 |
70 | # now, connect and use paramiko Client to negotiate SSH2 across the connection
71 | try:
72 | client = paramiko.SSHClient()
73 | client.load_system_host_keys()
74 | client.set_missing_host_key_policy(paramiko.WarningPolicy())
75 | print('*** Connecting...')
76 | if not UseGSSAPI and not DoGSSAPIKeyExchange:
77 | client.connect(hostname, port, username, password)
78 | else:
79 | try:
80 | client.connect(hostname, port, username, gss_auth=UseGSSAPI,
81 | gss_kex=DoGSSAPIKeyExchange)
82 | except Exception:
83 | # traceback.print_exc()
84 | password = getpass.getpass('Password for %s@%s: ' % (username, hostname))
85 | client.connect(hostname, port, username, password)
86 |
87 | chan = client.invoke_shell()
88 | print(repr(client.get_transport()))
89 | print('*** Here we go!\n')
90 | interactive.interactive_shell(chan)
91 | chan.close()
92 | client.close()
93 |
94 | except Exception as e:
95 | print('*** Caught exception: %s: %s' % (e.__class__, e))
96 | traceback.print_exc()
97 | try:
98 | client.close()
99 | except:
100 | pass
101 | sys.exit(1)
102 |
--------------------------------------------------------------------------------
/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.decode('utf-8'))
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 socket
31 | import select
32 | import sys
33 | import threading
34 | from optparse import OptionParser
35 |
36 | import paramiko
37 |
38 | SSH_PORT = 22
39 | DEFAULT_PORT = 4000
40 |
41 | g_verbose = True
42 |
43 |
44 | def handler(chan, host, port):
45 | sock = socket.socket()
46 | try:
47 | sock.connect((host, port))
48 | except Exception as e:
49 | verbose('Forwarding request to %s:%d failed: %r' % (host, port, e))
50 | return
51 |
52 | verbose('Connected! Tunnel open %r -> %r -> %r' % (chan.origin_addr,
53 | chan.getpeername(), (host, port)))
54 | while True:
55 | r, w, x = select.select([sock, chan], [], [])
56 | if sock in r:
57 | data = sock.recv(1024)
58 | if len(data) == 0:
59 | break
60 | chan.sendall(data)
61 | if chan in r:
62 | data = chan.recv(1024)
63 | if len(data) == 0:
64 | break
65 | sock.sendall(data)
66 | chan.close()
67 | sock.close()
68 | verbose('Tunnel closed from %r' % (chan.origin_addr,))
69 |
70 |
71 | def reverse_forward_tunnel(server_port, remote_host, remote_port, transport):
72 | transport.request_port_forward('', server_port)
73 | while True:
74 | chan = transport.accept(1000)
75 | if chan is None:
76 | continue
77 | thr = threading.Thread(target=handler, args=(chan, remote_host, remote_port))
78 | thr.daemon = True
79 | thr.start()
80 |
81 |
82 | def verbose(s):
83 | if g_verbose:
84 | print(s)
85 |
86 |
87 | HELP = """\
88 | Set up a reverse forwarding tunnel across an SSH server, using paramiko. A
89 | port on the SSH server (given with -p) is forwarded across an SSH session
90 | back to the local machine, and out to a remote site reachable from this
91 | network. This is similar to the openssh -R option.
92 | """
93 |
94 |
95 | def get_host_port(spec, default_port):
96 | "parse 'hostname:22' into a host and port, with the port optional"
97 | args = (spec.split(':', 1) + [default_port])[:2]
98 | args[1] = int(args[1])
99 | return args[0], args[1]
100 |
101 |
102 | def parse_options():
103 | global g_verbose
104 |
105 | parser = OptionParser(usage='usage: %prog [options] [:]',
106 | version='%prog 1.0', description=HELP)
107 | parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
108 | help='squelch all informational output')
109 | parser.add_option('-p', '--remote-port', action='store', type='int', dest='port',
110 | default=DEFAULT_PORT,
111 | help='port on server to forward (default: %d)' % DEFAULT_PORT)
112 | parser.add_option('-u', '--user', action='store', type='string', dest='user',
113 | default=getpass.getuser(),
114 | help='username for SSH authentication (default: %s)' % getpass.getuser())
115 | parser.add_option('-K', '--key', action='store', type='string', dest='keyfile',
116 | default=None,
117 | help='private key file to use for SSH authentication')
118 | parser.add_option('', '--no-key', action='store_false', dest='look_for_keys', default=True,
119 | help='don\'t look for or use a private key file')
120 | parser.add_option('-P', '--password', action='store_true', dest='readpass', default=False,
121 | help='read password (for key or password auth) from stdin')
122 | parser.add_option('-r', '--remote', action='store', type='string', dest='remote', default=None,
123 | help='remote host and port to forward to', metavar='host:port')
124 | options, args = parser.parse_args()
125 |
126 | if len(args) != 1:
127 | parser.error('Incorrect number of arguments.')
128 | if options.remote is None:
129 | parser.error('Remote address required (-r).')
130 |
131 | g_verbose = options.verbose
132 | server_host, server_port = get_host_port(args[0], SSH_PORT)
133 | remote_host, remote_port = get_host_port(options.remote, SSH_PORT)
134 | return options, (server_host, server_port), (remote_host, remote_port)
135 |
136 |
137 | def main():
138 | options, server, remote = parse_options()
139 |
140 | password = None
141 | if options.readpass:
142 | password = getpass.getpass('Enter SSH password: ')
143 |
144 | client = paramiko.SSHClient()
145 | client.load_system_host_keys()
146 | client.set_missing_host_key_policy(paramiko.WarningPolicy())
147 |
148 | verbose('Connecting to ssh host %s:%d ...' % (server[0], server[1]))
149 | try:
150 | client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile,
151 | look_for_keys=options.look_for_keys, password=password)
152 | except Exception as e:
153 | print('*** Failed to connect to %s:%d: %r' % (server[0], server[1], e))
154 | sys.exit(1)
155 |
156 | verbose('Now forwarding remote port %d to %s:%d ...' % (options.port, remote[0], remote[1]))
157 |
158 | try:
159 | reverse_forward_tunnel(options.port, remote[0], remote[1], client.get_transport())
160 | except KeyboardInterrupt:
161 | print('C-c: Port forwarding stopped.')
162 | sys.exit(0)
163 |
164 |
165 | if __name__ == '__main__':
166 | main()
167 |
--------------------------------------------------------------------------------
/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 | # flake8 strictly controls versions of pyflakes/pycodestyle
2 | flake8==5.0.4;python_version>="3.6"
3 | # for running tests
4 | pytest==4.6.11;python_version<"3.9"
5 | pytest==6.2.5;python_version>="3.9"
6 | mock==2.0.0;python_version<"3.3"
7 |
--------------------------------------------------------------------------------
/doc-requirements.txt:
--------------------------------------------------------------------------------
1 | alabaster==0.7.12
2 | Babel==2.9.1
3 | charset-normalizer==2.0.4
4 | docutils==0.16
5 | idna==3.2
6 | imagesize==1.2.0
7 | Jinja2==3.0.1
8 | MarkupSafe==2.0.1
9 | Pygments==2.10.0
10 | pyparsing==2.4.7
11 | releases==1.6.3
12 | six==1.16.0
13 | semantic_version==2.6.0
14 | snowballstemmer==2.1.0
15 | Sphinx==2.4.4
16 | sphinxcontrib-applehelp==1.0.2
17 | sphinxcontrib-devhelp==1.0.2
18 | sphinxcontrib-htmlhelp==2.0.0
19 | sphinxcontrib-jsmath==1.0.1
20 | sphinxcontrib-qthelp==1.0.3
21 | sphinxcontrib-serializinghtml==1.1.5
22 |
--------------------------------------------------------------------------------
/paramiko/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2003-2011 Robey Pointer
2 | # Copyright (C) 2013-2019 Jeff Forcier
3 | # Copyright (C) 2019-2020 Pierce Lopez
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 | from paramiko.transport import SecurityOptions, Transport
22 | from paramiko.client import (
23 | SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy,
24 | WarningPolicy,
25 | )
26 | from paramiko.auth_handler import AuthHandler
27 | from paramiko.ssh_gss import GSSAuth, GSS_AUTH_AVAILABLE, GSS_EXCEPTIONS
28 | from paramiko.channel import Channel, ChannelFile, ChannelStderrFile, ChannelStdinFile
29 | from paramiko.ssh_exception import (
30 | SSHException, PasswordRequiredException, BadAuthenticationType,
31 | ChannelException, BadHostKeyException, AuthenticationException,
32 | ProxyCommandFailure,
33 | )
34 | from paramiko.server import ServerInterface, SubsystemHandler, InteractiveQuery
35 | from paramiko.rsakey import RSAKey
36 | from paramiko.dsskey import DSSKey
37 | from paramiko.ecdsakey import ECDSAKey
38 | from paramiko.ed25519key import Ed25519Key
39 | from paramiko.sftp import SFTPError
40 | from paramiko.sftp_client import SFTP, SFTPClient
41 | from paramiko.sftp_server import SFTPServer
42 | from paramiko.sftp_attr import SFTPAttributes
43 | from paramiko.sftp_handle import SFTPHandle
44 | from paramiko.sftp_si import SFTPServerInterface
45 | from paramiko.sftp_file import SFTPFile
46 | from paramiko.message import Message
47 | from paramiko.packet import Packetizer
48 | from paramiko.file import BufferedFile
49 | from paramiko.agent import Agent, AgentKey
50 | from paramiko.pkey import (
51 | PKey,
52 | PublicBlob,
53 | load_private_key,
54 | load_private_key_file,
55 | )
56 | from paramiko.hostkeys import HostKeys
57 | from paramiko.config import SSHConfig
58 | from paramiko.proxy import ProxyCommand
59 | from paramiko.common import io_sleep
60 | from paramiko.common import (
61 | AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, OPEN_SUCCEEDED,
62 | OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED,
63 | OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE,
64 | )
65 | from paramiko.sftp import (
66 | SFTP_OK, SFTP_EOF, SFTP_NO_SUCH_FILE, SFTP_PERMISSION_DENIED, SFTP_FAILURE,
67 | SFTP_BAD_MESSAGE, SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST,
68 | SFTP_OP_UNSUPPORTED,
69 | )
70 | from paramiko._version import __version__, __version_info__ # noqa: F401
71 |
72 |
73 | __author__ = "various"
74 | __license__ = "GNU Lesser General Public License (LGPL)"
75 | __pkgname__ = "paramiko-ng"
76 |
77 | __all__ = [
78 | 'Transport',
79 | 'SSHClient',
80 | 'MissingHostKeyPolicy',
81 | 'AutoAddPolicy',
82 | 'RejectPolicy',
83 | 'WarningPolicy',
84 | 'SecurityOptions',
85 | 'AuthHandler',
86 | 'Channel',
87 | 'ChannelFile',
88 | 'ChannelStderrFile',
89 | 'ChannelStdinFile',
90 | 'PKey',
91 | 'RSAKey',
92 | 'DSSKey',
93 | 'ECDSAKey',
94 | 'Ed25519Key',
95 | 'PublicBlob',
96 | 'load_private_key',
97 | 'load_private_key_file',
98 | 'Message',
99 | 'Packetizer',
100 | 'SSHException',
101 | 'AuthenticationException',
102 | 'PasswordRequiredException',
103 | 'BadAuthenticationType',
104 | 'ChannelException',
105 | 'BadHostKeyException',
106 | 'ProxyCommand',
107 | 'ProxyCommandFailure',
108 | 'GSSAuth',
109 | 'GSS_AUTH_AVAILABLE',
110 | 'GSS_EXCEPTIONS',
111 | 'AUTH_SUCCESSFUL',
112 | 'AUTH_PARTIALLY_SUCCESSFUL',
113 | 'AUTH_FAILED',
114 | 'OPEN_SUCCEEDED',
115 | 'OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED',
116 | 'OPEN_FAILED_CONNECT_FAILED',
117 | 'OPEN_FAILED_UNKNOWN_CHANNEL_TYPE',
118 | 'OPEN_FAILED_RESOURCE_SHORTAGE',
119 | 'SFTP',
120 | 'SFTPFile',
121 | 'SFTPHandle',
122 | 'SFTPClient',
123 | 'SFTPServer',
124 | 'SFTPError',
125 | 'SFTPAttributes',
126 | 'SFTPServerInterface',
127 | 'SFTP_OK',
128 | 'SFTP_EOF',
129 | 'SFTP_NO_SUCH_FILE',
130 | 'SFTP_PERMISSION_DENIED',
131 | 'SFTP_FAILURE',
132 | 'SFTP_BAD_MESSAGE',
133 | 'SFTP_NO_CONNECTION',
134 | 'SFTP_CONNECTION_LOST',
135 | 'SFTP_OP_UNSUPPORTED',
136 | 'ServerInterface',
137 | 'SubsystemHandler',
138 | 'InteractiveQuery',
139 | 'BufferedFile',
140 | 'Agent',
141 | 'AgentKey',
142 | 'HostKeys',
143 | 'SSHConfig',
144 | 'io_sleep',
145 | ]
146 |
--------------------------------------------------------------------------------
/paramiko/_version.py:
--------------------------------------------------------------------------------
1 | # last version component is odd for pre-release development, even for stable release
2 | __version_info__ = (2, 9, 0)
3 | __version__ = '.'.join(map(str, __version_info__))
4 |
--------------------------------------------------------------------------------
/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 | def __init__(self, content=bytes()):
30 | self.content = b(content)
31 | self.idx = 0
32 |
33 | def asbytes(self):
34 | return self.content
35 |
36 | def __str__(self):
37 | return self.asbytes()
38 |
39 | def __repr__(self):
40 | return 'BER(\'' + repr(self.content) + '\')'
41 |
42 | def decode(self):
43 | return self.decode_next()
44 |
45 | def decode_next(self):
46 | if self.idx >= len(self.content):
47 | return None
48 | ident = byte_ord(self.content[self.idx])
49 | self.idx += 1
50 | if (ident & 31) == 31:
51 | # identifier > 30
52 | ident = 0
53 | while self.idx < len(self.content):
54 | t = byte_ord(self.content[self.idx])
55 | self.idx += 1
56 | ident = (ident << 7) | (t & 0x7f)
57 | if not (t & 0x80):
58 | break
59 | if self.idx >= len(self.content):
60 | return None
61 | # now fetch length
62 | size = byte_ord(self.content[self.idx])
63 | self.idx += 1
64 | if size & 0x80:
65 | # more complimicated...
66 | # FIXME: theoretically should handle indefinite-length (0x80)
67 | t = size & 0x7f
68 | if self.idx + t > len(self.content):
69 | return None
70 | size = util.inflate_long(
71 | self.content[self.idx: self.idx + t], True)
72 | self.idx += t
73 | if self.idx + size > len(self.content):
74 | # can't fit
75 | return None
76 | data = self.content[self.idx: self.idx + size]
77 | self.idx += size
78 | # now switch on id
79 | if ident == 0x30:
80 | # sequence
81 | return self.decode_sequence(data)
82 | elif ident == 2:
83 | # int
84 | return util.inflate_long(data)
85 | else:
86 | # 1: boolean (00 false, otherwise true)
87 | msg = 'Unknown ber encoding type {:d} (robey is lazy)'
88 | raise BERException(msg.format(ident))
89 |
90 | @staticmethod
91 | def decode_sequence(data):
92 | out = []
93 | ber = BER(data)
94 | while True:
95 | x = ber.decode_next()
96 | if x is None:
97 | break
98 | out.append(x)
99 | return out
100 |
101 | def encode_tlv(self, ident, val):
102 | # no need to support ident > 31 here
103 | self.content += byte_chr(ident)
104 | if len(val) > 0x7f:
105 | lenstr = util.deflate_long(len(val))
106 | self.content += byte_chr(0x80 + len(lenstr)) + lenstr
107 | else:
108 | self.content += byte_chr(len(val))
109 | self.content += val
110 |
111 | def encode(self, x):
112 | if type(x) is bool:
113 | if x:
114 | self.encode_tlv(1, max_byte)
115 | else:
116 | self.encode_tlv(1, zero_byte)
117 | elif (type(x) is int) or (type(x) is long):
118 | self.encode_tlv(2, util.deflate_long(x))
119 | elif type(x) is str:
120 | self.encode_tlv(4, x)
121 | elif (type(x) is list) or (type(x) is tuple):
122 | self.encode_tlv(0x30, self.encode_sequence(x))
123 | else:
124 | raise BERException(
125 | 'Unknown type for encoding: {!r}'.format(type(x))
126 | )
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 | # Use the default level of zlib compression
29 | self.z = zlib.compressobj()
30 |
31 | def __call__(self, data):
32 | return self.z.compress(data) + self.z.flush(zlib.Z_FULL_FLUSH)
33 |
34 |
35 | class ZlibDecompressor (object):
36 | def __init__(self):
37 | self.z = zlib.decompressobj()
38 |
39 | def __call__(self, data):
40 | return self.z.decompress(data)
41 |
--------------------------------------------------------------------------------
/paramiko/ed25519key.py:
--------------------------------------------------------------------------------
1 | # This file is part of paramiko.
2 | #
3 | # Paramiko is free software; you can redistribute it and/or modify it under the
4 | # terms of the GNU Lesser General Public License as published by the Free
5 | # Software Foundation; either version 2.1 of the License, or (at your option)
6 | # any later version.
7 | #
8 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
9 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
10 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | # details.
12 | #
13 | # You should have received a copy of the GNU Lesser General Public License
14 | # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
15 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16 |
17 | from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
18 | from cryptography.hazmat.primitives import serialization
19 | from cryptography.hazmat.primitives.asymmetric import ed25519
20 |
21 | from paramiko.message import Message
22 | from paramiko.pkey import PKey, register_pkey_type
23 | from paramiko.ssh_exception import SSHException
24 |
25 |
26 | @register_pkey_type
27 | class Ed25519Key(PKey):
28 | """
29 | Representation of an `Ed25519 `_ key.
30 |
31 | .. note::
32 | Ed25519 key support was added to OpenSSH in version 6.5.
33 |
34 | .. versionadded:: 2.2
35 | .. versionchanged:: 2.3
36 | Added a ``file_obj`` parameter to match other key classes.
37 | """
38 |
39 | # Legacy file format does not support Ed25519
40 | LEGACY_TYPE = None
41 | OPENSSH_TYPE_PREFIX = 'ssh-ed25519'
42 |
43 | @staticmethod
44 | def is_supported():
45 | """
46 | Check if the openssl version pyca/cryptography is linked against
47 | supports Ed25519 keys.
48 | """
49 | try:
50 | ed25519.Ed25519PublicKey.from_public_bytes(b"\x00" * 32)
51 | except UnsupportedAlgorithm:
52 | return False # openssl < 1.1.0
53 | return True
54 |
55 | def __init__(self, msg=None, data=None, filename=None, password=None,
56 | file_obj=None, _raw=None):
57 | self.public_blob = None
58 | verifying_key = None
59 | signing_key = None
60 |
61 | if msg is None and data is not None:
62 | msg = Message(data)
63 | if msg is not None:
64 | self._check_type_and_load_cert(
65 | msg=msg,
66 | key_type="ssh-ed25519",
67 | cert_type="ssh-ed25519-cert-v01@openssh.com",
68 | )
69 | verifying_key = ed25519.Ed25519PublicKey.from_public_bytes(msg.get_binary())
70 | elif filename is not None:
71 | _raw = self._from_private_key_file(filename, password)
72 | elif file_obj is not None:
73 | _raw = self._from_private_key(file_obj, password)
74 | if _raw is not None:
75 | signing_key = self._decode_key(_raw)
76 |
77 | if signing_key is None and verifying_key is None:
78 | raise ValueError("need a key")
79 |
80 | self._signing_key = signing_key
81 | self._verifying_key = verifying_key or signing_key.public_key()
82 |
83 | def _decode_key(self, _raw):
84 | pkformat, data = _raw
85 | if pkformat != self.FORMAT_OPENSSH:
86 | raise SSHException("Invalid key format")
87 |
88 | message = Message(data)
89 | public = message.get_binary()
90 | key_data = message.get_binary()
91 | comment = message.get_binary() # noqa: F841
92 |
93 | # The second half of the key data is yet another copy of the public key...
94 | signing_key = ed25519.Ed25519PrivateKey.from_private_bytes(key_data[:32])
95 |
96 | # Verify that all the public keys are the same...
97 | derived_public = signing_key.public_key().public_bytes(
98 | serialization.Encoding.Raw,
99 | serialization.PublicFormat.Raw,
100 | )
101 | if public != key_data[32:] or public != derived_public:
102 | raise SSHException("Invalid key public part mis-match")
103 |
104 | return signing_key
105 |
106 | def asbytes(self):
107 | public_bytes = self._verifying_key.public_bytes(
108 | serialization.Encoding.Raw,
109 | serialization.PublicFormat.Raw,
110 | )
111 | m = Message()
112 | m.add_string("ssh-ed25519")
113 | m.add_string(public_bytes)
114 | return m.asbytes()
115 |
116 | def get_name(self):
117 | return "ssh-ed25519"
118 |
119 | def get_bits(self):
120 | return 256
121 |
122 | def can_sign(self):
123 | return self._signing_key is not None
124 |
125 | def sign_ssh_data(self, data):
126 | m = Message()
127 | m.add_string("ssh-ed25519")
128 | m.add_string(self._signing_key.sign(data))
129 | return m
130 |
131 | def verify_ssh_sig(self, data, msg):
132 | if msg.get_text() != "ssh-ed25519":
133 | return False
134 | try:
135 | self._verifying_key.verify(msg.get_binary(), data)
136 | except InvalidSignature:
137 | return False
138 | else:
139 | return True
140 |
--------------------------------------------------------------------------------
/paramiko/kex_curve25519.py:
--------------------------------------------------------------------------------
1 | # This file is part of paramiko.
2 | #
3 | # Paramiko is free software; you can redistribute it and/or modify it under the
4 | # terms of the GNU Lesser General Public License as published by the Free
5 | # Software Foundation; either version 2.1 of the License, or (at your option)
6 | # any later version.
7 | #
8 | # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
9 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
10 | # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | # details.
12 | #
13 | # You should have received a copy of the GNU Lesser General Public License
14 | # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
15 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16 |
17 | """
18 | Key exchange using DJB's Curve25519. Originally introduced in OpenSSH 6.5
19 | """
20 |
21 | # Author: Dan Fuhry
22 |
23 | from hashlib import sha256
24 |
25 | from paramiko.message import Message
26 | from paramiko.py3compat import byte_chr, long
27 | from paramiko.ssh_exception import SSHException
28 | from cryptography.hazmat.primitives.asymmetric import x25519
29 | from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
30 | from cryptography.exceptions import UnsupportedAlgorithm
31 | from binascii import hexlify
32 |
33 | _MSG_KEXC25519_INIT, _MSG_KEXC25519_REPLY = range(30, 32)
34 | c_MSG_KEXC25519_INIT, c_MSG_KEXC25519_REPLY = [
35 | byte_chr(c) for c in range(30, 32)
36 | ]
37 |
38 |
39 | class KexCurve25519(object):
40 | name = "curve25519-sha256@libssh.org"
41 | hash_algo = sha256
42 | K = None
43 |
44 | def __init__(self, transport):
45 | self.transport = transport
46 |
47 | self.P = long(0)
48 | # Client public key
49 | self.Q_C = None
50 | # Server public key
51 | self.Q_S = None
52 |
53 | def start_kex(self):
54 | self._generate_key_pair()
55 | if self.transport.server_mode:
56 | self.transport._expect_packet(_MSG_KEXC25519_INIT)
57 | return
58 | m = Message()
59 | m.add_byte(c_MSG_KEXC25519_INIT)
60 | Q_C_bytes = self.Q_C.public_bytes(
61 | encoding=Encoding.Raw, format=PublicFormat.Raw
62 | )
63 | m.add_string(Q_C_bytes)
64 | self.transport._send_message(m)
65 | self.transport._expect_packet(_MSG_KEXC25519_REPLY)
66 |
67 | def parse_next(self, ptype, m):
68 | if self.transport.server_mode and (ptype == _MSG_KEXC25519_INIT):
69 | return self._parse_kexc25519_init(m)
70 | elif not self.transport.server_mode and (
71 | ptype == _MSG_KEXC25519_REPLY
72 | ):
73 |
74 | return self._parse_kexc25519_reply(m)
75 | msg = "KexCurve25519 asked to handle packet type {:d}"
76 | raise SSHException(msg.format(ptype))
77 |
78 | @staticmethod
79 | def is_supported():
80 | """
81 | Check if the openssl version pyca/cryptography is linked against
82 | supports curve25519 key agreement.
83 | """
84 | try:
85 | x25519.X25519PublicKey.from_public_bytes(b"\x00" * 32)
86 | except UnsupportedAlgorithm:
87 | return False # openssl < 1.1.0
88 |
89 | return True
90 |
91 | is_available = is_supported # for compatibility with upstream's variant
92 |
93 | # ...internals...
94 |
95 | def _generate_key_pair(self):
96 | while True:
97 | self.P = x25519.X25519PrivateKey.generate()
98 | pub = self.P.public_key().public_bytes(
99 | encoding=Encoding.Raw, format=PublicFormat.Raw
100 | )
101 | if len(pub) != 32:
102 | continue
103 |
104 | if self.transport.server_mode:
105 | self.Q_S = self.P.public_key()
106 | else:
107 | self.Q_C = self.P.public_key()
108 | break
109 |
110 | def _parse_kexc25519_reply(self, m):
111 | # client mode
112 |
113 | # 3 fields in response:
114 | # - KEX host key
115 | # - Ephemeral (Curve25519) key
116 | # - Signature
117 | K_S = m.get_string()
118 | self.Q_S = x25519.X25519PublicKey.from_public_bytes(m.get_string())
119 | sig = m.get_binary()
120 |
121 | # Compute shared secret
122 | K = self.P.exchange(self.Q_S)
123 | K = long(hexlify(K), 16)
124 |
125 | hm = Message()
126 | hm.add(
127 | self.transport.local_version,
128 | self.transport.remote_version,
129 | self.transport.local_kex_init,
130 | self.transport.remote_kex_init,
131 | )
132 |
133 | # "hm" is used as the initial transport key
134 | hm.add_string(K_S)
135 | hm.add_string(
136 | self.Q_C.public_bytes(
137 | encoding=Encoding.Raw, format=PublicFormat.Raw
138 | )
139 | )
140 | hm.add_string(
141 | self.Q_S.public_bytes(
142 | encoding=Encoding.Raw, format=PublicFormat.Raw
143 | )
144 | )
145 | hm.add_mpint(K)
146 | self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
147 | # Verify that server signed kex message with its own pubkey
148 | self.transport._verify_key(K_S, sig)
149 | self.transport._activate_outbound()
150 |
151 | def _parse_kexc25519_init(self, m):
152 | # server mode
153 |
154 | # Only one field in the client's message, which is their public key
155 | Q_C_bytes = m.get_string()
156 | self.Q_C = x25519.X25519PublicKey.from_public_bytes(Q_C_bytes)
157 |
158 | # Compute shared secret
159 | K = self.P.exchange(self.Q_C)
160 | K = long(hexlify(K), 16)
161 |
162 | # Prepare hostkey
163 | K_S = self.transport.get_server_key().asbytes()
164 |
165 | # Compute initial transport key
166 | hm = Message()
167 | hm.add(
168 | self.transport.remote_version,
169 | self.transport.local_version,
170 | self.transport.remote_kex_init,
171 | self.transport.local_kex_init,
172 | )
173 |
174 | hm.add_string(K_S)
175 | hm.add_string(Q_C_bytes)
176 | hm.add_string(
177 | self.Q_S.public_bytes(
178 | encoding=Encoding.Raw, format=PublicFormat.Raw
179 | )
180 | )
181 | hm.add_mpint(K)
182 | H = self.hash_algo(hm.asbytes()).digest()
183 | self.transport._set_K_H(K, H)
184 |
185 | # Compute signature
186 | sig = self.transport.get_server_key().sign_ssh_data(H)
187 | # construct reply
188 | m = Message()
189 | m.add_byte(c_MSG_KEXC25519_REPLY)
190 | m.add_string(K_S)
191 | m.add_string(
192 | self.Q_S.public_bytes(
193 | encoding=Encoding.Raw, format=PublicFormat.Raw
194 | )
195 | )
196 | m.add_string(sig)
197 | self.transport._send_message(m)
198 | self.transport._activate_outbound()
199 |
--------------------------------------------------------------------------------
/paramiko/kex_ecdh_nist.py:
--------------------------------------------------------------------------------
1 | """
2 | Ephemeral Elliptic Curve Diffie-Hellman (ECDH) key exchange
3 | RFC 5656, Section 4
4 | """
5 |
6 | from hashlib import sha256, sha384, sha512
7 | from paramiko.message import Message
8 | from paramiko.py3compat import byte_chr, long
9 | from paramiko.ssh_exception import SSHException
10 | from cryptography.hazmat.backends import default_backend
11 | from cryptography.hazmat.primitives.asymmetric import ec
12 | from cryptography.hazmat.primitives import serialization
13 | from binascii import hexlify
14 |
15 | _MSG_KEXECDH_INIT, _MSG_KEXECDH_REPLY = range(30, 32)
16 | c_MSG_KEXECDH_INIT, c_MSG_KEXECDH_REPLY = [byte_chr(c) for c in range(30, 32)]
17 |
18 |
19 | class KexNistp256(object):
20 |
21 | name = "ecdh-sha2-nistp256"
22 | hash_algo = sha256
23 | curve = ec.SECP256R1()
24 |
25 | def __init__(self, transport):
26 | self.transport = transport
27 | # private key, client public and server public keys
28 | self.P = long(0)
29 | self.Q_C = None
30 | self.Q_S = None
31 |
32 | def start_kex(self):
33 | self._generate_key_pair()
34 | if self.transport.server_mode:
35 | self.transport._expect_packet(_MSG_KEXECDH_INIT)
36 | return
37 | m = Message()
38 | m.add_byte(c_MSG_KEXECDH_INIT)
39 | # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
40 | m.add_string(self.Q_C.public_bytes(serialization.Encoding.X962,
41 | serialization.PublicFormat.UncompressedPoint))
42 | self.transport._send_message(m)
43 | self.transport._expect_packet(_MSG_KEXECDH_REPLY)
44 |
45 | def parse_next(self, ptype, m):
46 | if self.transport.server_mode and (ptype == _MSG_KEXECDH_INIT):
47 | return self._parse_kexecdh_init(m)
48 | elif not self.transport.server_mode and (ptype == _MSG_KEXECDH_REPLY):
49 | return self._parse_kexecdh_reply(m)
50 | raise SSHException(
51 | 'KexECDH asked to handle packet type {:d}'.format(ptype)
52 | )
53 |
54 | def _generate_key_pair(self):
55 | self.P = ec.generate_private_key(self.curve, default_backend())
56 | if self.transport.server_mode:
57 | self.Q_S = self.P.public_key()
58 | return
59 | self.Q_C = self.P.public_key()
60 |
61 | def _parse_kexecdh_init(self, m):
62 | Q_C_bytes = m.get_string()
63 | self.Q_C = ec.EllipticCurvePublicKey.from_encoded_point(
64 | self.curve, Q_C_bytes
65 | )
66 | K_S = self.transport.get_server_key().asbytes()
67 | K = self.P.exchange(ec.ECDH(), self.Q_C)
68 | K = long(hexlify(K), 16)
69 | # compute exchange hash
70 | hm = Message()
71 | hm.add(self.transport.remote_version, self.transport.local_version,
72 | self.transport.remote_kex_init, self.transport.local_kex_init)
73 | hm.add_string(K_S)
74 | hm.add_string(Q_C_bytes)
75 | # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
76 | Q_S_bytes = self.Q_S.public_bytes(serialization.Encoding.X962,
77 | serialization.PublicFormat.UncompressedPoint)
78 | hm.add_string(Q_S_bytes)
79 | hm.add_mpint(long(K))
80 | H = self.hash_algo(hm.asbytes()).digest()
81 | self.transport._set_K_H(K, H)
82 | sig = self.transport.get_server_key().sign_ssh_data(H)
83 | # construct reply
84 | m = Message()
85 | m.add_byte(c_MSG_KEXECDH_REPLY)
86 | m.add_string(K_S)
87 | m.add_string(Q_S_bytes)
88 | m.add_string(sig)
89 | self.transport._send_message(m)
90 | self.transport._activate_outbound()
91 |
92 | def _parse_kexecdh_reply(self, m):
93 | K_S = m.get_string()
94 | Q_S_bytes = m.get_string()
95 | self.Q_S = ec.EllipticCurvePublicKey.from_encoded_point(
96 | self.curve, Q_S_bytes
97 | )
98 | sig = m.get_binary()
99 | K = self.P.exchange(ec.ECDH(), self.Q_S)
100 | K = long(hexlify(K), 16)
101 | # compute exchange hash and verify signature
102 | hm = Message()
103 | hm.add(self.transport.local_version, self.transport.remote_version,
104 | self.transport.local_kex_init, self.transport.remote_kex_init)
105 | hm.add_string(K_S)
106 | # SEC1: V2.0 2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion
107 | hm.add_string(self.Q_C.public_bytes(serialization.Encoding.X962,
108 | serialization.PublicFormat.UncompressedPoint))
109 | hm.add_string(Q_S_bytes)
110 | hm.add_mpint(K)
111 | self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
112 | self.transport._verify_key(K_S, sig)
113 | self.transport._activate_outbound()
114 |
115 |
116 | class KexNistp384(KexNistp256):
117 | name = "ecdh-sha2-nistp384"
118 | hash_algo = sha384
119 | curve = ec.SECP384R1()
120 |
121 |
122 | class KexNistp521(KexNistp256):
123 | name = "ecdh-sha2-nistp521"
124 | hash_algo = sha512
125 | curve = ec.SECP521R1()
126 |
--------------------------------------------------------------------------------
/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 # noqa
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 | msg = "KexGroup1 asked to handle packet type {:d}"
77 | raise SSHException(msg.format(ptype))
78 |
79 | # ...internals...
80 |
81 | def _generate_x(self):
82 | # generate an "x" (1 < x < q), where q is (p-1)/2.
83 | # p is a 128-byte (1024-bit) number, where the first 64 bits are 1.
84 | # therefore q can be approximated as a 2^1023. we drop the subset of
85 | # potential x where the first 63 bits are 1, because some of those
86 | # will be larger than q (but this is a tiny tiny subset of
87 | # potential x).
88 | while 1:
89 | x_bytes = os.urandom(128)
90 | x_bytes = byte_mask(x_bytes[0], 0x7f) + x_bytes[1:]
91 | if (x_bytes[:8] != b7fffffffffffffff and
92 | x_bytes[:8] != b0000000000000000):
93 | break
94 | self.x = util.inflate_long(x_bytes)
95 |
96 | def _parse_kexdh_reply(self, m):
97 | # client mode
98 | host_key = m.get_string()
99 | self.f = m.get_mpint()
100 | if (self.f < 1) or (self.f > self.P - 1):
101 | raise SSHException('Server kex "f" is out of range')
102 | sig = m.get_binary()
103 | K = pow(self.f, self.x, self.P)
104 | # okay, build up the hash H of
105 | # (V_C || V_S || I_C || I_S || K_S || e || f || K)
106 | hm = Message()
107 | hm.add(self.transport.local_version, self.transport.remote_version,
108 | self.transport.local_kex_init, self.transport.remote_kex_init)
109 | hm.add_string(host_key)
110 | hm.add_mpint(self.e)
111 | hm.add_mpint(self.f)
112 | hm.add_mpint(K)
113 | self.transport._set_K_H(K, self.hash_algo(hm.asbytes()).digest())
114 | self.transport._verify_key(host_key, sig)
115 | self.transport._activate_outbound()
116 |
117 | def _parse_kexdh_init(self, m):
118 | # server mode
119 | self.e = m.get_mpint()
120 | if (self.e < 1) or (self.e > self.P - 1):
121 | raise SSHException('Client kex "e" is out of range')
122 | K = pow(self.e, self.x, self.P)
123 | key = self.transport.get_server_key().asbytes()
124 | # okay, build up the hash H of
125 | # (V_C || V_S || I_C || I_S || K_S || e || f || K)
126 | hm = Message()
127 | hm.add(self.transport.remote_version, self.transport.local_version,
128 | self.transport.remote_kex_init, self.transport.local_kex_init)
129 | hm.add_string(key)
130 | hm.add_mpint(self.e)
131 | hm.add_mpint(self.f)
132 | hm.add_mpint(K)
133 | H = self.hash_algo(hm.asbytes()).digest()
134 | self.transport._set_K_H(K, H)
135 | # sign it
136 | sig = self.transport.get_server_key().sign_ssh_data(H)
137 | # send reply
138 | m = Message()
139 | m.add_byte(c_MSG_KEXDH_REPLY)
140 | m.add_string(key)
141 | m.add_mpint(self.f)
142 | m.add_string(sig)
143 | self.transport._send_message(m)
144 | self.transport._activate_outbound()
145 |
--------------------------------------------------------------------------------
/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 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 | 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, sha256
26 |
27 |
28 | class KexGroup14(KexGroup1):
29 |
30 | # http://tools.ietf.org/html/rfc3526#section-3
31 | P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF # noqa
32 | G = 2
33 |
34 | name = 'diffie-hellman-group14-sha1'
35 | hash_algo = sha1
36 |
37 |
38 | class KexGroup14SHA256(KexGroup14):
39 | name = 'diffie-hellman-group14-sha256'
40 | hash_algo = sha256
41 |
--------------------------------------------------------------------------------
/paramiko/kex_group16.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2019 Edgar Sousa
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 | 4096 bit key halves, using a known "p" prime and "g" generator.
22 | """
23 |
24 | from paramiko.kex_group1 import KexGroup1
25 | from hashlib import sha512
26 |
27 |
28 | class KexGroup16SHA512(KexGroup1):
29 |
30 | # http://tools.ietf.org/html/rfc3526#section-5
31 | P = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF # noqa: E501
32 | G = 2
33 |
34 | name = "diffie-hellman-group16-sha512"
35 | hash_algo = sha512
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 | import threading
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, lock):
123 | self._set = False
124 | self._partner = None
125 | self._pipe = pipe
126 | self._lock = lock
127 |
128 | def set(self):
129 | with self._lock:
130 | self._set = True
131 | if not self._partner._set:
132 | self._pipe.set()
133 |
134 | def clear(self):
135 | with self._lock:
136 | self._set = False
137 | if not self._partner._set:
138 | self._pipe.clear()
139 |
140 |
141 | def make_or_pipe(pipe):
142 | """
143 | wraps a pipe into two pipe-like objects which are "or"d together to
144 | affect the real pipe. if either returned pipe is set, the wrapped pipe
145 | is set. when both are cleared, the wrapped pipe is cleared.
146 | """
147 | lock = threading.Lock()
148 | p1 = OrPipe(pipe, lock)
149 | p2 = OrPipe(pipe, lock)
150 | p1._partner = p2
151 | p2._partner = p1
152 | return p1, p2
153 |
--------------------------------------------------------------------------------
/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 |
29 |
30 | def _roll_random(n):
31 | """returns a random # from 0 to N-1"""
32 | bits = util.bit_length(n - 1)
33 | byte_count = (bits + 7) // 8
34 | hbyte_mask = pow(2, bits % 8) - 1
35 |
36 | # so here's the plan:
37 | # we fetch as many random bits as we'd need to fit N-1, and if the
38 | # generated number is >= N, we try again. in the worst case (N-1 is a
39 | # power of 2), we have slightly better than 50% odds of getting one that
40 | # fits, so i can't guarantee that this loop will ever finish, but the odds
41 | # of it looping forever should be infinitesimal.
42 | while True:
43 | x = os.urandom(byte_count)
44 | if hbyte_mask > 0:
45 | x = byte_mask(x[0], hbyte_mask) + x[1:]
46 | num = util.inflate_long(x, 1)
47 | if num < n:
48 | break
49 | return num
50 |
51 |
52 | class ModulusPack (object):
53 | """
54 | convenience object for holding the contents of the /etc/ssh/moduli file,
55 | on systems that have such a file.
56 | """
57 |
58 | def __init__(self):
59 | # pack is a hash of: bits -> [ (generator, modulus) ... ]
60 | self.pack = {}
61 | self.discarded = []
62 |
63 | def _parse_modulus(self, line):
64 | timestamp, mod_type, tests, tries, size, generator, modulus = \
65 | 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 (
78 | mod_type < 2 or
79 | tests < 4 or
80 | (tests & 4 and tests < 8 and tries < 100)
81 | ):
82 | self.discarded.append(
83 | (modulus, 'does not meet basic requirements'))
84 | return
85 | if generator == 0:
86 | generator = 2
87 |
88 | # there's a bug in the ssh "moduli" file (yeah, i know: shock! dismay!
89 | # call cnn!) where it understates the bit lengths of these primes by 1.
90 | # this is okay.
91 | bl = util.bit_length(modulus)
92 | if (bl != size) and (bl != size + 1):
93 | self.discarded.append(
94 | (modulus, 'incorrectly reported bit length {}'.format(size)))
95 | return
96 | if bl not in self.pack:
97 | self.pack[bl] = []
98 | self.pack[bl].append((generator, modulus))
99 |
100 | def read_file(self, filename):
101 | """
102 | :raises IOError: passed from any file operations that fail.
103 | """
104 | self.pack = {}
105 | with open(filename, 'r') as f:
106 | for line in f:
107 | line = line.strip()
108 | if (len(line) == 0) or (line[0] == '#'):
109 | continue
110 | try:
111 | self._parse_modulus(line)
112 | except:
113 | continue
114 |
115 | def get_modulus(self, min, prefer, max):
116 | bitsizes = sorted(self.pack.keys())
117 | if len(bitsizes) == 0:
118 | raise SSHException('no moduli available')
119 | good = -1
120 | # find nearest bitsize >= preferred
121 | for b in bitsizes:
122 | if (b >= prefer) and (b <= max) and (b < good or good == -1):
123 | good = b
124 | # if that failed, find greatest bitsize >= min
125 | if good == -1:
126 | for b in bitsizes:
127 | if (b >= min) and (b <= max) and (b > good):
128 | good = b
129 | if good == -1:
130 | # their entire (min, max) range has no intersection with our range.
131 | # if their range is below ours, pick the smallest. otherwise pick
132 | # the largest. it'll be out of their range requirement either way,
133 | # but we'll be sending them the closest one we have.
134 | good = bitsizes[0]
135 | if min > good:
136 | good = bitsizes[-1]
137 | # now pick a random modulus of this bitsize
138 | n = _roll_random(len(self.pack[good]))
139 | return self.pack[good][n]
140 |
--------------------------------------------------------------------------------
/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 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 os
21 | import sys
22 | import time
23 | import socket
24 |
25 | from paramiko.ssh_exception import ProxyCommandFailure
26 | from paramiko.util import ClosingContextManager, poll_read
27 |
28 |
29 | class ProxyCommand(ClosingContextManager):
30 | """
31 | Wraps a subprocess running ProxyCommand-driven programs.
32 |
33 | This class implements a the socket-like interface needed by the
34 | `.Transport` and `.Packetizer` classes. Using this class instead of a
35 | regular socket makes it possible to talk with a Popen'd command that will
36 | proxy traffic between the client and a server hosted in another machine.
37 |
38 | Instances of this class may be used as context managers.
39 | """
40 | def __init__(self, command_line):
41 | """
42 | Create a new CommandProxy instance. The instance created by this
43 | class can be passed as an argument to the `.Transport` class.
44 |
45 | :param str command_line:
46 | the command that should be executed and used as the proxy.
47 | """
48 | # NOTE: subprocess import done lazily so platforms without it (e.g.
49 | # GAE) can still import us during overall Paramiko load.
50 | from subprocess import Popen, PIPE
51 | self.cmd = command_line
52 | self.process = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE,
53 | bufsize=0, shell=True)
54 | self.timeout = None
55 |
56 | def send(self, content):
57 | """
58 | Write the content received from the SSH client to the standard
59 | input of the forked command.
60 |
61 | :param str content: string to be sent to the forked command
62 | """
63 | try:
64 | self.process.stdin.write(content)
65 | except IOError as e:
66 | # There was a problem with the child process. It probably
67 | # died and we can't proceed. The best option here is to
68 | # raise an exception informing the user that the informed
69 | # ProxyCommand is not working.
70 | raise ProxyCommandFailure(self.cmd, e.strerror)
71 | return len(content)
72 |
73 | def recv(self, size):
74 | """
75 | Read from the standard output of the forked program.
76 |
77 | :param int size: how many chars should be read
78 |
79 | :return: the string of bytes read, which may be shorter than requested
80 | """
81 | buffer = b''
82 | try:
83 | if sys.platform == 'win32':
84 | # windows does not support select() on pipes (only on sockets)
85 | return os.read(self.process.stdout.fileno(), size)
86 |
87 | start = time.time()
88 | while len(buffer) < size:
89 | if self.closed:
90 | if buffer:
91 | return buffer
92 | raise EOFError()
93 |
94 | select_timeout = None
95 | if self.timeout is not None:
96 | elapsed = (time.time() - start)
97 | if elapsed >= self.timeout:
98 | raise socket.timeout()
99 | select_timeout = self.timeout - elapsed
100 |
101 | r = poll_read([self.process.stdout], select_timeout)
102 | if r and r[0] == self.process.stdout:
103 | buffer += os.read(self.process.stdout.fileno(), size - len(buffer))
104 |
105 | return buffer
106 |
107 | except socket.timeout:
108 | if buffer:
109 | # Don't raise socket.timeout, return partial result instead
110 | return buffer
111 | raise # socket.timeout is a subclass of IOError
112 | except IOError as e:
113 | raise ProxyCommandFailure(self.cmd, e.strerror)
114 |
115 | def close(self):
116 | if self.process.poll() is None:
117 | try:
118 | self.process.terminate()
119 | except OSError:
120 | pass
121 |
122 | @property
123 | def closed(self):
124 | return self.process.poll() is not None
125 |
126 | @property
127 | def _closed(self):
128 | # Concession to Python 3 socket-like API
129 | return self.closed
130 |
131 | def settimeout(self, timeout):
132 | self.timeout = timeout
133 |
--------------------------------------------------------------------------------
/paramiko/py3compat.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import base64
3 |
4 | __all__ = [
5 | "PY2",
6 | "StringIO",
7 | "b",
8 | "builtins",
9 | "byte_chr",
10 | "byte_mask",
11 | "byte_ord",
12 | "decodebytes",
13 | "encodebytes",
14 | "input",
15 | "integer_types",
16 | "long",
17 | "string_types",
18 | "text_type",
19 | "u",
20 | ]
21 |
22 | PY2 = sys.version_info[0] < 3
23 |
24 | if PY2:
25 | string_types = basestring # NOQA
26 | text_type = unicode # NOQA
27 | integer_types = (int, long) # NOQA
28 | long = long # NOQA
29 | input = raw_input # NOQA
30 | decodebytes = base64.decodestring
31 | encodebytes = base64.encodestring
32 |
33 | import __builtin__ as builtins
34 |
35 | byte_ord = ord # NOQA
36 | byte_chr = chr # NOQA
37 |
38 | def byte_mask(c, mask):
39 | return chr(ord(c) & mask)
40 |
41 | def b(s):
42 | """cast unicode or bytes to bytes"""
43 | if isinstance(s, (str, buffer)): # noqa: F821
44 | return s
45 | elif isinstance(s, unicode): # NOQA
46 | return s.encode('utf-8')
47 | else:
48 | raise TypeError("Expected unicode or bytes, got {!r}".format(s))
49 |
50 | def u(s):
51 | """cast bytes or unicode to unicode"""
52 | if isinstance(s, unicode): # NOQA
53 | return s
54 | elif isinstance(s, (str, buffer)): # noqa: F821
55 | return s.decode('utf-8')
56 | else:
57 | raise TypeError("Expected unicode or bytes, got {!r}".format(s))
58 |
59 | import cStringIO
60 | StringIO = cStringIO.StringIO
61 |
62 |
63 | else: # python 3+
64 | import struct
65 | import builtins
66 | string_types = str
67 | text_type = str
68 | integer_types = int
69 | input = input
70 | decodebytes = base64.decodebytes
71 | encodebytes = base64.encodebytes
72 |
73 | class long(int):
74 | pass
75 |
76 | def byte_ord(c):
77 | # In case we're handed a string instead of an int.
78 | if not isinstance(c, int):
79 | c = ord(c)
80 | return c
81 |
82 | def byte_chr(c):
83 | assert isinstance(c, int)
84 | return struct.pack('B', c)
85 |
86 | def byte_mask(c, mask):
87 | assert isinstance(c, int)
88 | return struct.pack('B', c & mask)
89 |
90 | def b(s):
91 | """cast unicode or bytes to bytes"""
92 | if isinstance(s, bytes):
93 | return s
94 | elif isinstance(s, str):
95 | return s.encode('utf-8')
96 | else:
97 | raise TypeError("Expected unicode or bytes, got {!r}".format(s))
98 |
99 | def u(s):
100 | """cast bytes or unicode to unicode"""
101 | if isinstance(s, bytes):
102 | return s.decode('utf-8')
103 | elif isinstance(s, str):
104 | return s
105 | else:
106 | raise TypeError("Expected unicode or bytes, got {!r}".format(s))
107 |
108 | import io
109 | StringIO = io.StringIO
110 |
--------------------------------------------------------------------------------
/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 | from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
24 | from cryptography.hazmat.backends import default_backend
25 | from cryptography.hazmat.primitives import hashes, serialization
26 | from cryptography.hazmat.primitives.asymmetric import rsa, padding
27 |
28 | from paramiko.message import Message
29 | from paramiko.pkey import PKey, register_pkey_type
30 | from paramiko.ssh_exception import SSHException
31 |
32 |
33 | @register_pkey_type
34 | class RSAKey(PKey):
35 | """
36 | Representation of an RSA key which can be used to sign and verify SSH2 data.
37 | """
38 |
39 | LEGACY_TYPE = "RSA"
40 | OPENSSH_TYPE_PREFIX = "ssh-rsa"
41 |
42 | def __init__(self, msg=None, data=None, filename=None, password=None,
43 | key=None, file_obj=None, _raw=None):
44 | self.key = None
45 | self.public_blob = None
46 | if file_obj is not None:
47 | _raw = self._from_private_key(file_obj, password)
48 | if filename is not None:
49 | _raw = self._from_private_key_file(filename, password)
50 | if _raw is not None:
51 | self._decode_key(_raw)
52 | return
53 | if (msg is None) and (data is not None):
54 | msg = Message(data)
55 | if key is not None:
56 | self.key = key
57 | else:
58 | self._check_type_and_load_cert(
59 | msg=msg,
60 | key_type='ssh-rsa',
61 | cert_type='ssh-rsa-cert-v01@openssh.com',
62 | )
63 | self.key = rsa.RSAPublicNumbers(
64 | e=msg.get_mpint(), n=msg.get_mpint()
65 | ).public_key(default_backend())
66 |
67 | @property
68 | def size(self):
69 | return self.key.key_size
70 |
71 | @property
72 | def public_numbers(self):
73 | if isinstance(self.key, rsa.RSAPrivateKey):
74 | return self.key.private_numbers().public_numbers
75 | else:
76 | return self.key.public_numbers()
77 |
78 | def asbytes(self):
79 | m = Message()
80 | m.add_string('ssh-rsa')
81 | m.add_mpint(self.public_numbers.e)
82 | m.add_mpint(self.public_numbers.n)
83 | return m.asbytes()
84 |
85 | def get_name(self):
86 | return 'ssh-rsa'
87 |
88 | def get_bits(self):
89 | return self.size
90 |
91 | def can_sign(self):
92 | return isinstance(self.key, rsa.RSAPrivateKey)
93 |
94 | def sign_ssh_data(self, data):
95 | sig = self.key.sign(
96 | data,
97 | padding=padding.PKCS1v15(),
98 | algorithm=hashes.SHA1(),
99 | )
100 |
101 | m = Message()
102 | m.add_string('ssh-rsa')
103 | m.add_string(sig)
104 | return m
105 |
106 | def verify_ssh_sig(self, data, msg):
107 | if msg.get_text() != 'ssh-rsa':
108 | return False
109 | key = self.key
110 | if isinstance(key, rsa.RSAPrivateKey):
111 | key = key.public_key()
112 |
113 | # pad received signature with leading zeros, key.verify() expects
114 | # a signature of key_size bits (e.g. PuTTY doesn't pad)
115 | sign = msg.get_binary()
116 | diff = key.key_size - len(sign) * 8
117 | if diff > 0:
118 | sign = b"\x00" * ((diff + 7) // 8) + sign
119 |
120 | try:
121 | key.verify(sign, data, padding.PKCS1v15(), hashes.SHA1())
122 | except InvalidSignature:
123 | return False
124 | else:
125 | return True
126 |
127 | def write_private_key_file(self, filename, password=None):
128 | self._write_private_key_file(
129 | filename,
130 | self.key,
131 | serialization.PrivateFormat.TraditionalOpenSSL,
132 | password=password
133 | )
134 |
135 | def write_private_key(self, file_obj, password=None):
136 | self._write_private_key(
137 | file_obj,
138 | self.key,
139 | serialization.PrivateFormat.TraditionalOpenSSL,
140 | password=password
141 | )
142 |
143 | @staticmethod
144 | def generate(bits, progress_func=None):
145 | """
146 | Generate a new private RSA key. This factory function can be used to
147 | generate a new host key or authentication key.
148 |
149 | :param int bits: number of bits the generated key should be.
150 | :param progress_func: Unused
151 | :return: new `.RSAKey` private key
152 | """
153 | key = rsa.generate_private_key(
154 | public_exponent=65537, key_size=bits, backend=default_backend()
155 | )
156 | return RSAKey(key=key)
157 |
158 | # ...internals...
159 | def _decode_key(self, _raw):
160 | pkformat, data = _raw
161 | if pkformat == self.FORMAT_ORIGINAL:
162 | try:
163 | key = serialization.load_der_private_key(
164 | data, password=None, backend=default_backend()
165 | )
166 | except (ValueError, TypeError, UnsupportedAlgorithm) as e:
167 | raise SSHException(str(e))
168 |
169 | elif pkformat == self.FORMAT_OPENSSH:
170 | msg = Message(data)
171 | n = msg.get_mpint()
172 | e = msg.get_mpint()
173 | d = msg.get_mpint()
174 | iqmp = msg.get_mpint()
175 | p = msg.get_mpint()
176 | q = msg.get_mpint()
177 |
178 | public_numbers = rsa.RSAPublicNumbers(e=e, n=n)
179 | key = rsa.RSAPrivateNumbers(
180 | p=p,
181 | q=q,
182 | d=d,
183 | dmp1=d % (p - 1),
184 | dmq1=d % (q - 1),
185 | iqmp=iqmp,
186 | public_numbers=public_numbers,
187 | ).private_key(default_backend())
188 | else:
189 | raise SSHException('unknown private key format.')
190 |
191 | if not isinstance(key, rsa.RSAPrivateKey):
192 | raise SSHException("Invalid key type")
193 |
194 | self.key = key
195 |
--------------------------------------------------------------------------------
/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 socket
20 | import struct
21 |
22 | from paramiko import util
23 | from paramiko.common import DEBUG
24 | from paramiko.message import Message
25 | from paramiko.py3compat import byte_chr, byte_ord
26 |
27 |
28 | CMD_INIT, CMD_VERSION, CMD_OPEN, CMD_CLOSE, CMD_READ, CMD_WRITE, CMD_LSTAT, \
29 | CMD_FSTAT, CMD_SETSTAT, CMD_FSETSTAT, CMD_OPENDIR, CMD_READDIR, \
30 | CMD_REMOVE, CMD_MKDIR, CMD_RMDIR, CMD_REALPATH, CMD_STAT, CMD_RENAME, \
31 | 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, \
37 | SFTP_BAD_MESSAGE, SFTP_NO_CONNECTION, SFTP_CONNECTION_LOST, \
38 | SFTP_OP_UNSUPPORTED = range(1, 9)
39 |
40 | SFTP_DESC = ['Success',
41 | 'End of file',
42 | 'No such file',
43 | 'Permission denied',
44 | 'Failure',
45 | 'Bad message',
46 | 'No connection',
47 | 'Connection lost',
48 | 'Operation unsupported']
49 |
50 | SFTP_FLAG_READ = 0x1
51 | SFTP_FLAG_WRITE = 0x2
52 | SFTP_FLAG_APPEND = 0x4
53 | SFTP_FLAG_CREATE = 0x8
54 | SFTP_FLAG_TRUNC = 0x10
55 | SFTP_FLAG_EXCL = 0x20
56 |
57 | _VERSION = 3
58 |
59 |
60 | # for debugging
61 | CMD_NAMES = {
62 | CMD_INIT: 'init',
63 | CMD_VERSION: 'version',
64 | CMD_OPEN: 'open',
65 | CMD_CLOSE: 'close',
66 | CMD_READ: 'read',
67 | CMD_WRITE: 'write',
68 | CMD_LSTAT: 'lstat',
69 | CMD_FSTAT: 'fstat',
70 | CMD_SETSTAT: 'setstat',
71 | CMD_FSETSTAT: 'fsetstat',
72 | CMD_OPENDIR: 'opendir',
73 | CMD_READDIR: 'readdir',
74 | CMD_REMOVE: 'remove',
75 | CMD_MKDIR: 'mkdir',
76 | CMD_RMDIR: 'rmdir',
77 | CMD_REALPATH: 'realpath',
78 | CMD_STAT: 'stat',
79 | CMD_RENAME: 'rename',
80 | CMD_READLINK: 'readlink',
81 | CMD_SYMLINK: 'symlink',
82 | CMD_STATUS: 'status',
83 | CMD_HANDLE: 'handle',
84 | CMD_DATA: 'data',
85 | CMD_NAME: 'name',
86 | CMD_ATTRS: 'attrs',
87 | CMD_EXTENDED: 'extended',
88 | CMD_EXTENDED_REPLY: 'extended_reply'
89 | }
90 |
91 |
92 | class SFTPError (Exception):
93 | pass
94 |
95 |
96 | class BaseSFTP (object):
97 | def __init__(self):
98 | self.logger = util.get_logger('paramiko.sftp')
99 | self.sock = None
100 | self.ultra_debug = False
101 |
102 | # ...internals...
103 |
104 | def _send_version(self):
105 | m = Message()
106 | m.add_int(_VERSION)
107 | self._send_packet(CMD_INIT, m)
108 | t, data = self._read_packet()
109 | if t != CMD_VERSION:
110 | raise SFTPError('Incompatible sftp protocol')
111 | version = struct.unpack('>I', data[:4])[0]
112 | # if version != _VERSION:
113 | # raise SFTPError('Incompatible sftp protocol')
114 | return version
115 |
116 | def _send_server_version(self):
117 | # winscp will freak out if the server sends version info before the
118 | # client finishes sending INIT.
119 | t, data = self._read_packet()
120 | if t != CMD_INIT:
121 | raise SFTPError('Incompatible sftp protocol')
122 | version = struct.unpack('>I', data[:4])[0]
123 | # advertise that we support "check-file"
124 | extension_pairs = ['check-file', 'md5,sha1']
125 | msg = Message()
126 | msg.add_int(_VERSION)
127 | msg.add(*extension_pairs)
128 | self._send_packet(CMD_VERSION, msg)
129 | return version
130 |
131 | def _log(self, level, msg, *args, **kwargs):
132 | self.logger.log(level, msg, *args, **kwargs)
133 |
134 | def _loglist(self, level, msgs):
135 | for m in msgs:
136 | self._log(level, "%s", m)
137 |
138 | def _write_all(self, out):
139 | while len(out) > 0:
140 | n = self.sock.send(out)
141 | if n <= 0:
142 | raise EOFError()
143 | if n == len(out):
144 | return
145 | out = out[n:]
146 | return
147 |
148 | def _read_all(self, n):
149 | out = bytes()
150 | while n > 0:
151 | if isinstance(self.sock, socket.socket):
152 | # sometimes sftp is used directly over a socket instead of a paramiko channel
153 | # recv() may not return/raise an exception when socket closed ??
154 | # so check periodically if socket is closed using select/poll - see a9c51b23cea3
155 | while True:
156 | read = util.poll_read([self.sock], 0.1)
157 | if len(read) > 0:
158 | x = self.sock.recv(n)
159 | break
160 | else:
161 | x = self.sock.recv(n)
162 |
163 | if len(x) == 0:
164 | raise EOFError()
165 | out += x
166 | n -= len(x)
167 | return out
168 |
169 | def _send_packet(self, t, packet):
170 | packet = packet.asbytes()
171 | out = struct.pack('>I', len(packet) + 1) + byte_chr(t) + packet
172 | if self.ultra_debug:
173 | self._loglist(DEBUG, util.format_binary(out, 'OUT: '))
174 | self._write_all(out)
175 |
176 | def _read_packet(self):
177 | x = self._read_all(4)
178 | # most sftp servers won't accept packets larger than about 32k, so
179 | # anything with the high byte set (> 16MB) is just garbage.
180 | if byte_ord(x[0]):
181 | raise SFTPError('Garbage packet received')
182 | size = struct.unpack('>I', x)[0]
183 | data = self._read_all(size)
184 | if self.ultra_debug:
185 | self._loglist(DEBUG, util.format_binary(data, 'IN: '))
186 | if size > 0:
187 | t = byte_ord(data[0])
188 | return t, data[1:]
189 | return 0, bytes()
190 |
--------------------------------------------------------------------------------
/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 |
20 | class SSHException (Exception):
21 | """
22 | Exception raised by failures in SSH2 protocol negotiation or logic errors.
23 | """
24 | pass
25 |
26 |
27 | class AuthenticationException (SSHException):
28 | """
29 | Exception raised when authentication failed for some reason. It may be
30 | possible to retry with different credentials. (Other classes specify more
31 | specific reasons.)
32 |
33 | .. versionadded:: 1.6
34 | """
35 | pass
36 |
37 |
38 | class PasswordRequiredException (AuthenticationException):
39 | """
40 | Exception raised when a password is needed to unlock a private key file.
41 | """
42 | pass
43 |
44 |
45 | class BadAuthenticationType (AuthenticationException):
46 | """
47 | Exception raised when an authentication type (like password) is used, but
48 | the server isn't allowing that type. (It may only allow public-key, for
49 | example.)
50 |
51 | .. versionadded:: 1.1
52 | """
53 | allowed_types = []
54 |
55 | def __init__(self, explanation, types):
56 | AuthenticationException.__init__(self, explanation, types)
57 | self.explanation = explanation
58 | self.allowed_types = types
59 |
60 | def __str__(self):
61 | return "%s; allowed types: %r" % self.args
62 |
63 |
64 | class PartialAuthentication (AuthenticationException):
65 | """
66 | An internal exception thrown in the case of partial authentication.
67 | """
68 | allowed_types = []
69 |
70 | def __init__(self, types):
71 | AuthenticationException.__init__(self, types)
72 | self.allowed_types = types
73 |
74 | def __str__(self):
75 | return "Partial authentication; allowed types: %r" % self.allowed_types
76 |
77 |
78 | class ChannelException (SSHException):
79 | """
80 | Exception raised when an attempt to open a new `.Channel` fails.
81 |
82 | :param int code: the error code returned by the server
83 |
84 | .. versionadded:: 1.6
85 | """
86 | def __init__(self, code, text):
87 | SSHException.__init__(self, code, text)
88 | self.code = code
89 | self.text = text
90 |
91 | def __str__(self):
92 | return "ChannelException(%r, %r)" % self.args
93 |
94 |
95 | class BadHostKeyException (SSHException):
96 | """
97 | The host key given by the SSH server did not match what we were expecting.
98 |
99 | :param str hostname: the hostname of the SSH server
100 | :param PKey got_key: the host key presented by the server
101 | :param PKey expected_key: the host key expected
102 |
103 | .. versionadded:: 1.6
104 | """
105 | def __init__(self, hostname, got_key, expected_key):
106 | SSHException.__init__(self, hostname, got_key, expected_key)
107 | self.hostname = hostname
108 | self.key = got_key
109 | self.expected_key = expected_key
110 |
111 | def __str__(self):
112 | return "Host key for server %s does not match: got %s, expected %s" % (
113 | self.hostname, self.key.get_base64(), self.expected_key.get_base64()
114 | )
115 |
116 |
117 | class ProxyCommandFailure (SSHException):
118 | """
119 | The "ProxyCommand" found in the .ssh/config file returned an error.
120 |
121 | :param str command: The command line that is generating this exception.
122 | :param str error: The error captured from the proxy command output.
123 | """
124 | def __init__(self, command, error):
125 | SSHException.__init__(self, command, error)
126 | self.command = command
127 | self.error = error
128 |
129 | def __str__(self):
130 | return "ProxyCommand (%r) returned non-zero exit status: %r" % self.args
131 |
--------------------------------------------------------------------------------
/paramiko/win_openssh.py:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2021 Lew Gordon
2 | # Copyright (C) 2022 Patrick Spendrin
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 | import os.path
20 | import time
21 |
22 | PIPE_NAME = r"\\.\pipe\openssh-ssh-agent"
23 |
24 |
25 | def can_talk_to_agent():
26 | # use os.listdir() instead of os.path.exists(), because os.path.exists()
27 | # uses CreateFileW() API and the pipe cannot be reopen unless the server
28 | # calls DisconnectNamedPipe().
29 | dir_, name = os.path.split(PIPE_NAME)
30 | name = name.lower()
31 | return any(name == n.lower() for n in os.listdir(dir_))
32 |
33 |
34 | class OpenSSHAgentConnection:
35 | def __init__(self):
36 | while True:
37 | try:
38 | self._pipe = os.open(PIPE_NAME, os.O_RDWR | os.O_BINARY)
39 | except OSError as e:
40 | # retry when errno 22 which means that the server has not
41 | # called DisconnectNamedPipe() yet.
42 | if e.errno != 22:
43 | raise
44 | else:
45 | break
46 | time.sleep(0.1)
47 |
48 | def send(self, data):
49 | return os.write(self._pipe, data)
50 |
51 | def recv(self, n):
52 | return os.read(self._pipe, n)
53 |
54 | def close(self):
55 | return os.close(self._pipe)
56 |
--------------------------------------------------------------------------------
/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.common import zero_byte
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 | if platform.architecture()[0] == '64bit':
61 | ULONG_PTR = ctypes.c_uint64
62 | else:
63 | ULONG_PTR = ctypes.c_uint32
64 |
65 |
66 | class COPYDATASTRUCT(ctypes.Structure):
67 | """
68 | ctypes implementation of
69 | http://msdn.microsoft.com/en-us/library/windows/desktop/ms649010%28v=vs.85%29.aspx
70 | """
71 | _fields_ = [
72 | ('num_data', ULONG_PTR),
73 | ('data_size', ctypes.wintypes.DWORD),
74 | ('data_loc', ctypes.c_void_p),
75 | ]
76 |
77 |
78 | def _query_pageant(msg):
79 | """
80 | Communication with the Pageant process is done through a shared
81 | memory-mapped file.
82 | """
83 | hwnd = _get_pageant_window_object()
84 | if not hwnd:
85 | # Raise a failure to connect exception, pageant isn't running anymore!
86 | return None
87 |
88 | # create a name for the mmap
89 | map_name = 'PageantRequest%08x' % thread.get_ident()
90 |
91 | pymap = _winapi.MemoryMap(map_name, _AGENT_MAX_MSGLEN,
92 | _winapi.get_security_attributes_for_user())
93 | with pymap:
94 | pymap.write(msg)
95 | # Create an array buffer containing the mapped filename
96 | char_buffer = array.array("b", b(map_name) + zero_byte)
97 | char_buffer_address, char_buffer_size = char_buffer.buffer_info()
98 | # Create a string to use for the SendMessage function call
99 | cds = COPYDATASTRUCT(_AGENT_COPYDATA_ID, char_buffer_size,
100 | char_buffer_address)
101 |
102 | response = ctypes.windll.user32.SendMessageA(hwnd,
103 | win32con_WM_COPYDATA, ctypes.sizeof(cds), ctypes.byref(cds))
104 |
105 | if response > 0:
106 | pymap.seek(0)
107 | datalen = pymap.read(4)
108 | retlen = struct.unpack('>I', datalen)[0]
109 | return datalen + pymap.read(retlen)
110 | return None
111 |
112 |
113 | class PageantConnection(object):
114 | """
115 | Mock "connection" to an agent which roughly approximates the behavior of
116 | a unix local-domain socket (as used by Agent). Requests are sent to the
117 | pageant daemon via special Windows magick, and responses are buffered back
118 | for subsequent reads.
119 | """
120 |
121 | def __init__(self):
122 | self._response = None
123 |
124 | def send(self, data):
125 | self._response = _query_pageant(data)
126 |
127 | def recv(self, n):
128 | if self._response is None:
129 | return ''
130 | ret = self._response[:n]
131 | self._response = self._response[n:]
132 | if self._response == '':
133 | self._response = None
134 | return ret
135 |
136 | def close(self):
137 | pass
138 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bdist_wheel]
2 | universal = 1
3 |
4 | [metadata]
5 | license_file = LICENSE
6 |
7 | [flake8]
8 | exclude = .git,build,dist,sites
9 | max-line-length = 99
10 | ignore =
11 | # continuation line under-indented for visual indent
12 | E128,
13 | # multiple spaces after ':'
14 | E241,
15 | # at least two spaces before inline comment
16 | E261,
17 | # line break after binary operator
18 | W504,
19 | # do not use bare 'except'
20 | E722,
21 | # ambiguous variable name
22 | E741,
23 |
24 |
--------------------------------------------------------------------------------
/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 | import os
20 | from setuptools import setup
21 |
22 | longdesc = '''
23 | *paramiko-ng* is a fork of `paramiko `_
24 |
25 | For changes in releases of this fork, see https://github.com/ploxiln/paramiko-ng/releases
26 |
27 | This is a library for making SSH2 connections (client or server).
28 | Emphasis is on using SSH2 as an alternative to SSL for making secure
29 | connections between python scripts. All major ciphers and hash methods
30 | are supported. SFTP client and server mode are both supported too.
31 |
32 | Required packages:
33 | Cryptography
34 |
35 | The import name is still just ``paramiko``. Make sure the original *paramiko*
36 | is not installed before installing *paramiko-ng* - otherwise pip may report
37 | success even though *paramiko-ng* was not correctly installed.
38 | (Because the import name is the same, installed files can conflict.)
39 |
40 | You can also install under the original "paramiko" pip-package-name,
41 | in order to satisfy requirements for other packages::
42 |
43 | PARAMIKO_REPLACE=1 pip install "https://github.com/ploxiln/paramiko-ng/archive/2.8.10.tar.gz#egg=paramiko"
44 |
45 | Replace "2.8.10" with the desired version.
46 |
47 | To install the latest development version::
48 |
49 | pip install "git+https://github.com/ploxiln/paramiko-ng/#egg=paramiko-ng"
50 |
51 | ''' # noqa: E501
52 |
53 | name = "paramiko" if os.environ.get('PARAMIKO_REPLACE') else "paramiko-ng"
54 |
55 | # Version info -- read without importing
56 | _locals = {}
57 | with open('paramiko/_version.py') as fp:
58 | exec(fp.read(), None, _locals)
59 | version = _locals['__version__']
60 |
61 | setup(
62 | name=name,
63 | version=version,
64 | packages=['paramiko'],
65 | description="SSH2 protocol library",
66 | long_description=longdesc,
67 | author="Jeff Forcier",
68 | author_email="jeff@bitprophet.org",
69 | maintainer='Pierce Lopez',
70 | maintainer_email='pierce.lopez@gmail.com',
71 | url="https://github.com/ploxiln/paramiko-ng/",
72 | license='LGPL',
73 | platforms='Posix; MacOS X; Windows',
74 | classifiers=[
75 | 'Development Status :: 5 - Production/Stable',
76 | 'Intended Audience :: Developers',
77 | 'License :: OSI Approved :: '
78 | '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 :: 3',
85 | ],
86 | python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
87 | install_requires=[
88 | 'bcrypt>=3',
89 | 'cryptography>=2.6',
90 | ],
91 | extras_require={
92 | 'Ed25519': [], # can be removed in 3.0
93 | 'gssapi': [
94 | "pyasn1",
95 | 'gssapi;platform_system!="Windows"',
96 | 'pywin32;platform_system=="Windows"',
97 | ],
98 | },
99 | )
100 |
--------------------------------------------------------------------------------
/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 | :member-order: bysource
10 |
11 | DSA (DSS)
12 | =========
13 |
14 | .. automodule:: paramiko.dsskey
15 |
16 | RSA
17 | ===
18 |
19 | .. automodule:: paramiko.rsakey
20 |
21 | ECDSA
22 | =====
23 |
24 | .. automodule:: paramiko.ecdsakey
25 |
26 | Ed25519
27 | =======
28 |
29 | .. automodule:: paramiko.ed25519key
30 |
--------------------------------------------------------------------------------
/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 | import alabaster
2 |
3 | import sys
4 | from os.path import abspath, join, dirname
5 | sys.path.append(abspath(join(dirname(__file__), '..', '..')))
6 |
7 | extensions = [
8 | 'sphinx.ext.intersphinx',
9 | 'sphinx.ext.autodoc',
10 | 'alabaster',
11 | 'releases',
12 | ]
13 |
14 | # Alabaster theme + mini-extension
15 | html_theme_path = [alabaster.get_path()]
16 | html_theme = 'alabaster'
17 | html_theme_options = {
18 | 'description': "A Python implementation of SSHv2.",
19 | 'github_user': 'ploxiln',
20 | 'github_repo': 'paramiko-ng',
21 | 'github_type': 'star',
22 | }
23 | html_sidebars = {
24 | '**': [
25 | 'about.html',
26 | 'navigation.html',
27 | 'searchbox.html',
28 | ]
29 | }
30 |
31 | # Regular settings
32 | project = 'Paramiko-NG'
33 | copyright = 'Paramiko contributors'
34 | master_doc = 'index'
35 | templates_path = ['_templates']
36 | exclude_trees = ['_build']
37 | source_suffix = '.rst'
38 | default_role = 'obj'
39 |
40 | # Autodoc settings
41 | autodoc_default_options = {
42 | 'members': True,
43 | 'special-members': "__init__, __eq__",
44 | }
45 |
46 | # Intersphinx connection to stdlib
47 | intersphinx_mapping = {
48 | 'python': ('https://docs.python.org/3.7', None),
49 | }
50 |
51 | # just used for old changelog
52 | releases_github_path = "paramiko/paramiko"
53 | releases_document_name = ["old_changelog"]
54 |
--------------------------------------------------------------------------------
/sites/docs/faq.rst:
--------------------------------------------------------------------------------
1 | ===================================
2 | Frequently Asked/Answered Questions
3 | ===================================
4 |
5 | Paramiko doesn't work with my Cisco, Windows or other non-Unix system!
6 | ======================================================================
7 |
8 | In an ideal world, the developers would love to support every possible target
9 | system. Unfortunately, volunteer development time and access to non-mainstream
10 | platforms are limited, meaning that we can only fully support standard OpenSSH
11 | implementations such as those found on Linux, FreeBSD, OpenBSD, and macOS.
12 |
13 | Because of this, we typically close bug reports for nonstandard SSH implementations
14 | or host systems. However, closed does not imply locked - affected users can still
15 | post comments on such tickets, and we will consider actual patch submissions for
16 | these issues, provided they can get +1s from similarly affected users and are proven
17 | to not break existing functionality.
18 |
19 | For interacting with managed networking equipment from Cisco/Juniper/Arista/HP/etc
20 | over SSH, try using `netmiko `_.
21 |
22 | I'm having strange issues with my code hanging at shutdown!
23 | ===========================================================
24 |
25 | Make sure you explicitly ``.close()`` your connection objects (usually
26 | ``SSHClient``) if you're having any sort of hang/freeze at shutdown time!
27 |
28 | Doing so isn't strictly necessary 100% of the time, but it is almost always the
29 | right solution if you run into the various corner cases that cause race
30 | conditions, etc.
31 |
--------------------------------------------------------------------------------
/sites/docs/index.rst:
--------------------------------------------------------------------------------
1 | ====================================
2 | Welcome to Paramiko's documentation!
3 | ====================================
4 |
5 | **Paramiko-NG** is a Python (2.7, 3.4+) implementation of the SSHv2 protocol [#]_,
6 | providing both client and server functionality. While it leverages a Python C
7 | extension for low level cryptography (`Cryptography `_),
8 | Paramiko-NG itself is a pure Python interface around SSH networking concepts.
9 |
10 | This site covers **Paramiko-NG**'s usage & API documentation. Paramiko-NG is still
11 | usually referred to as Paramiko in the documentation, and it is still imported
12 | as ``paramiko``.
13 |
14 | For information about installing Paramiko-NG: :doc:`installing`
15 |
16 | .. rubric:: Footnotes
17 |
18 | .. [#]
19 | SSH is defined in :rfc:`4251`, :rfc:`4252`, :rfc:`4253` and :rfc:`4254`. The
20 | primary working implementation of the protocol is the `OpenSSH project
21 | `_. Paramiko implements a large portion of the SSH
22 | feature set, but there are occasional gaps.
23 |
24 |
25 | API documentation
26 | =================
27 |
28 | The high-level client API starts with creation of an `.SSHClient` object. For
29 | more direct control, pass a socket (or socket-like object) to a `.Transport`,
30 | and use `start_server <.Transport.start_server>` or `start_client
31 | <.Transport.start_client>` to negotiate with the remote host as either a server
32 | or client.
33 |
34 | As a client, you are responsible for authenticating using a password or private
35 | key, and checking the server's host key. (Key signature and verification is
36 | done by paramiko, but you will need to provide private keys and check that the
37 | content of a public key matches what you expected to see.)
38 |
39 | As a server, you are responsible for deciding which users, passwords, and keys
40 | to allow, and what kind of channels to allow.
41 |
42 | Once you have finished, either side may request flow-controlled `channels
43 | <.Channel>` to the other side, which are Python objects that act like sockets,
44 | but send and receive data over the encrypted session.
45 |
46 | For details, please see the following tables of contents (which are organized
47 | by area of interest.)
48 |
49 |
50 | Core SSH protocol classes
51 | -------------------------
52 |
53 | .. toctree::
54 | api/channel
55 | api/client
56 | api/message
57 | api/packet
58 | api/transport
59 |
60 |
61 | Authentication & keys
62 | ---------------------
63 |
64 | .. toctree::
65 | api/agent
66 | api/hostkeys
67 | api/keys
68 | api/ssh_gss
69 | api/kex_gss
70 |
71 |
72 | Other primary functions
73 | -----------------------
74 |
75 | .. toctree::
76 | api/config
77 | api/proxy
78 | api/server
79 | api/sftp
80 |
81 |
82 | Miscellany
83 | ----------
84 |
85 | .. toctree::
86 | api/buffered_pipe
87 | api/file
88 | api/pipe
89 | api/ssh_exception
90 |
91 |
92 | Appendix
93 | ========
94 |
95 | .. toctree::
96 | :maxdepth: 1
97 |
98 | installing
99 | faq
100 | old_changelog
101 |
--------------------------------------------------------------------------------
/sites/docs/installing.rst:
--------------------------------------------------------------------------------
1 | ==========
2 | Installing
3 | ==========
4 |
5 | Paramiko-NG itself
6 | ==================
7 |
8 | The recommended way to get Paramiko-NG is to install the latest stable release
9 | via `pip `_::
10 |
11 | $ pip install paramiko-ng
12 |
13 | You can also install under the original "paramiko" pip-package-name,
14 | in order to satisfy requirements for other packages (replace "2.8.10" with desired version)::
15 |
16 | $ PARAMIKO_REPLACE=1 pip install "https://github.com/ploxiln/paramiko-ng/archive/2.8.10.tar.gz#egg=paramiko"
17 |
18 | Paramiko-NG currently supports Python 2.7, 3.4+, and PyPy.
19 |
20 | Paramiko-NG has only a few direct dependencies:
21 |
22 | - The big one is Cryptography; see :ref:`its specific note below ` for more details.
23 | - `bcrypt `_, for "new openssh format" private keys
24 |
25 | If you need GSS-API / SSPI support, see :ref:`the below subsection on it
26 | ` for details on its optional dependencies.
27 |
28 |
29 | .. _cryptography:
30 |
31 | Cryptography
32 | ============
33 |
34 | `Cryptography `__ provides the low-level (C-based)
35 | encryption algorithms we need to implement the SSH protocol. It has detailed
36 | `installation instructions`_ (and an `FAQ `_)
37 | which you should read carefully.
38 |
39 | Cryptography provides statically built "wheels" for most common systems,
40 | which modern "pip" will preferentially install. These include all needed
41 | non-python components pre-built and should "just work".
42 |
43 | If you need or want to build cryptography from source, you will need a
44 | C build toolchain, development headers for Python, OpenSSL and
45 | ``libffi``, and starting with cryptography-3.4, also a Rust language
46 | toolchain installed. Again, see `Cryptography's install docs`_;
47 | these requirements may occasionally change.
48 |
49 | - Cryptography-3.4 dropped support for Python-2.7
50 | - Cryptography-3.3 dropped support for Python-3.5
51 | - Cryptography-3.2 dropped support for OpenSSL-1.0.2
52 |
53 | If you have a problem with these changing requirements, you can install
54 | the last patch release before the incompatible minor release like::
55 |
56 | $ pip install 'cryptography<3.4'
57 |
58 | .. _installation instructions:
59 | .. _Cryptography's install docs: https://cryptography.io/en/latest/installation.html
60 |
61 |
62 | .. _gssapi:
63 |
64 | Optional dependencies for GSS-API / SSPI / Kerberos
65 | ===================================================
66 |
67 | In order to use GSS-API/Kerberos & related functionality, a couple of
68 | additional dependencies are required:
69 |
70 | * All platforms need **a working installation of GSS-API itself**, e.g. Heimdal
71 | * All platforms need `pyasn1 `__
72 | * **Unix** needs `gssapi `__
73 | * **Windows** needs `pywin32 `__
74 |
75 | .. note::
76 | If you use Microsoft SSPI for kerberos authentication and credential
77 | delegation, make sure that the target host is trusted for delegation in the
78 | active directory configuration. For details see:
79 | http://technet.microsoft.com/en-us/library/cc738491%28v=ws.10%29.aspx
80 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 | # This file's just here so test modules can use explicit-relative imports.
2 |
--------------------------------------------------------------------------------
/tests/cert_support/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/cert_support/test_dss.key-cert.pub:
--------------------------------------------------------------------------------
1 | ssh-dss-cert-v01@openssh.com AAAAHHNzaC1kc3MtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgJA3GjLmg6JbIWxokW/c827lmPOSvSfPDIY586yICFqIAAACBAOeBpgNnfRzr/twmAQRu2XwWAp3CFtrVnug6s6fgwj/oLjYbVtjAy6pl/h0EKCWx2rf1IetyNsTxWrniA9I6HeDj65X1FyDkg6g8tvCnaNB8Xp/UUhuzHuGsMIipRxBxw9LF608EqZcj1E3ytktoW5B5OcjrkEoz3xG7C+rpIjYvAAAAFQDwz4UnmsGiSNu5iqjn3uTzwUpshwAAAIEAkxfFeY8P2wZpDjX0MimZl5wkoFQDL25cPzGBuB4OnB8NoUk/yjAHIIpEShw8V+LzouMK5CTJQo5+Ngw3qIch/WgRmMHy4kBq1SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg4Ok10+XFDxlqZo8Y+wAAACARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lYukmnjO1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+NwacIBlXa8cMDL7Q/69o0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgEAAAAAAAAAAAAAAAEAAAAJdXNlcl90ZXN0AAAACAAAAAR0ZXN0AAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQDskr46Umjxh3wo7PoPQsSVS3xt6+5PhwmXrnVtBBnkOo+zHRwQo8G8sY+Lc6oOOzA5GCSawKOwqE305GIDfB8/L9EKOkAjdN18imDjw/YuJFA4bl9yFhsXrCb1GZPJw0pJ0H0Eid9EldyMQAhGE49MWvnFMQl1TgO6YWq/g71xAFimge0LvVWijlbMy7O+nsGxSpinIprV5S9Viv8XC/ku89tadZfca1uxq751aGfAWGeYrVytpUl8UO0ggqH6BaUvkDU7rWh2n5RHUTvgzceKWnz5wqd8BngK37WmJjAgCtHCJS5ZRf6oJGj2QVcqc6cjvEFWsCuOKB4KAjktauWxAAABDwAAAAdzc2gtcnNhAAABAK6jweL231fRhFoybEGTOXJfj0lx55KpDsw9Q1rBvZhrSgwUr2dFr9HVcKe44mTC7CMtdW5VcyB67l1fnMil/D/e4zYxI0PvbW6RxLFNqvvtxBu5sGt5B7uzV4aAV31TpWR0l5RwwpZqc0NUlTx7oMutN1BDrPqW70QZ/iTEwalkn5fo1JWej0cf4BdC9VgYDLnprx0KN3IToukbszRQySnuR6MQUfj0m7lUloJfF3rq8G0kNxWqDGoJilMhO5Lqu9wAhlZWdouypI6bViO6+ToCVixLNUYs3EfS1zCxvXpiyMvh6rZofJ6WqzUuSd4Mzb2Ka4ocTKi7kynF+OG0Ivo= tests/test_dss.key.pub
2 |
--------------------------------------------------------------------------------
/tests/cert_support/test_ecdsa_256.key:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | MHcCAQEEIKB6ty3yVyKEnfF/zprx0qwC76MsMlHY4HXCnqho2eKioAoGCCqGSM49
3 | AwEHoUQDQgAElI9mbdlaS+T9nHxY/59lFnn80EEecZDBHq4gLpccY8Mge5ZTMiMD
4 | ADRvOqQ5R98Sxst765CAqXmRtz8vwoD96g==
5 | -----END EC PRIVATE KEY-----
6 |
--------------------------------------------------------------------------------
/tests/cert_support/test_ecdsa_256.key-cert.pub:
--------------------------------------------------------------------------------
1 | ecdsa-sha2-nistp256-cert-v01@openssh.com AAAAKGVjZHNhLXNoYTItbmlzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgJ+ZkRXedIWPl9y6fvel60p47ys5WgwMSjiwzJ2Ho+4MAAAAIbmlzdHAyNTYAAABBBJSPZm3ZWkvk/Zx8WP+fZRZ5/NBBHnGQwR6uIC6XHGPDIHuWUzIjAwA0bzqkOUffEsbLe+uQgKl5kbc/L8KA/eoAAAAAAAAAAAAAAAEAAAAJdXNlcl90ZXN0AAAACAAAAAR0ZXN0AAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQDskr46Umjxh3wo7PoPQsSVS3xt6+5PhwmXrnVtBBnkOo+zHRwQo8G8sY+Lc6oOOzA5GCSawKOwqE305GIDfB8/L9EKOkAjdN18imDjw/YuJFA4bl9yFhsXrCb1GZPJw0pJ0H0Eid9EldyMQAhGE49MWvnFMQl1TgO6YWq/g71xAFimge0LvVWijlbMy7O+nsGxSpinIprV5S9Viv8XC/ku89tadZfca1uxq751aGfAWGeYrVytpUl8UO0ggqH6BaUvkDU7rWh2n5RHUTvgzceKWnz5wqd8BngK37WmJjAgCtHCJS5ZRf6oJGj2QVcqc6cjvEFWsCuOKB4KAjktauWxAAABDwAAAAdzc2gtcnNhAAABALdnEil8XIFkcgLZgYwS2cIQPHetUzMNxYCqzk7mSfVpCaIYNTr27RG+f+sD0cerdAIUUvhCT7iA82/Y7wzwkO2RUBi61ATfw9DDPPRQTDfix1SSRwbmPB/nVI1HlPMCEs6y48PFaBZqXwJPS3qycgSxoTBhaLCLzT+r6HRaibY7kiRLDeL3/WHyasK2PRdcYJ6KrLd0ctQcUHZCLK3fJfMfuQRg8MZLVrmK3fHStCXHpRFueRxUhZjaiS9evA/NtzEQhf46JDClQ2rLYpSqSg7QUR/rKwqWWyMuQkOHmlJw797VVa+ZzpUFXP7ekWel3FaBj8IHiimIA7Jm6dOCLm4= tests/test_ecdsa_256.key.pub
2 |
--------------------------------------------------------------------------------
/tests/cert_support/test_ed25519.key:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
3 | QyNTUxOQAAACB69SvZKJh/9VgSL0G27b5xVYa8nethH3IERbi0YqJDXwAAAKhjwAdrY8AH
4 | awAAAAtzc2gtZWQyNTUxOQAAACB69SvZKJh/9VgSL0G27b5xVYa8nethH3IERbi0YqJDXw
5 | AAAEA9tGQi2IrprbOSbDCF+RmAHd6meNSXBUQ2ekKXm4/8xnr1K9komH/1WBIvQbbtvnFV
6 | hryd62EfcgRFuLRiokNfAAAAI2FsZXhfZ2F5bm9yQEFsZXhzLU1hY0Jvb2stQWlyLmxvY2
7 | FsAQI=
8 | -----END OPENSSH PRIVATE KEY-----
9 |
--------------------------------------------------------------------------------
/tests/cert_support/test_ed25519.key-cert.pub:
--------------------------------------------------------------------------------
1 | ssh-ed25519-cert-v01@openssh.com AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAIIjBkc8l1X887CLBHraU+d6/74Hxr9oa+3HC0iioecZ6AAAAIHr1K9komH/1WBIvQbbtvnFVhryd62EfcgRFuLRiokNfAAAAAAAAAAAAAAABAAAACXVzZXJfdGVzdAAAAAgAAAAEdGVzdAAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEA7JK+OlJo8Yd8KOz6D0LElUt8bevuT4cJl651bQQZ5DqPsx0cEKPBvLGPi3OqDjswORgkmsCjsKhN9ORiA3wfPy/RCjpAI3TdfIpg48P2LiRQOG5fchYbF6wm9RmTycNKSdB9BInfRJXcjEAIRhOPTFr5xTEJdU4DumFqv4O9cQBYpoHtC71Voo5WzMuzvp7BsUqYpyKa1eUvVYr/Fwv5LvPbWnWX3Gtbsau+dWhnwFhnmK1craVJfFDtIIKh+gWlL5A1O61odp+UR1E74M3Hilp8+cKnfAZ4Ct+1piYwIArRwiUuWUX+qCRo9kFXKnOnI7xBVrArjigeCgI5LWrlsQAAAQ8AAAAHc3NoLXJzYQAAAQCNfYITv/GCW42fLI89x0pKpXIET/xHIBVan5S3fy5SZq9gLG1Db9g/FITDfOVA7OX8mU/91rucHGtuEi3isILdNFrCcoLEml289tyyluUbbFD5fjvBchMWBkYPwrOPfEzSs299Yk8ZgfV1pjWlndfV54s4c9pinkGu8c0Vzc6stEbWkdmoOHE8su3ogUPg/hOygDzJ+ZOgP5HIUJ6YgkgVpWgZm7zofwdZfa2HEb+WhZaKfMK1UCw1UiSBVk9dx6qzF9m243tHwSHraXvb9oJ1wT1S/MypTbP4RT4fHN8koYNrv2szEBN+lkRgk1D7xaxS/Md2TJsau9ho/UCXSR8L tests/test_ed25519.key.pub
2 |
--------------------------------------------------------------------------------
/tests/cert_support/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/cert_support/test_rsa.key-cert.pub:
--------------------------------------------------------------------------------
1 | ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgsZlXTd5NE4uzGAn6TyAqQj+IPbsTEFGap2x5pTRwQR8AAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4cAAAAAAAAE0gAAAAEAAAAmU2FtcGxlIHNlbGYtc2lnbmVkIE9wZW5TU0ggY2VydGlmaWNhdGUAAAASAAAABXVzZXIxAAAABXVzZXIyAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAACVAAAAB3NzaC1yc2EAAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4cAAACPAAAAB3NzaC1yc2EAAACATFHFsARDgQevc6YLxNnDNjsFtZ08KPMyYVx0w5xm95IVZHVWSOc5w+ccjqN9HRwxV3kP7IvL91qx0Uc3MJdB9g/O6HkAP+rpxTVoTb2EAMekwp5+i8nQJW4CN2BSsbQY1M6r7OBZ5nmF4hOW/5Pu4l22lXe2ydy8kEXOEuRpUeQ= test_rsa.key.pub
2 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 | import shutil
4 | import threading
5 |
6 | import pytest
7 | from paramiko import SFTPServer, SFTP, Transport, load_private_key_file
8 |
9 | from .loop import LoopSocket
10 | from .stub_sftp import StubServer, StubSFTPServer
11 | from .util import _support
12 |
13 |
14 | # TODO: not a huge fan of conftest.py files, see if we can move these somewhere
15 | # 'nicer'.
16 |
17 |
18 | # Perform logging by default; pytest will capture and thus hide it normally,
19 | # presenting it on error/failure. (But also allow turning it off when doing
20 | # very pinpoint debugging - e.g. using breakpoints, so you don't want output
21 | # hiding enabled, but also don't want all the logging to gum up the terminal.)
22 | if not os.environ.get('DISABLE_LOGGING', False):
23 | logging.basicConfig(
24 | level=logging.DEBUG,
25 | # Also make sure to set up timestamping for more sanity when debugging.
26 | format="[%(relativeCreated)s]\t%(levelname)s:%(name)s:%(message)s",
27 | datefmt="%H:%M:%S",
28 | )
29 |
30 |
31 | def make_sftp_folder():
32 | """
33 | Ensure expected target temp folder exists on the remote end.
34 |
35 | Will clean it out if it already exists.
36 | """
37 | # TODO: go back to using the sftp functionality itself for folder setup so
38 | # we can test against live SFTP servers again someday. (Not clear if anyone
39 | # is/was using the old capability for such, though...)
40 | # TODO: something that would play nicer with concurrent testing (but
41 | # probably e.g. using thread ID or UUIDs or something; not the "count up
42 | # until you find one not used!" crap from before...)
43 | # TODO: if we want to lock ourselves even harder into localhost-only
44 | # testing (probably not?) could use tempdir modules for this for improved
45 | # safety. Then again...why would someone have such a folder???
46 | path = os.environ.get('TEST_FOLDER', 'paramiko-test-target')
47 | # Forcibly nuke this directory locally, since at the moment, the below
48 | # fixtures only ever run with a locally scoped stub test server.
49 | shutil.rmtree(path, ignore_errors=True)
50 | # Then create it anew, again locally, for the same reason.
51 | os.mkdir(path)
52 | return path
53 |
54 |
55 | @pytest.fixture # (scope='session')
56 | def sftp_server():
57 | """
58 | Set up an in-memory SFTP server thread. Yields the client Transport/socket.
59 |
60 | The resulting client Transport (along with all the server components) will
61 | be the same object throughout the test session; the `sftp` fixture then
62 | creates new higher level client objects wrapped around the client
63 | Transport, as necessary.
64 | """
65 | # Sockets & transports
66 | socks = LoopSocket()
67 | sockc = LoopSocket()
68 | sockc.link(socks)
69 | tc = Transport(sockc)
70 | ts = Transport(socks)
71 | # Auth
72 | host_key = load_private_key_file(_support('test_rsa.key'))
73 | ts.add_server_key(host_key)
74 | # Server setup
75 | event = threading.Event()
76 | server = StubServer()
77 | ts.set_subsystem_handler('sftp', SFTPServer, StubSFTPServer)
78 | ts.start_server(event, server)
79 | # Wait (so client has time to connect? Not sure. Old.)
80 | event.wait(1.0)
81 | # Make & yield connection.
82 | tc.connect(username='slowdive', password='pygmalion')
83 | yield tc
84 | # TODO: any need for shutdown? Why didn't old suite do so? Or was that the
85 | # point of the "join all threads from threading module" crap in test.py?
86 |
87 |
88 | @pytest.fixture
89 | def sftp(sftp_server):
90 | """
91 | Yield an SFTP client connected to the global in-session SFTP server thread.
92 | """
93 | # Client setup
94 | client = SFTP.from_transport(sftp_server)
95 | # Work in 'remote' folder setup (as it wants to use the client)
96 | # TODO: how cleanest to make this available to tests? Doing it this way is
97 | # marginally less bad than the previous 'global'-using setup, but not by
98 | # much?
99 | client.FOLDER = make_sftp_folder()
100 | # Yield client to caller
101 | yield client
102 | # Clean up - as in make_sftp_folder, we assume local-only exec for now.
103 | shutil.rmtree(client.FOLDER, ignore_errors=True)
104 |
--------------------------------------------------------------------------------
/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 | import socket
20 | import threading
21 |
22 | from paramiko.common import asbytes
23 |
24 |
25 | class LoopSocket (object):
26 | """
27 | A LoopSocket looks like a normal socket, but all data written to it is
28 | delivered on the read-end of another LoopSocket, and vice versa. It's
29 | like a software "socketpair".
30 | """
31 |
32 | def __init__(self):
33 | self.__in_buffer = bytes()
34 | self.__lock = threading.Lock()
35 | self.__cv = threading.Condition(self.__lock)
36 | self.__timeout = None
37 | self.__mate = None
38 | self._closed = False
39 |
40 | def close(self):
41 | self.__unlink()
42 | self._closed = True
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.notify_all()
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 |
--------------------------------------------------------------------------------
/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 |
27 | from paramiko.buffered_pipe import BufferedPipe, PipeTimeout
28 | from paramiko import pipe
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_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_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_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_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_channelfile.py:
--------------------------------------------------------------------------------
1 | try:
2 | from unittest.mock import patch, create_autospec
3 | except ImportError:
4 | from mock import patch, create_autospec
5 |
6 | from paramiko import Channel, ChannelFile, ChannelStderrFile, ChannelStdinFile
7 |
8 |
9 | class ChannelFileBase(object):
10 | @patch("paramiko.channel.ChannelFile._set_mode")
11 | def test_defaults_to_unbuffered_reading(self, setmode):
12 | self.klass(Channel(None))
13 | setmode.assert_called_once_with("r", -1)
14 |
15 | @patch("paramiko.channel.ChannelFile._set_mode")
16 | def test_can_override_mode_and_bufsize(self, setmode):
17 | self.klass(Channel(None), mode="w", bufsize=25)
18 | setmode.assert_called_once_with("w", 25)
19 |
20 | def test_read_recvs_from_channel(self):
21 | chan = create_autospec(Channel, instance=True)
22 | cf = self.klass(chan)
23 | cf.read(100)
24 | chan.recv.assert_called_once_with(100)
25 |
26 | def test_write_calls_channel_sendall(self):
27 | chan = create_autospec(Channel, instance=True)
28 | cf = self.klass(chan, mode="w")
29 | cf.write("ohai")
30 | chan.sendall.assert_called_once_with(b"ohai")
31 |
32 |
33 | class TestChannelFile(ChannelFileBase):
34 | klass = ChannelFile
35 |
36 |
37 | class TestChannelStderrFile(object):
38 | def test_read_calls_channel_recv_stderr(self):
39 | chan = create_autospec(Channel, instance=True)
40 | cf = ChannelStderrFile(chan)
41 | cf.read(100)
42 | chan.recv_stderr.assert_called_once_with(100)
43 |
44 | def test_write_calls_channel_sendall(self):
45 | chan = create_autospec(Channel, instance=True)
46 | cf = ChannelStderrFile(chan, mode="w")
47 | cf.write("ohai")
48 | chan.sendall_stderr.assert_called_once_with(b"ohai")
49 |
50 |
51 | class TestChannelStdinFile(ChannelFileBase):
52 | klass = ChannelStdinFile
53 |
54 | def test_close_calls_channel_shutdown_write(self):
55 | chan = create_autospec(Channel, instance=True)
56 | cf = ChannelStdinFile(chan, mode="wb")
57 | cf.flush = create_autospec(lambda: None)
58 | cf.close()
59 | # Sanity check that we still call BufferedFile.close()
60 | cf.flush.assert_called_once_with()
61 | assert cf._closed is True
62 | # Actual point of test
63 | chan.shutdown_write.assert_called_once_with()
64 |
--------------------------------------------------------------------------------
/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_1k_o.key:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABAsyq4pxL
3 | R5sOprPDHGpvzxAAAAEAAAAAEAAAGxAAAAB3NzaC1kc3MAAACBAL8XEx7F9xuwBNles+vW
4 | pNF+YcofrBhjX1r5QhpBe0eoYWLHRcroN6lxwCdGYRfgOoRjTncBiixQX/uUxAY96zDh3i
5 | r492s2BcJt4ihvNn/AY0I0OTuX/2IwGk9CGzafjaeZNVYxMa8lcVt0hSOTjkPQ7gVuk6bJ
6 | zMInvie+VWKLAAAAFQDUgYdY+rhR0SkKbC09BS/SIHcB+wAAAIB44+4zpCNcd0CGvZlowH
7 | 99zyPX8uxQtmTLQFuR2O8O0FgVVuCdDgD0D9W8CLOp32oatpM0jyyN89EdvSWzjHzZJ+L6
8 | H1FtZps7uhpDFWHdva1R25vyGecLMUuXjo5t/D7oCDih+HwHoSAxoi0QvsPd8/qqHQVznN
9 | JKtR6thUpXEwAAAIAG4DCBjbgTTgpBw0egRkJwBSz0oTt+1IcapNU2jA6N8urMSk9YXHEQ
10 | HKN68BAF3YJ59q2Ujv3LOXmBqGd1T+kzwUszfMlgzq8MMu19Yfzse6AIK1Agn1Vj6F7YXL
11 | sXDN+T4KszX5+FJa7t/Zsp3nALWy6l0f4WKivEF5Y2QpEFcQAAAgCH6XUl1hYWB6kgCSHV
12 | a4C+vQHrgFNgNwEQnE074LXHXlAhxC+Dm8XTGqVPX1KRPWzadq9/+v6pqLFqiRueB86uRb
13 | J5WtAbUs3WwxAaC5Mi+mn42MBfL9PIwWPWCvstrAq9Nyj3EBMeX3XFLxN3RuGXIQnY/5rF
14 | f5hriUVxhWDQGIVbBKhkpn7Geqg6nLpn7iqQhzFmFGjPmAdrllgdVGJRLyIN6BRsaltDdy
15 | vxufkvGzKudvQ85QvsaoFJQ6K1d0S7907pexvxmWpcO7zchXb6i09BITWOAKIcHpVkbNQw
16 | +8pzSdpggsAwCRbfk/Jkezz8sXVUCfmmJ23NFUw04/0ZbilCADRsUaPfafgVPeDznBnuCm
17 | tfXa4JSrVUvPdwoex3SKZmYsFXwsuOEQnFkhUGHfWwTbmOmxzy6dtC24KYhnWG5OGFVJXh
18 | 3B8jQJGGs2ANfusI/Z0o15tAnQy5fqsLf9TT3RX7RG2ujIiDBsU+A1g//IXmSxxkUOQMZs
19 | v+cMI8KfODAXmQtB30+yAgoV03Zb/bdptv+HqPT4eeecstJUxzEGYADt1mDq3uV7fQbNmo
20 | 80bppU52JjztrJb7hBmXsXHPRRK6spQ1FCatqvu1ggZeXZpEifNsHeqCljt87ueXsQsORY
21 | pvhLzjTbTKZmjLDPuB+GxUNLEKh1ZNyAqKng==
22 | -----END OPENSSH PRIVATE KEY-----
23 |
--------------------------------------------------------------------------------
/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_256.key:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | MHcCAQEEIKB6ty3yVyKEnfF/zprx0qwC76MsMlHY4HXCnqho2eKioAoGCCqGSM49
3 | AwEHoUQDQgAElI9mbdlaS+T9nHxY/59lFnn80EEecZDBHq4gLpccY8Mge5ZTMiMD
4 | ADRvOqQ5R98Sxst765CAqXmRtz8vwoD96g==
5 | -----END EC PRIVATE KEY-----
6 |
--------------------------------------------------------------------------------
/tests/test_ecdsa_384.key:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | MIGkAgEBBDBDdO8IXvlLJgM7+sNtPl7tI7FM5kzuEUEEPRjXIPQM7mISciwJPBt+
3 | y43EuG8nL4mgBwYFK4EEACKhZANiAAQWxom0C1vQAGYhjdoREMVmGKBWlisDdzyk
4 | mgyUjKpiJ9WfbIEVLsPGP8OdNjhr1y/8BZNIts+dJd6VmYw+4HzB+4F+U1Igs8K0
5 | JEvh59VNkvWheViadDXCM2MV8Nq+DNg=
6 | -----END EC PRIVATE KEY-----
7 |
--------------------------------------------------------------------------------
/tests/test_ecdsa_384_o.key:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABDwIHkBEZ
3 | 75XuqQS6/7daAIAAAAEAAAAAEAAACIAAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlz
4 | dHAzODQAAABhBIch5LXTq/L/TWsTGG6dIktxD8DIMh7EfvoRmWsks6CuNDTvFvbQNtY4QO
5 | 1mn5OXegHbS0M5DPIS++wpKGFP3suDEH08O35vZQasLNrL0tO2jyyEnzB2ZEx3PPYci811
6 | ygAAAOBKGxFl+JcMHjldOdTA9iwv88gxoelCwln/NATglUuyzHMLJwx53n8NLqrnHALvbz
7 | RHjyTmjU4dbSM9o9Vjhcvq+1aipjAQg2qx825f7T4BMoKyhLBS/qTg7RfyW/h0Sbequ1wl
8 | PhBfwhv0LUphRFsGdnOgrXWfZqWqxOP1WhJWIh1p+ja5va/Ii/+hD6RORQjvzbHTPJA53c
9 | OguISImkx0vdqPuFTLyclaC3eO4Px68Ki0b8cdyivExbAWLkNOtBdIAgeO7Egbruu4O5Sn
10 | I6bn1Kc+kZlWtO02IkwSA5DaKw==
11 | -----END OPENSSH PRIVATE KEY-----
12 |
--------------------------------------------------------------------------------
/tests/test_ecdsa_521.key:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | MIHcAgEBBEIAprQtAS3OF6iVUkT8IowTHWicHzShGgk86EtuEXvfQnhZFKsWm6Jo
3 | iqAr1yEaiuI9LfB3Xs8cjuhgEEfbduYr/f6gBwYFK4EEACOhgYkDgYYABACaOaFL
4 | ZGuxa5AW16qj6VLypFbLrEWrt9AZUloCMefxO8bNLjK/O5g0rAVasar1TnyHE9qj
5 | 4NwzANZASWjQNbc4MAG8vzqezFwLIn/kNyNTsXNfqEko9OgHZknlj2Z79dwTJcRA
6 | L4QLcT5aND0EHZLB2fAUDXiWIb2j4rg1mwPlBMiBXA==
7 | -----END EC PRIVATE KEY-----
8 |
--------------------------------------------------------------------------------
/tests/test_ecdsa_password_256.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_ecdsa_password_384.key:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | Proc-Type: 4,ENCRYPTED
3 | DEK-Info: AES-128-CBC,7F7B5DBE4CE040D822441AFE7A023A1D
4 |
5 | y/d6tGonAXYgJniQoFCdto+CuT1y1s41qzwNLN9YdNq/+R/dtQvZAaOuGtHJRFE6
6 | wWabhY1bSjavVPT2z1Zw1jhDJX5HGrf9LDoyORKtUWtUJoUvGdYLHbcg8Q+//WRf
7 | R0A01YuSw1SJX0a225S1aRcsDAk1k5F8EMb8QzSSDgjAOI8ldQF35JI+ofNSGjgS
8 | BPOlorQXTJxDOGmokw/Wql6MbhajXKPO39H2Z53W88U=
9 | -----END EC PRIVATE KEY-----
10 |
--------------------------------------------------------------------------------
/tests/test_ecdsa_password_521.key:
--------------------------------------------------------------------------------
1 | -----BEGIN EC PRIVATE KEY-----
2 | Proc-Type: 4,ENCRYPTED
3 | DEK-Info: AES-128-CBC,AEB2DE62C65D1A88C4940A3476B2F10A
4 |
5 | 5kNk/FFPbHa0402QTrgpIT28uirJ4Amvb2/ryOEyOCe0NPbTLCqlQekj2RFYH2Un
6 | pgCLUDkelKQv4pyuK8qWS7R+cFjE/gHHCPUWkK3djZUC8DKuA9lUKeQIE+V1vBHc
7 | L5G+MpoYrPgaydcGx/Uqnc/kVuZx1DXLwrGGtgwNROVBtmjXC9EdfeXHLL1y0wvH
8 | paNgacJpUtgqJEmiehf7eL/eiReegG553rZK3jjfboGkREUaKR5XOgamiKUtgKoc
9 | sMpImVYCsRKd/9RI+VOqErZaEvy/9j0Ye3iH32wGOaA=
10 | -----END EC PRIVATE KEY-----
11 |
--------------------------------------------------------------------------------
/tests/test_ed25519.key:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
3 | QyNTUxOQAAACB69SvZKJh/9VgSL0G27b5xVYa8nethH3IERbi0YqJDXwAAAKhjwAdrY8AH
4 | awAAAAtzc2gtZWQyNTUxOQAAACB69SvZKJh/9VgSL0G27b5xVYa8nethH3IERbi0YqJDXw
5 | AAAEA9tGQi2IrprbOSbDCF+RmAHd6meNSXBUQ2ekKXm4/8xnr1K9komH/1WBIvQbbtvnFV
6 | hryd62EfcgRFuLRiokNfAAAAI2FsZXhfZ2F5bm9yQEFsZXhzLU1hY0Jvb2stQWlyLmxvY2
7 | FsAQI=
8 | -----END OPENSSH PRIVATE KEY-----
9 |
--------------------------------------------------------------------------------
/tests/test_ed25519_nopad.key:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
3 | QyNTUxOQAAACAHzPvYoDSkMVX52/CbA2M2aSBS7R0wt/9b2n5n+osNygAAAJAHZ1meB2dZ
4 | ngAAAAtzc2gtZWQyNTUxOQAAACAHzPvYoDSkMVX52/CbA2M2aSBS7R0wt/9b2n5n+osNyg
5 | AAAEAIyamvYUpzCovQuUtLhz+fwE4qYQo+rTuUVIX4fmTzMAfM+9igNKQxVfnb8JsDYzZp
6 | IFLtHTC3/1vafmf6iw3KAAAADW15IGNvbW1lbnQgaXM=
7 | -----END OPENSSH PRIVATE KEY-----
8 |
--------------------------------------------------------------------------------
/tests/test_ed25519_password.key:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABDaKD4ac7
3 | kieb+UfXaLaw68AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIOQn7fjND5ozMSV3
4 | CvbEtIdT73hWCMRjzS/lRdUDw50xAAAAsE8kLGyYBnl9ihJNqv378y6mO3SkzrDbWXOnK6
5 | ij0vnuTAvcqvWHAnyu6qBbplu/W2m55ZFeAItgaEcV2/V76sh/sAKlERqrLFyXylN0xoOW
6 | NU5+zU08aTlbSKGmeNUU2xE/xfJq12U9XClIRuVUkUpYANxNPbmTRpVrbD3fgXMhK97Jrb
7 | DEn8ca1IqMPiYmd/hpe5+tq3OxyRljXjCUFWTnqkp9VvUdzSTdSGZHsW9i
8 | -----END OPENSSH PRIVATE KEY-----
9 |
--------------------------------------------------------------------------------
/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 socket
26 |
27 | from .util import KerberosTestCase, update_env
28 |
29 |
30 | class GSSAPITest(KerberosTestCase):
31 | def setUp(self):
32 | super(GSSAPITest, self).setUp()
33 | self.krb5_mech = "1.2.840.113554.1.2.2"
34 | self.targ_name = self.realm.hostname
35 | self.server_mode = False
36 | update_env(self, self.realm.env)
37 |
38 | def test_pyasn1(self):
39 | """
40 | Test the used methods of pyasn1.
41 | """
42 | from pyasn1.type.univ import ObjectIdentifier
43 | from pyasn1.codec.der import encoder, decoder
44 | oid = encoder.encode(ObjectIdentifier(self.krb5_mech))
45 | mech, __ = decoder.decode(oid)
46 | self.assertEqual(self.krb5_mech, mech.__str__())
47 |
48 | def _gssapi_sspi_test(self):
49 | try:
50 | import gssapi
51 | _API = "MIT-NEW"
52 | except ImportError:
53 | import sspicon
54 | import sspi
55 | _API = "SSPI"
56 |
57 | c_token = None
58 | gss_ctxt_status = False
59 | mic_msg = b"G'day Mate!"
60 |
61 | if _API == "MIT-NEW":
62 | if self.server_mode:
63 | gss_flags = (gssapi.RequirementFlag.protection_ready,
64 | gssapi.RequirementFlag.integrity,
65 | gssapi.RequirementFlag.mutual_authentication,
66 | gssapi.RequirementFlag.delegate_to_peer)
67 | else:
68 | gss_flags = (gssapi.RequirementFlag.protection_ready,
69 | gssapi.RequirementFlag.integrity,
70 | gssapi.RequirementFlag.delegate_to_peer)
71 | # Initialize a GSS-API context.
72 | krb5_oid = gssapi.MechType.kerberos
73 | target_name = gssapi.Name("host@" + self.targ_name,
74 | name_type=gssapi.NameType.hostbased_service)
75 | gss_ctxt = gssapi.SecurityContext(name=target_name,
76 | flags=gss_flags,
77 | mech=krb5_oid,
78 | usage='initiate')
79 | if self.server_mode:
80 | c_token = gss_ctxt.step(c_token)
81 | gss_ctxt_status = gss_ctxt.complete
82 | self.assertEqual(False, gss_ctxt_status)
83 | # Accept a GSS-API context.
84 | gss_srv_ctxt = gssapi.SecurityContext(usage='accept')
85 | s_token = gss_srv_ctxt.step(c_token)
86 | gss_ctxt_status = gss_srv_ctxt.complete
87 | self.assertNotEqual(None, s_token)
88 | self.assertEqual(True, gss_ctxt_status)
89 | # Establish the client context
90 | c_token = gss_ctxt.step(s_token)
91 | self.assertEqual(None, c_token)
92 | else:
93 | while not gss_ctxt.complete:
94 | c_token = gss_ctxt.step(c_token)
95 | self.assertNotEqual(None, c_token)
96 | # Build MIC
97 | mic_token = gss_ctxt.get_signature(mic_msg)
98 |
99 | if self.server_mode:
100 | # Check MIC
101 | status = gss_srv_ctxt.verify_signature(mic_msg, mic_token)
102 | self.assertEqual(0, status)
103 |
104 | else:
105 | gss_flags = (
106 | sspicon.ISC_REQ_INTEGRITY |
107 | sspicon.ISC_REQ_MUTUAL_AUTH |
108 | sspicon.ISC_REQ_DELEGATE
109 | )
110 | # Initialize a GSS-API context.
111 | target_name = "host/" + socket.getfqdn(self.targ_name)
112 | gss_ctxt = sspi.ClientAuth("Kerberos",
113 | scflags=gss_flags,
114 | targetspn=target_name)
115 | if self.server_mode:
116 | error, token = gss_ctxt.authorize(c_token)
117 | c_token = token[0].Buffer
118 | self.assertEqual(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.assertEqual(None, c_token)
127 | self.assertEqual(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.assertNotEqual(0, error)
136 |
137 | def test_gssapi_sspi_client(self):
138 | self._gssapi_sspi_test()
139 |
140 | def test_gssapi_sspi_server(self):
141 | self.server_mode = True
142 | self._gssapi_sspi_test()
143 |
--------------------------------------------------------------------------------
/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 |
27 | import paramiko
28 | from paramiko.py3compat import decodebytes
29 |
30 |
31 | test_hosts_file = """\
32 | secure.example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1PD6U2/TVxET6lkpKhOk5r\
33 | 9q/kAYG6sP9f5zuUYP8i7FOFp/6ncCEbbtg/lB+A3iidyxoSWl+9jtoyyDOOVX4UIDV9G11Ml8om3\
34 | D+jrpI9cycZHqilK0HmxDeCuxbwyMuaCygU9gS2qoRvNLWZk70OpIKSSpBo0Wl3/XUmz9uhc=
35 | broken.example.com ssh-rsa AAAA
36 | happy.example.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA8bP1ZA7DCZDB9J0s50l31M\
37 | BGQ3GQ/Fc7SX6gkpXkwcZryoi4kNFhHu5LvHcZPdxXV1D+uTMfGS1eyd2Yz/DoNWXNAl8TI0cAsW\
38 | 5ymME3bQ4J/k1IKxCtz/bAlAqFgKoc+EolMziDYqWIATtW0rYTJvzGAzTmMj80/QpsFH+Pc2M=
39 | """
40 |
41 | keyblob = b"""\
42 | AAAAB3NzaC1yc2EAAAABIwAAAIEA8bP1ZA7DCZDB9J0s50l31MBGQ3GQ/Fc7SX6gkpXkwcZryoi4k\
43 | NFhHu5LvHcZPdxXV1D+uTMfGS1eyd2Yz/DoNWXNAl8TI0cAsW5ymME3bQ4J/k1IKxCtz/bAlAqFgK\
44 | oc+EolMziDYqWIATtW0rYTJvzGAzTmMj80/QpsFH+Pc2M="""
45 |
46 | keyblob_dss = b"""\
47 | AAAAB3NzaC1kc3MAAACBAOeBpgNnfRzr/twmAQRu2XwWAp3CFtrVnug6s6fgwj/oLjYbVtjAy6pl/\
48 | h0EKCWx2rf1IetyNsTxWrniA9I6HeDj65X1FyDkg6g8tvCnaNB8Xp/UUhuzHuGsMIipRxBxw9LF60\
49 | 8EqZcj1E3ytktoW5B5OcjrkEoz3xG7C+rpIjYvAAAAFQDwz4UnmsGiSNu5iqjn3uTzwUpshwAAAIE\
50 | AkxfFeY8P2wZpDjX0MimZl5wkoFQDL25cPzGBuB4OnB8NoUk/yjAHIIpEShw8V+LzouMK5CTJQo5+\
51 | Ngw3qIch/WgRmMHy4kBq1SsXMjQCte1So6HBMvBPIW5SiMTmjCfZZiw4AYHK+B/JaOwaG9yRg2Ejg\
52 | 4Ok10+XFDxlqZo8Y+wAAACARmR7CCPjodxASvRbIyzaVpZoJ/Z6x7dAumV+ysrV1BVYd0lYukmnjO\
53 | 1kKBWApqpH1ve9XDQYN8zgxM4b16L21kpoWQnZtXrY3GZ4/it9kUgyB7+NwacIBlXa8cMDL7Q/69o\
54 | 0d54U0X/NeX5QxuYR6OMJlrkQB7oiW/P/1mwjQgE="""
55 |
56 |
57 | class HostKeysTest (unittest.TestCase):
58 |
59 | def setUp(self):
60 | with open('hostfile.temp', 'w') as f:
61 | f.write(test_hosts_file)
62 |
63 | def tearDown(self):
64 | os.unlink('hostfile.temp')
65 |
66 | def test_load(self):
67 | hostdict = paramiko.HostKeys('hostfile.temp')
68 | self.assertEqual(2, len(hostdict))
69 | self.assertEqual(1, len(list(hostdict.values())[0]))
70 | self.assertEqual(1, len(list(hostdict.values())[1]))
71 | fp = hexlify(hostdict['secure.example.com']['ssh-rsa'].get_fingerprint()).upper()
72 | self.assertEqual(b'E6684DB30E109B67B70FF1DC5C7F1363', fp)
73 |
74 | def test_add(self):
75 | hostdict = paramiko.HostKeys('hostfile.temp')
76 | hh = '|1|BMsIC6cUIP2zBuXR3t2LRcJYjzM=|hpkJMysjTk/+zzUUzxQEa2ieq6c='
77 | key = paramiko.RSAKey(data=decodebytes(keyblob))
78 | hostdict.add(hh, 'ssh-rsa', key)
79 | self.assertEqual(3, len(list(hostdict)))
80 | x = hostdict['foo.example.com']
81 | fp = hexlify(x['ssh-rsa'].get_fingerprint()).upper()
82 | self.assertEqual(b'7EC91BB336CB6D810B124B1353C32396', fp)
83 | self.assertTrue(hostdict.check('foo.example.com', key))
84 |
85 | def test_dict(self):
86 | hostdict = paramiko.HostKeys('hostfile.temp')
87 | self.assertTrue('secure.example.com' in hostdict)
88 | self.assertTrue('not.example.com' not in hostdict)
89 | self.assertTrue('secure.example.com' in hostdict)
90 | self.assertTrue('not.example.com' not in hostdict)
91 | x = hostdict.get('secure.example.com', None)
92 | self.assertTrue(x is not None)
93 | fp = hexlify(x['ssh-rsa'].get_fingerprint()).upper()
94 | self.assertEqual(b'E6684DB30E109B67B70FF1DC5C7F1363', fp)
95 | i = 0
96 | for key in hostdict:
97 | i += 1
98 | self.assertEqual(2, i)
99 |
100 | def test_dict_set(self):
101 | hostdict = paramiko.HostKeys('hostfile.temp')
102 | key = paramiko.RSAKey(data=decodebytes(keyblob))
103 | key_dss = paramiko.DSSKey(data=decodebytes(keyblob_dss))
104 | hostdict['secure.example.com'] = {
105 | 'ssh-rsa': key,
106 | 'ssh-dss': key_dss
107 | }
108 | hostdict['fake.example.com'] = {}
109 | hostdict['fake.example.com']['ssh-rsa'] = key
110 |
111 | self.assertEqual(3, len(hostdict))
112 | self.assertEqual(2, len(list(hostdict.values())[0]))
113 | self.assertEqual(1, len(list(hostdict.values())[1]))
114 | self.assertEqual(1, len(list(hostdict.values())[2]))
115 | fp = hexlify(hostdict['secure.example.com']['ssh-rsa'].get_fingerprint()).upper()
116 | self.assertEqual(b'7EC91BB336CB6D810B124B1353C32396', fp)
117 | fp = hexlify(hostdict['secure.example.com']['ssh-dss'].get_fingerprint()).upper()
118 | self.assertEqual(b'4478F0B9A23CC5182009FF755BC1D26C', fp)
119 |
120 | def test_delitem(self):
121 | hostdict = paramiko.HostKeys('hostfile.temp')
122 | target = 'happy.example.com'
123 | _ = hostdict[target] # will KeyError if not present
124 | del hostdict[target]
125 | try:
126 | _ = hostdict[target]
127 | except KeyError:
128 | pass # Good
129 | else:
130 | assert False, "Entry was not deleted from HostKeys on delitem!"
131 |
132 | def test_entry_delitem(self):
133 | hostdict = paramiko.HostKeys('hostfile.temp')
134 | target = 'happy.example.com'
135 | entry = hostdict[target]
136 | key_type_list = [key_type for key_type in entry]
137 | for key_type in key_type_list:
138 | del entry[key_type]
139 |
140 | # will KeyError if not present
141 | for key_type in key_type_list:
142 | try:
143 | del entry[key_type]
144 | except KeyError:
145 | pass # Good
146 | else:
147 | assert False, "Key was not deleted from Entry on delitem!"
148 |
--------------------------------------------------------------------------------
/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 | from .util import KerberosTestCase, update_env
35 |
36 |
37 | class NullServer (paramiko.ServerInterface):
38 |
39 | def get_allowed_auths(self, username):
40 | return 'gssapi-keyex'
41 |
42 | def check_auth_gssapi_keyex(self, username,
43 | gss_authenticated=paramiko.AUTH_FAILED,
44 | cc_file=None):
45 | if gss_authenticated == paramiko.AUTH_SUCCESSFUL:
46 | return paramiko.AUTH_SUCCESSFUL
47 | return paramiko.AUTH_FAILED
48 |
49 | def enable_auth_gssapi(self):
50 | UseGSSAPI = True
51 | return UseGSSAPI
52 |
53 | def check_channel_request(self, kind, chanid):
54 | return paramiko.OPEN_SUCCEEDED
55 |
56 | def check_channel_exec_request(self, channel, command):
57 | if command != b"yes":
58 | return False
59 | return True
60 |
61 |
62 | class GSSKexTest(KerberosTestCase):
63 | def setUp(self):
64 | self.username = self.realm.user_princ
65 | self.hostname = socket.getfqdn(self.realm.hostname)
66 | self.sockl = socket.socket()
67 | self.sockl.bind((self.realm.hostname, 0))
68 | self.sockl.listen(1)
69 | self.addr, self.port = self.sockl.getsockname()
70 | self.event = threading.Event()
71 | update_env(self, self.realm.env)
72 | thread = threading.Thread(target=self._run)
73 | thread.start()
74 |
75 | def tearDown(self):
76 | for attr in "tc ts socks sockl".split():
77 | if hasattr(self, attr):
78 | getattr(self, attr).close()
79 |
80 | def _run(self):
81 | self.socks, addr = self.sockl.accept()
82 | self.ts = paramiko.Transport(self.socks, gss_kex=True)
83 | host_key = paramiko.load_private_key_file('tests/test_rsa.key')
84 | self.ts.add_server_key(host_key)
85 | self.ts.set_gss_host(self.realm.hostname)
86 | try:
87 | self.ts.load_server_moduli()
88 | except:
89 | print('(Failed to load moduli -- gex will be unsupported.)')
90 | server = NullServer()
91 | self.ts.start_server(self.event, server)
92 |
93 | def _test_gsskex_and_auth(self, gss_host, rekey=False):
94 | """
95 | Verify that Paramiko can handle SSHv2 GSS-API / SSPI authenticated
96 | Diffie-Hellman Key Exchange and user authentication with the GSS-API
97 | context created during key exchange.
98 | """
99 | host_key = paramiko.load_private_key_file('tests/test_rsa.key')
100 | public_host_key = paramiko.RSAKey(data=host_key.asbytes())
101 |
102 | self.tc = paramiko.SSHClient()
103 | self.tc.get_host_keys().add('[%s]:%d' % (self.hostname, self.port),
104 | 'ssh-rsa', public_host_key)
105 | self.tc.connect(self.hostname, self.port, username=self.username,
106 | gss_auth=True, gss_kex=True, gss_host=gss_host)
107 |
108 | self.event.wait(1.0)
109 | self.assertTrue(self.event.is_set())
110 | self.assertTrue(self.ts.is_active())
111 | self.assertEqual(self.username, self.ts.get_username())
112 | self.assertTrue(self.ts.is_authenticated())
113 | self.assertTrue(self.tc.get_transport().gss_kex_used)
114 |
115 | stdin, stdout, stderr = self.tc.exec_command('yes')
116 | schan = self.ts.accept(1.0)
117 | if rekey:
118 | self.tc.get_transport().renegotiate_keys()
119 |
120 | schan.send('Hello there.\n')
121 | schan.send_stderr('This is on stderr.\n')
122 | schan.close()
123 |
124 | self.assertEqual('Hello there.\n', stdout.readline())
125 | self.assertEqual('', stdout.readline())
126 | self.assertEqual('This is on stderr.\n', stderr.readline())
127 | self.assertEqual('', stderr.readline())
128 |
129 | stdin.close()
130 | stdout.close()
131 | stderr.close()
132 |
133 | def test_gsskex_and_auth(self):
134 | """
135 | Verify that Paramiko can handle SSHv2 GSS-API / SSPI authenticated
136 | Diffie-Hellman Key Exchange and user authentication with the GSS-API
137 | context created during key exchange.
138 | """
139 | self._test_gsskex_and_auth(gss_host=None)
140 |
141 | @unittest.expectedFailure # https://github.com/paramiko/paramiko/issues/1312
142 | def test_gsskex_and_auth_rekey(self):
143 | """
144 | Verify that Paramiko can rekey.
145 | """
146 | self._test_gsskex_and_auth(gss_host=None, rekey=True)
147 |
--------------------------------------------------------------------------------
/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 |
25 | from paramiko.message import Message
26 | from paramiko.common import byte_chr, zero_byte
27 |
28 |
29 | class MessageTest (unittest.TestCase):
30 |
31 | __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 # noqa: E501
32 | __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' # noqa: E501
33 | __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' # noqa: E501
34 | __d = b'\x00\x00\x00\x05\x01\x00\x00\x00\x03\x63\x61\x74\x00\x00\x00\x03\x61\x2c\x62'
35 |
36 | def test_encode(self):
37 | msg = Message()
38 | msg.add_int(23)
39 | msg.add_int(123789456)
40 | msg.add_string('q')
41 | msg.add_string('hello')
42 | msg.add_string('x' * 1000)
43 | self.assertEqual(msg.asbytes(), self.__a)
44 |
45 | msg = Message()
46 | msg.add_boolean(True)
47 | msg.add_boolean(False)
48 | msg.add_byte(byte_chr(0xf3))
49 |
50 | msg.add_bytes(zero_byte + byte_chr(0x3f))
51 | msg.add_list(['huey', 'dewey', 'louie'])
52 | self.assertEqual(msg.asbytes(), self.__b)
53 |
54 | msg = Message()
55 | msg.add_int64(5)
56 | msg.add_int64(0xf5e4d3c2b109)
57 | msg.add_mpint(17)
58 | msg.add_mpint(0xf5e4d3c2b109)
59 | msg.add_mpint(-0x65e4d3c2b109)
60 | self.assertEqual(msg.asbytes(), self.__c)
61 |
62 | def test_decode(self):
63 | msg = Message(self.__a)
64 | self.assertEqual(msg.get_int(), 23)
65 | self.assertEqual(msg.get_int(), 123789456)
66 | self.assertEqual(msg.get_text(), 'q')
67 | self.assertEqual(msg.get_text(), 'hello')
68 | self.assertEqual(msg.get_text(), 'x' * 1000)
69 |
70 | msg = Message(self.__b)
71 | self.assertEqual(msg.get_boolean(), True)
72 | self.assertEqual(msg.get_boolean(), False)
73 | self.assertEqual(msg.get_byte(), byte_chr(0xf3))
74 | self.assertEqual(msg.get_bytes(2), zero_byte + byte_chr(0x3f))
75 | self.assertEqual(msg.get_list(), ['huey', 'dewey', 'louie'])
76 |
77 | msg = Message(self.__c)
78 | self.assertEqual(msg.get_int64(), 5)
79 | self.assertEqual(msg.get_int64(), 0xf5e4d3c2b109)
80 | self.assertEqual(msg.get_mpint(), 17)
81 | self.assertEqual(msg.get_mpint(), 0xf5e4d3c2b109)
82 | self.assertEqual(msg.get_mpint(), -0x65e4d3c2b109)
83 |
84 | def test_add(self):
85 | msg = Message()
86 | msg.add(5)
87 | msg.add(True)
88 | msg.add('cat')
89 | msg.add(['a', 'b'])
90 | self.assertEqual(msg.asbytes(), self.__d)
91 |
92 | def test_misc(self):
93 | msg = Message(self.__d)
94 | self.assertEqual(msg.get_int(), 5)
95 | self.assertEqual(msg.get_boolean(), True)
96 | self.assertEqual(msg.get_text(), 'cat')
97 | self.assertEqual(msg.get_so_far(), self.__d[:12])
98 | self.assertEqual(msg.get_remainder(), self.__d[12:])
99 |
--------------------------------------------------------------------------------
/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 sys
24 | import unittest
25 | from hashlib import sha1
26 |
27 | import pytest
28 |
29 | from cryptography.hazmat.backends import default_backend
30 | from cryptography.hazmat.primitives.ciphers import algorithms, Cipher, modes
31 |
32 | from paramiko import Message, Packetizer, util
33 | from paramiko.common import byte_chr, zero_byte
34 |
35 | from .loop import LoopSocket
36 |
37 |
38 | x55 = byte_chr(0x55)
39 | x1f = byte_chr(0x1f)
40 |
41 |
42 | class PacketizerTest (unittest.TestCase):
43 |
44 | def test_write(self):
45 | rsock = LoopSocket()
46 | wsock = LoopSocket()
47 | rsock.link(wsock)
48 | p = Packetizer(wsock)
49 | p.set_log(util.get_logger('paramiko.transport'))
50 | p.set_hexdump(True)
51 | encryptor = Cipher(
52 | algorithms.AES(zero_byte * 16),
53 | modes.CBC(x55 * 16),
54 | backend=default_backend()
55 | ).encryptor()
56 | p.set_outbound_cipher(encryptor, 16, sha1, 12, x1f * 20)
57 |
58 | # message has to be at least 16 bytes long, so we'll have at least one
59 | # block of data encrypted that contains zero random padding bytes
60 | m = Message()
61 | m.add_byte(byte_chr(100))
62 | m.add_int(100)
63 | m.add_int(1)
64 | m.add_int(900)
65 | p.send_message(m)
66 | data = rsock.recv(100)
67 | # 32 + 12 bytes of MAC = 44
68 | self.assertEqual(44, len(data))
69 | self.assertEqual(
70 | b'\x43\x91\x97\xbd\x5b\x50\xac\x25\x87\xc2\xc4\x6b\xc7\xe9\x38\xc0', data[:16]
71 | )
72 |
73 | def test_read(self):
74 | rsock = LoopSocket()
75 | wsock = LoopSocket()
76 | rsock.link(wsock)
77 | p = Packetizer(rsock)
78 | p.set_log(util.get_logger('paramiko.transport'))
79 | p.set_hexdump(True)
80 | decryptor = Cipher(
81 | algorithms.AES(zero_byte * 16),
82 | modes.CBC(x55 * 16),
83 | backend=default_backend()
84 | ).decryptor()
85 | p.set_inbound_cipher(decryptor, 16, sha1, 12, x1f * 20)
86 | 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') # noqa: E501
87 | cmd, m = p.read_message()
88 | self.assertEqual(100, cmd)
89 | self.assertEqual(100, m.get_int())
90 | self.assertEqual(1, m.get_int())
91 | self.assertEqual(900, m.get_int())
92 |
93 | @pytest.mark.skipif(sys.platform.startswith('win'), reason="no SIGALRM on windows")
94 | def test_closed(self):
95 | rsock = LoopSocket()
96 | wsock = LoopSocket()
97 | rsock.link(wsock)
98 | p = Packetizer(wsock)
99 | p.set_log(util.get_logger('paramiko.transport'))
100 | p.set_hexdump(True)
101 | encryptor = Cipher(
102 | algorithms.AES(zero_byte * 16),
103 | modes.CBC(x55 * 16),
104 | backend=default_backend()
105 | ).encryptor()
106 | p.set_outbound_cipher(encryptor, 16, sha1, 12, x1f * 20)
107 |
108 | # message has to be at least 16 bytes long, so we'll have at least one
109 | # block of data encrypted that contains zero random padding bytes
110 | m = Message()
111 | m.add_byte(byte_chr(100))
112 | m.add_int(100)
113 | m.add_int(1)
114 | m.add_int(900)
115 | wsock.send = lambda x: 0
116 | from functools import wraps
117 | import errno
118 | import os
119 | import signal
120 |
121 | class TimeoutError(Exception):
122 | def __init__(self, error_message):
123 | if hasattr(errno, 'ETIME'):
124 | self.message = os.sterror(errno.ETIME)
125 | else:
126 | self.messaage = error_message
127 |
128 | def timeout(seconds=1, error_message='Timer expired'):
129 | def decorator(func):
130 | def _handle_timeout(signum, frame):
131 | raise TimeoutError(error_message)
132 |
133 | def wrapper(*args, **kwargs):
134 | signal.signal(signal.SIGALRM, _handle_timeout)
135 | signal.alarm(seconds)
136 | try:
137 | result = func(*args, **kwargs)
138 | finally:
139 | signal.alarm(0)
140 | return result
141 |
142 | return wraps(func)(wrapper)
143 |
144 | return decorator
145 | send = timeout()(p.send_message)
146 | self.assertRaises(EOFError, send, m)
147 |
--------------------------------------------------------------------------------
/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.key.pub:
--------------------------------------------------------------------------------
1 | ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA049W6geFpmsljTwfvI1UmKWWJPNFI74+vNKTk4dmzkQY2yAMs6FhlvhlI8ysU4oj71ZsRYMecHbBbxdN79+JRFVYTKaLqjwGENeTd+yv4q+V2PvZv3fLnzApI3l7EJCqhWwJUHJ1jAkZzqDx0tyOL4uoZpww3nmE0kb3y21tH4c=
2 |
--------------------------------------------------------------------------------
/tests/test_rsa_2k_o.key:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABD0R3hOFS
3 | FMb2SJeo5h8QPNAAAAEAAAAAEAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQDF+Dpr54DX
4 | 0WdeTDpNAMdkCWEkl3OXtNgf58qlN1gX572OLBqLf0zT4bHstUEpU3piazph/rSWcUMuBo
5 | D46tZ6jiH7H9b9Pem2eYQWaELDDkM+v9BMbEy5rMbFRLol5OtEvPFqneyEAanPOgvd8t3y
6 | yhSev9QVusakzJ8j8LGgrA8huYZ+Srnw0shEWLG70KUKCh3rG0QIvA8nfhtUOisr2Gp+F0
7 | YxMGb5gwBlQYAYE5l6u1SjZ7hNjyNosjK+wRBFgFFBYVpkZKJgWoK9w4ijFyzMZTucnZMq
8 | KOKAjIJvHfKBf2/cEfYxSq1EndqTqjYsd9T7/s2vcn1OH5a0wkERAAAD0JnzCJYfDeiUQ6
9 | 9LOAb6/NnhKvFjCdBYal60MfLcLBHvzHLJvTneQ4f1Vknq8xEVmRba7SDSfwaEybP/1FsP
10 | SGH6FNKA5gKllemgmcaUVr3wtNPtjX4WgsyHcwCRgHmOiyNrUj0OZR5wbZabHIIyirl4wa
11 | LBz8Jb3GalKEagtyWsBKDCKHCFNzh8xmsT1SWhnC7baRyC8e3krQm9hGbNhpj6Q5AtN3ql
12 | wBVamUp0eKxkt70mKBKI4v3DR8KqrEndeK6d0cegVEkE67fqa99a5J3uSDC8mglKrHiKEs
13 | dU1dh/bOF/H3aFpINlRwvlZ95Opby7rG0BHgbZONq0+VUnABVzNTM5Xd5UKjjCF28CrQBf
14 | XS6WeHeUx2zHtOmL1xdePk+Bii+SSUl3pLa4SDwX4nV95cSPx8vMm8dJEruxad6+MPoSuy
15 | Oyho89jqUTSgC/RPejuTgrnB3WbzE5SJb+V3zMata0J1wxbNfYKG9U+VucUZhP4+jzfNqH
16 | B/v8JqtuxnqR8NjPsK2+8wJxebL2KVNjKOm//6P3KSDsavpscGpVWOM06zUlwWCB26W3pP
17 | X/+xO9aR5wiBteFKoJG1waziIjqhOJSmvq+I/texUKEUd/eEFNt10Ubc0zy0sRYVN8rIRJ
18 | masQzCYuUylDzCa4ar1s4qngBZzWL2PRkPuXuhoHuT0J5no174GR6+6EAYZZhnq0tkYrhZ
19 | Ar0tQ4CDlI235a3MPHzvABuwYuWys1tBuLAb+6Gc6CmCiQ+mhojfQUBYG5T65iRFA5UQsH
20 | O1RLEC3yasxGcBI6d0J/fwOP/YLktNu3AeUumr0N9Xgf02DlBNwd+4GOI0LcQvl/3J8ppo
21 | bamTppKPEZ2d32VNEO+Z6Zx5DlIVm5gDeMvIvdwap445VnhL3ZZH2NCkAcXM9+0WH+Quas
22 | JCAMgPYiP9FzF+8Onmj2OmhgIVj/9eanhS3/GLrRC4xCvER2V7PwgB0I5qY110BPEttDyo
23 | IvYE51kvtdW447SK7HZywJnkyw2RNm+29dvWJJwSQckUHuZkXEtmEPk0ePL3yf2NH5XYJc
24 | pXX6Zac0KemCPIHr8l7GogE4Rb2BBTqddkegb9piz6QTAPcQnn+GuMFG06IBhUrgcMEQ8x
25 | UOXYUUrT5HvSxWUcgaYH1nfC3bTWmDaodw8/HQKyF6c44rujO2s2NLFOCAyQMUNdhh3lfD
26 | yHYLO7xYkP6xzzkpk+2lwBoeYdQdAwlKN/XqC8ZhBfwTdem/1hh1BpQJFbbFftWxU8gxxi
27 | iuI+vmlsuIsxKoGCq8YXuophx62lo=
28 | -----END OPENSSH PRIVATE KEY-----
29 |
--------------------------------------------------------------------------------
/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_exception.py:
--------------------------------------------------------------------------------
1 | import pickle
2 |
3 | import pytest
4 |
5 | from paramiko import RSAKey
6 | from paramiko.ssh_exception import (
7 | BadAuthenticationType,
8 | PartialAuthentication,
9 | ChannelException,
10 | BadHostKeyException,
11 | ProxyCommandFailure,
12 | )
13 |
14 |
15 | @pytest.mark.parametrize(['exc'], [
16 | (BadAuthenticationType("Bad authentication type", ["ok", "also-ok"]),),
17 | (PartialAuthentication(["ok", "also-ok"]),),
18 | (BadHostKeyException("myhost", RSAKey.generate(2048), RSAKey.generate(2048)),),
19 | (ProxyCommandFailure("nc servername 22", 1),),
20 | (ChannelException(17, "whatever"),),
21 | ])
22 | def test_ssh_exception_strings(exc):
23 | assert isinstance(str(exc), str)
24 | assert isinstance(repr(exc), str)
25 | if type(exc) != BadHostKeyException:
26 | ne = pickle.loads(pickle.dumps(exc))
27 | assert type(ne) == type(exc)
28 |
--------------------------------------------------------------------------------
/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 |
29 | import paramiko
30 |
31 | from .util import _support, KerberosTestCase, update_env
32 | from .test_client import FINGERPRINTS
33 |
34 |
35 | class NullServer (paramiko.ServerInterface):
36 | def get_allowed_auths(self, username):
37 | return 'gssapi-with-mic,publickey'
38 |
39 | def check_auth_gssapi_with_mic(
40 | self,
41 | username,
42 | gss_authenticated=paramiko.AUTH_FAILED,
43 | cc_file=None,
44 | ):
45 | if gss_authenticated == paramiko.AUTH_SUCCESSFUL:
46 | return paramiko.AUTH_SUCCESSFUL
47 | return paramiko.AUTH_FAILED
48 |
49 | def enable_auth_gssapi(self):
50 | return True
51 |
52 | def check_auth_publickey(self, username, key):
53 | try:
54 | expected = FINGERPRINTS[key.get_name()]
55 | except KeyError:
56 | return paramiko.AUTH_FAILED
57 | else:
58 | if key.get_fingerprint() == expected:
59 | return paramiko.AUTH_SUCCESSFUL
60 | return paramiko.AUTH_FAILED
61 |
62 | def check_channel_request(self, kind, chanid):
63 | return paramiko.OPEN_SUCCEEDED
64 |
65 | def check_channel_exec_request(self, channel, command):
66 | if command != b"yes":
67 | return False
68 | return True
69 |
70 |
71 | class GSSAuthTest(KerberosTestCase):
72 | def setUp(self):
73 | # TODO: username and targ_name should come from os.environ or whatever
74 | # the approved pytest method is for runtime-configuring test data.
75 | self.username = self.realm.user_princ
76 | self.hostname = socket.getfqdn(self.realm.hostname)
77 | self.sockl = socket.socket()
78 | self.sockl.bind((self.realm.hostname, 0))
79 | self.sockl.listen(1)
80 | self.addr, self.port = self.sockl.getsockname()
81 | self.event = threading.Event()
82 | update_env(self, self.realm.env)
83 | thread = threading.Thread(target=self._run)
84 | thread.start()
85 |
86 | def tearDown(self):
87 | for attr in "tc ts socks sockl".split():
88 | if hasattr(self, attr):
89 | getattr(self, attr).close()
90 |
91 | def _run(self):
92 | self.socks, addr = self.sockl.accept()
93 | self.ts = paramiko.Transport(self.socks)
94 | host_key = paramiko.load_private_key_file('tests/test_rsa.key')
95 | self.ts.add_server_key(host_key)
96 | server = NullServer()
97 | self.ts.start_server(self.event, server)
98 |
99 | def _test_connection(self, **kwargs):
100 | """
101 | (Most) kwargs get passed directly into SSHClient.connect().
102 |
103 | The exception is ... no exception yet
104 | """
105 | host_key = paramiko.load_private_key_file('tests/test_rsa.key')
106 | public_host_key = paramiko.RSAKey(data=host_key.asbytes())
107 |
108 | self.tc = paramiko.SSHClient()
109 | self.tc.set_missing_host_key_policy(paramiko.WarningPolicy())
110 | self.tc.get_host_keys().add('[%s]:%d' % (self.addr, self.port),
111 | 'ssh-rsa', public_host_key)
112 | self.tc.connect(hostname=self.addr, port=self.port, username=self.username,
113 | gss_host=self.hostname, gss_auth=True, **kwargs)
114 |
115 | self.event.wait(1.0)
116 | self.assertTrue(self.event.is_set())
117 | self.assertTrue(self.ts.is_active())
118 | self.assertEqual(self.username, self.ts.get_username())
119 | self.assertTrue(self.ts.is_authenticated())
120 |
121 | stdin, stdout, stderr = self.tc.exec_command('yes')
122 | schan = self.ts.accept(1.0)
123 |
124 | schan.send('Hello there.\n')
125 | schan.send_stderr('This is on stderr.\n')
126 | schan.close()
127 |
128 | self.assertEqual('Hello there.\n', stdout.readline())
129 | self.assertEqual('', stdout.readline())
130 | self.assertEqual('This is on stderr.\n', stderr.readline())
131 | self.assertEqual('', stderr.readline())
132 |
133 | stdin.close()
134 | stdout.close()
135 | stderr.close()
136 |
137 | def test_gss_auth(self):
138 | """
139 | Verify that Paramiko can handle SSHv2 GSS-API / SSPI authentication
140 | (gssapi-with-mic) in client and server mode.
141 | """
142 | self._test_connection(allow_agent=False,
143 | look_for_keys=False)
144 |
145 | def test_auth_trickledown(self):
146 | """
147 | Failed gssapi-with-mic auth doesn't prevent subsequent key auth from
148 | succeeding
149 | """
150 | self.hostname = "this_host_does_not_exists_and_causes_a_GSSAPI-exception"
151 | self._test_connection(key_filename=[_support('test_rsa.key')],
152 | allow_agent=False,
153 | look_for_keys=False)
154 |
--------------------------------------------------------------------------------
/tests/util.py:
--------------------------------------------------------------------------------
1 | from os.path import dirname, realpath, join
2 | import os
3 | import sys
4 | import unittest
5 |
6 | import pytest
7 |
8 | from paramiko.py3compat import builtins
9 | from paramiko.ssh_gss import GSS_AUTH_AVAILABLE
10 |
11 |
12 | def _support(filename):
13 | return join(dirname(realpath(__file__)), filename)
14 |
15 |
16 | def needs_builtin(name):
17 | """
18 | Skip decorated test if builtin name does not exist.
19 | """
20 | reason = "Test requires a builtin '{}'".format(name)
21 | return pytest.mark.skipif(not hasattr(builtins, name), reason=reason)
22 |
23 |
24 | slow = pytest.mark.slow
25 |
26 |
27 | # GSSAPI / Kerberos related tests need a working Kerberos environment.
28 | # The class `KerberosTestCase` provides such an environment or skips all tests.
29 | # There are 3 distinct cases:
30 | #
31 | # - A Kerberos environment has already been created and the environment
32 | # contains the required information.
33 | #
34 | # - We can use the package 'k5test' to setup an working kerberos environment on
35 | # the fly.
36 | #
37 | # - We skip all tests.
38 | #
39 | # ToDo: add a Windows specific implementation?
40 |
41 | class SkipKerberosTestCase(unittest.TestCase):
42 | @classmethod
43 | def setUpClass(cls):
44 | raise unittest.SkipTest("Missing gssapi or k5test")
45 |
46 |
47 | if not GSS_AUTH_AVAILABLE:
48 | KerberosTestCase = SkipKerberosTestCase
49 |
50 | elif (os.environ.get("K5TEST_USER_PRINC") and
51 | os.environ.get("K5TEST_HOSTNAME") and
52 | os.environ.get("KRB5_KTNAME")): # add other vars as needed
53 | # The environment provides the required information
54 |
55 | class DummyK5Realm(object):
56 | def __init__(self):
57 | for k in os.environ:
58 | if not k.startswith("K5TEST_"):
59 | continue
60 | setattr(self, k[7:].lower(), os.environ[k])
61 | self.env = {}
62 |
63 | class KerberosTestCase(unittest.TestCase):
64 | @classmethod
65 | def setUpClass(cls):
66 | cls.realm = DummyK5Realm()
67 |
68 | @classmethod
69 | def tearDownClass(cls):
70 | del cls.realm
71 | else:
72 | try:
73 | # Try to setup a kerberos environment
74 | from k5test import KerberosTestCase
75 | except Exception:
76 | KerberosTestCase = SkipKerberosTestCase
77 |
78 |
79 | def update_env(testcase, mapping, env=os.environ):
80 | """Modify os.environ during a test case and restore during cleanup."""
81 | saved_env = env.copy()
82 |
83 | def replace(target, source):
84 | target.update(source)
85 | for k in list(target):
86 | if k not in source:
87 | target.pop(k, None)
88 |
89 | testcase.addCleanup(replace, env, saved_env)
90 | env.update(mapping)
91 | return testcase
92 |
93 |
94 | def k5shell(args=None):
95 | """Create a shell with an kerberos environment
96 |
97 | This can be used to debug paramiko or to test the old GSSAPI.
98 | To test a different GSSAPI, simply activate a suitable venv
99 | within the shell.
100 | """
101 | import k5test
102 | import atexit
103 | import subprocess
104 | k5 = k5test.K5Realm()
105 | atexit.register(k5.stop)
106 | os.environ.update(k5.env)
107 | for n in ("realm", "user_princ", "hostname"):
108 | os.environ["K5TEST_" + n.upper()] = getattr(k5, n)
109 |
110 | if not args:
111 | args = sys.argv[1:]
112 | if not args:
113 | args = [os.environ.get("SHELL", "bash")]
114 | sys.exit(subprocess.call(args))
115 |
--------------------------------------------------------------------------------