asyncssh 2.22.0__tar.gz → 2.23.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {asyncssh-2.22.0/asyncssh.egg-info → asyncssh-2.23.1}/PKG-INFO +11 -7
- {asyncssh-2.22.0 → asyncssh-2.23.1}/README.rst +8 -4
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/agent.py +1 -3
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/config.py +56 -6
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/connection.py +64 -35
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/pq.py +5 -3
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/encryption.py +26 -2
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/kex.py +1 -1
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/misc.py +1 -1
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/scp.py +6 -1
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/server.py +4 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/sftp.py +30 -17
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/sk.py +1 -1
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/version.py +1 -1
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/x11.py +11 -9
- {asyncssh-2.22.0 → asyncssh-2.23.1/asyncssh.egg-info}/PKG-INFO +11 -7
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh.egg-info/requires.txt +2 -2
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/api.rst +1 -15
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/changes.rst +54 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/pyproject.toml +1 -1
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_config.py +27 -3
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_connection.py +30 -2
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_forward.py +24 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_kex.py +6 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_process.py +17 -22
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_sftp.py +14 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tox.ini +1 -1
- {asyncssh-2.22.0 → asyncssh-2.23.1}/.coveragerc +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/.github/workflows/run_tests.yml +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/.gitignore +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/.readthedocs.yaml +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/CONTRIBUTING.rst +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/COPYRIGHT +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/LICENSE +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/MANIFEST.in +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/__init__.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/agent_unix.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/agent_win32.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/asn1.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/auth.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/auth_keys.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/channel.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/compression.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/constants.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/__init__.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/chacha.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/cipher.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/dh.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/dsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/ec.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/ec_params.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/ed.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/kdf.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/misc.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/rsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/umac.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/crypto/x509.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/dsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/ecdsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/eddsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/editor.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/forward.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/gss.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/gss_unix.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/gss_win32.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/kex_dh.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/kex_rsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/keysign.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/known_hosts.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/listener.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/logging.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/mac.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/packet.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/pattern.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/pbe.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/pkcs11.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/process.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/public_key.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/py.typed +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/rsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/saslprep.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/session.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/sk_ecdsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/sk_eddsa.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/socks.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/stream.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/subprocess.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh/tuntap.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh.egg-info/SOURCES.txt +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh.egg-info/dependency_links.txt +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/asyncssh.egg-info/top_level.txt +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/_templates/sidebarbottom.html +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/_templates/sidebartop.html +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/conf.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/contributing.rst +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/index.rst +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/requirements.txt +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/rftheme/layout.html +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/rftheme/static/rftheme.css_t +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/rftheme/theme.conf +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/docs/rtd-req.txt +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/callback_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/callback_client2.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/callback_client3.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/callback_math_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/chat_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/check_exit_status.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/chroot_sftp_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/direct_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/direct_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/editor.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/gather_results.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/listening_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/local_forwarding_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/local_forwarding_client2.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/local_forwarding_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/math_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/math_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/redirect_input.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/redirect_local_pipe.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/redirect_remote_pipe.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/redirect_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/remote_forwarding_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/remote_forwarding_client2.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/remote_forwarding_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/reverse_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/reverse_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/scp_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/set_environment.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/set_terminal.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/sftp_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/show_environment.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/show_terminal.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/simple_cert_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/simple_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/simple_keyed_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/simple_scp_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/simple_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/simple_sftp_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/stream_direct_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/stream_direct_server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/examples/stream_listening_client.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/mypy.ini +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/pylintrc +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/setup.cfg +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/__init__.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/gss_stub.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/gssapi_stub.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/keysign_stub.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/pkcs11_stub.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/server.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/sk_stub.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/sspi_stub.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_agent.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_asn1.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_auth.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_auth_keys.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_channel.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_compression.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_connection_auth.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_editor.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_encryption.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_known_hosts.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_logging.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_mac.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_packet.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_pkcs11.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_public_key.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_saslprep.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_sk.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_stream.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_subprocess.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_tuntap.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_x11.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/test_x509.py +0 -0
- {asyncssh-2.22.0 → asyncssh-2.23.1}/tests/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: asyncssh
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.23.1
|
|
4
4
|
Summary: AsyncSSH: Asynchronous SSHv2 client and server library
|
|
5
5
|
Author-email: Ron Frederick <ronf@timeheart.net>
|
|
6
6
|
License: EPL-2.0 OR GPL-2.0-or-later
|
|
@@ -32,10 +32,10 @@ Provides-Extra: bcrypt
|
|
|
32
32
|
Requires-Dist: bcrypt>=3.1.3; extra == "bcrypt"
|
|
33
33
|
Provides-Extra: fido2
|
|
34
34
|
Requires-Dist: fido2>=2; extra == "fido2"
|
|
35
|
+
Provides-Extra: ifaddr
|
|
36
|
+
Requires-Dist: ifaddr>=0.2.0; extra == "ifaddr"
|
|
35
37
|
Provides-Extra: gssapi
|
|
36
38
|
Requires-Dist: gssapi>=1.2.0; extra == "gssapi"
|
|
37
|
-
Provides-Extra: libnacl
|
|
38
|
-
Requires-Dist: libnacl>=1.4.2; extra == "libnacl"
|
|
39
39
|
Provides-Extra: pkcs11
|
|
40
40
|
Requires-Dist: python-pkcs11>=0.7.0; extra == "pkcs11"
|
|
41
41
|
Provides-Extra: pyopenssl
|
|
@@ -196,6 +196,9 @@ functionality:
|
|
|
196
196
|
* Install fido2 from https://pypi.org/project/fido2 if you want support
|
|
197
197
|
for key exchange and authentication with U2F/FIDO2 security keys.
|
|
198
198
|
|
|
199
|
+
* Install ifaddr from https://pypi.org/project/ifaddr/ if you want
|
|
200
|
+
support for matching on local network IP addresses.
|
|
201
|
+
|
|
199
202
|
* Install python-pkcs11 from https://pypi.org/project/python-pkcs11 if
|
|
200
203
|
you want support for accessing PIV keys on PKCS#11 security tokens.
|
|
201
204
|
|
|
@@ -221,24 +224,25 @@ easy to install any or all of these dependencies:
|
|
|
221
224
|
|
|
222
225
|
| bcrypt
|
|
223
226
|
| fido2
|
|
227
|
+
| ifaddr
|
|
224
228
|
| gssapi
|
|
225
229
|
| pkcs11
|
|
226
230
|
| pyOpenSSL
|
|
227
231
|
| pywin32
|
|
228
232
|
|
|
229
|
-
For example, to install bcrypt, fido2, gssapi, pkcs11, and pyOpenSSL
|
|
233
|
+
For example, to install bcrypt, fido2, gssapi, ifaddr, pkcs11, and pyOpenSSL
|
|
230
234
|
on UNIX, you can run:
|
|
231
235
|
|
|
232
236
|
::
|
|
233
237
|
|
|
234
|
-
pip install 'asyncssh[bcrypt,fido2,gssapi,pkcs11,pyOpenSSL]'
|
|
238
|
+
pip install 'asyncssh[bcrypt,fido2,gssapi,ifaddr,pkcs11,pyOpenSSL]'
|
|
235
239
|
|
|
236
|
-
To install bcrypt, fido2, pkcs11, pyOpenSSL, and pywin32 on Windows,
|
|
240
|
+
To install bcrypt, fido2, ifaddr, pkcs11, pyOpenSSL, and pywin32 on Windows,
|
|
237
241
|
you can run:
|
|
238
242
|
|
|
239
243
|
::
|
|
240
244
|
|
|
241
|
-
pip install 'asyncssh[bcrypt,fido2,pkcs11,pyOpenSSL,pywin32]'
|
|
245
|
+
pip install 'asyncssh[bcrypt,fido2,ifaddr,pkcs11,pyOpenSSL,pywin32]'
|
|
242
246
|
|
|
243
247
|
Note that you will still need to manually install the libnettle library
|
|
244
248
|
for UMAC support. Unfortunately, since liboqs and libnettle are not
|
|
@@ -150,6 +150,9 @@ functionality:
|
|
|
150
150
|
* Install fido2 from https://pypi.org/project/fido2 if you want support
|
|
151
151
|
for key exchange and authentication with U2F/FIDO2 security keys.
|
|
152
152
|
|
|
153
|
+
* Install ifaddr from https://pypi.org/project/ifaddr/ if you want
|
|
154
|
+
support for matching on local network IP addresses.
|
|
155
|
+
|
|
153
156
|
* Install python-pkcs11 from https://pypi.org/project/python-pkcs11 if
|
|
154
157
|
you want support for accessing PIV keys on PKCS#11 security tokens.
|
|
155
158
|
|
|
@@ -175,24 +178,25 @@ easy to install any or all of these dependencies:
|
|
|
175
178
|
|
|
176
179
|
| bcrypt
|
|
177
180
|
| fido2
|
|
181
|
+
| ifaddr
|
|
178
182
|
| gssapi
|
|
179
183
|
| pkcs11
|
|
180
184
|
| pyOpenSSL
|
|
181
185
|
| pywin32
|
|
182
186
|
|
|
183
|
-
For example, to install bcrypt, fido2, gssapi, pkcs11, and pyOpenSSL
|
|
187
|
+
For example, to install bcrypt, fido2, gssapi, ifaddr, pkcs11, and pyOpenSSL
|
|
184
188
|
on UNIX, you can run:
|
|
185
189
|
|
|
186
190
|
::
|
|
187
191
|
|
|
188
|
-
pip install 'asyncssh[bcrypt,fido2,gssapi,pkcs11,pyOpenSSL]'
|
|
192
|
+
pip install 'asyncssh[bcrypt,fido2,gssapi,ifaddr,pkcs11,pyOpenSSL]'
|
|
189
193
|
|
|
190
|
-
To install bcrypt, fido2, pkcs11, pyOpenSSL, and pywin32 on Windows,
|
|
194
|
+
To install bcrypt, fido2, ifaddr, pkcs11, pyOpenSSL, and pywin32 on Windows,
|
|
191
195
|
you can run:
|
|
192
196
|
|
|
193
197
|
::
|
|
194
198
|
|
|
195
|
-
pip install 'asyncssh[bcrypt,fido2,pkcs11,pyOpenSSL,pywin32]'
|
|
199
|
+
pip install 'asyncssh[bcrypt,fido2,ifaddr,pkcs11,pyOpenSSL,pywin32]'
|
|
196
200
|
|
|
197
201
|
Note that you will still need to manually install the libnettle library
|
|
198
202
|
for UMAC support. Unfortunately, since liboqs and libnettle are not
|
|
@@ -121,9 +121,7 @@ class SSHAgentKeyPair(SSHKeyPair):
|
|
|
121
121
|
else:
|
|
122
122
|
sig_algorithm = algorithm
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
# ssh-agent protocol flags used to request RSA SHA2 signatures yet
|
|
126
|
-
if sig_algorithm == b'ssh-rsa' and sys.platform != 'win32':
|
|
124
|
+
if sig_algorithm == b'ssh-rsa':
|
|
127
125
|
sig_algorithms: Sequence[bytes] = \
|
|
128
126
|
(b'rsa-sha2-256', b'rsa-sha2-512', b'ssh-rsa')
|
|
129
127
|
else:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) 2020-
|
|
1
|
+
# Copyright (c) 2020-2026 by Ron Frederick <ronf@timeheart.net> and others.
|
|
2
2
|
#
|
|
3
3
|
# This program and the accompanying materials are made available under
|
|
4
4
|
# the terms of the Eclipse Public License v2.0 which accompanies this
|
|
@@ -29,20 +29,27 @@ import subprocess
|
|
|
29
29
|
from hashlib import sha1
|
|
30
30
|
from pathlib import Path, PurePath
|
|
31
31
|
from subprocess import DEVNULL
|
|
32
|
-
from typing import Callable, Dict, List, NoReturn, Optional
|
|
33
|
-
from typing import Set, Tuple, Union, cast
|
|
32
|
+
from typing import Callable, Dict, Iterator, List, NoReturn, Optional
|
|
33
|
+
from typing import Sequence, Set, Tuple, Union, cast
|
|
34
34
|
|
|
35
35
|
from .constants import DEFAULT_PORT
|
|
36
36
|
from .logging import logger
|
|
37
|
-
from .misc import DefTuple, FilePath, ip_address
|
|
37
|
+
from .misc import DefTuple, FilePath, IllegalUserName, ip_address
|
|
38
38
|
from .pattern import HostPatternList, WildcardPatternList
|
|
39
39
|
|
|
40
|
+
try:
|
|
41
|
+
import ifaddr
|
|
42
|
+
_ifaddr_available = True
|
|
43
|
+
except ImportError: # pragma: no cover
|
|
44
|
+
_ifaddr_available = False
|
|
45
|
+
|
|
40
46
|
|
|
41
47
|
ConfigPaths = Union[None, FilePath, Sequence[FilePath]]
|
|
42
48
|
|
|
43
49
|
|
|
44
50
|
_token_pattern = re.compile(r'%(.)')
|
|
45
|
-
_env_pattern = re.compile(r'\${(
|
|
51
|
+
_env_pattern = re.compile(r'\${(.*?)}')
|
|
52
|
+
_unsafe_user_pattern = re.compile(r'^\.\.$|^~|^[A-Za-z]:|[/\\]|\$\{.*?\}')
|
|
46
53
|
|
|
47
54
|
|
|
48
55
|
def _exec(cmd: str) -> bool:
|
|
@@ -52,6 +59,18 @@ def _exec(cmd: str) -> bool:
|
|
|
52
59
|
stdout=DEVNULL, stderr=DEVNULL).returncode == 0
|
|
53
60
|
|
|
54
61
|
|
|
62
|
+
def _get_local_ips() -> Iterator[str]:
|
|
63
|
+
"""Return local IP addresses of the system"""
|
|
64
|
+
|
|
65
|
+
for adapter in ifaddr.get_adapters():
|
|
66
|
+
for ip in adapter.ips:
|
|
67
|
+
if isinstance(ip.ip, tuple):
|
|
68
|
+
addr, _, scope_id = ip.ip
|
|
69
|
+
yield f'{addr}%{scope_id}' if scope_id else addr
|
|
70
|
+
else:
|
|
71
|
+
yield ip.ip
|
|
72
|
+
|
|
73
|
+
|
|
55
74
|
class ConfigParseError(ValueError):
|
|
56
75
|
"""Configuration parsing exception"""
|
|
57
76
|
|
|
@@ -183,6 +202,10 @@ class SSHConfig:
|
|
|
183
202
|
elif match == 'final':
|
|
184
203
|
result = cast(bool, self._final)
|
|
185
204
|
else:
|
|
205
|
+
if (match == 'localnetwork' and
|
|
206
|
+
not _ifaddr_available): # pragma: no cover
|
|
207
|
+
self._error('Local network match requires ifaddr module')
|
|
208
|
+
|
|
186
209
|
match_val = self._match_val(match)
|
|
187
210
|
|
|
188
211
|
if match != 'exec' and match_val is None:
|
|
@@ -201,6 +224,15 @@ class SSHConfig:
|
|
|
201
224
|
ip = ip_address(cast(str, match_val)) \
|
|
202
225
|
if match_val else None
|
|
203
226
|
result = host_pat.matches(None, match_val, ip)
|
|
227
|
+
elif match == 'localnetwork':
|
|
228
|
+
host_pat = HostPatternList(arg)
|
|
229
|
+
|
|
230
|
+
for addr in cast(Iterator[str], match_val):
|
|
231
|
+
if host_pat.matches(None, addr, ip_address(addr)):
|
|
232
|
+
result = True
|
|
233
|
+
break
|
|
234
|
+
else:
|
|
235
|
+
result = False
|
|
204
236
|
else:
|
|
205
237
|
wild_pat = WildcardPatternList(arg)
|
|
206
238
|
result = wild_pat.matches(match_val)
|
|
@@ -514,6 +546,8 @@ class SSHClientConfig(SSHConfig):
|
|
|
514
546
|
return self._options.get('Hostname', self._orig_host)
|
|
515
547
|
elif match == 'originalhost':
|
|
516
548
|
return self._orig_host
|
|
549
|
+
elif match == 'localnetwork':
|
|
550
|
+
return _get_local_ips()
|
|
517
551
|
elif match == 'localuser':
|
|
518
552
|
return self._local_user
|
|
519
553
|
elif match == 'user':
|
|
@@ -677,7 +711,23 @@ class SSHServerConfig(SSHConfig):
|
|
|
677
711
|
return None
|
|
678
712
|
|
|
679
713
|
def _set_tokens(self) -> None:
|
|
680
|
-
"""Set the tokens available for percent expansion
|
|
714
|
+
"""Set the tokens available for percent expansion
|
|
715
|
+
|
|
716
|
+
Only allow "safe" username substitutions. Unsafe usernames are:
|
|
717
|
+
|
|
718
|
+
- a username of exactly ".."
|
|
719
|
+
- a username beginning with a "~"
|
|
720
|
+
- a username beginning with a Windows drive letter and a ":"
|
|
721
|
+
- a username containing forward or backward slashes
|
|
722
|
+
- a username containing an env substitution like "${...}"
|
|
723
|
+
|
|
724
|
+
Note: this code assumes that saslprep has already been performed
|
|
725
|
+
on the username before it is accessed here.
|
|
726
|
+
|
|
727
|
+
"""
|
|
728
|
+
|
|
729
|
+
if _unsafe_user_pattern.search(self._user):
|
|
730
|
+
raise IllegalUserName('Unsafe username substitution')
|
|
681
731
|
|
|
682
732
|
self._tokens.update({'u': self._user})
|
|
683
733
|
|
|
@@ -82,6 +82,7 @@ from .constants import OPEN_UNKNOWN_CHANNEL_TYPE
|
|
|
82
82
|
|
|
83
83
|
from .encryption import Encryption, get_encryption_algs
|
|
84
84
|
from .encryption import get_default_encryption_algs
|
|
85
|
+
from .encryption import encryption_needs_mac
|
|
85
86
|
from .encryption import get_encryption_params, get_encryption
|
|
86
87
|
|
|
87
88
|
from .forward import SSHForwarder
|
|
@@ -196,6 +197,9 @@ class _TunnelProtocol(Protocol):
|
|
|
196
197
|
def close(self) -> None:
|
|
197
198
|
"""Close this tunnel"""
|
|
198
199
|
|
|
200
|
+
async def wait_closed(self):
|
|
201
|
+
"""Wait for this tunnel to close"""
|
|
202
|
+
|
|
199
203
|
class _TunnelConnectorProtocol(_TunnelProtocol, Protocol):
|
|
200
204
|
"""Protocol to open a connection to tunnel an SSH connection over"""
|
|
201
205
|
|
|
@@ -279,7 +283,7 @@ async def _canonicalize_host(loop: asyncio.AbstractEventLoop,
|
|
|
279
283
|
options: 'SSHConnectionOptions') -> Optional[str]:
|
|
280
284
|
"""Canonicalize a host name"""
|
|
281
285
|
|
|
282
|
-
host = options.
|
|
286
|
+
host = options.orig_host
|
|
283
287
|
|
|
284
288
|
if not options.canonicalize_hostname or not options.canonical_domains:
|
|
285
289
|
logger.info('Host canonicalization disabled')
|
|
@@ -387,6 +391,11 @@ async def _open_proxy(
|
|
|
387
391
|
|
|
388
392
|
self._conn.connection_lost(exc)
|
|
389
393
|
|
|
394
|
+
def process_exited(self):
|
|
395
|
+
"""Called when the child process has exited"""
|
|
396
|
+
|
|
397
|
+
self._close_event.set()
|
|
398
|
+
|
|
390
399
|
def write(self, data: bytes) -> None:
|
|
391
400
|
"""Write data to this tunnel"""
|
|
392
401
|
|
|
@@ -403,13 +412,20 @@ async def _open_proxy(
|
|
|
403
412
|
|
|
404
413
|
if self._transport: # pragma: no cover
|
|
405
414
|
self._transport.close()
|
|
415
|
+
self._transport = None
|
|
406
416
|
|
|
407
|
-
|
|
417
|
+
async def wait_closed(self):
|
|
418
|
+
"""Wait for this subprocess to exit"""
|
|
419
|
+
|
|
420
|
+
await self._close_event.wait()
|
|
408
421
|
|
|
422
|
+
_, tunnel = await loop.subprocess_exec(_ProxyCommandTunnel, *command,
|
|
423
|
+
start_new_session=True)
|
|
409
424
|
|
|
410
|
-
|
|
425
|
+
conn = cast(_Conn, cast(_ProxyCommandTunnel, tunnel).get_conn())
|
|
426
|
+
conn.set_tunnel(tunnel)
|
|
411
427
|
|
|
412
|
-
return
|
|
428
|
+
return conn
|
|
413
429
|
|
|
414
430
|
|
|
415
431
|
async def _open_tunnel(tunnels: object, options: _Options,
|
|
@@ -437,8 +453,8 @@ async def _open_tunnel(tunnels: object, options: _Options,
|
|
|
437
453
|
|
|
438
454
|
last_conn = conn
|
|
439
455
|
conn = await connect(host, port, username=username,
|
|
440
|
-
passphrase=options.passphrase,
|
|
441
|
-
config=config)
|
|
456
|
+
passphrase=options.passphrase,
|
|
457
|
+
tunnel=conn or (), config=config)
|
|
442
458
|
conn.set_tunnel(last_conn)
|
|
443
459
|
|
|
444
460
|
if options.canonicalize_hostname != 'always':
|
|
@@ -459,7 +475,7 @@ async def _connect(options: _Options, config: DefTuple[ConfigPaths],
|
|
|
459
475
|
|
|
460
476
|
canon_host = await _canonicalize_host(loop, options)
|
|
461
477
|
|
|
462
|
-
host = canon_host if canon_host else options.
|
|
478
|
+
host = canon_host if canon_host else options.orig_host
|
|
463
479
|
canonical = bool(canon_host)
|
|
464
480
|
final = options.config.has_match_final()
|
|
465
481
|
|
|
@@ -537,7 +553,7 @@ async def _connect(options: _Options, config: DefTuple[ConfigPaths],
|
|
|
537
553
|
async def _listen(options: _Options, config: DefTuple[ConfigPaths],
|
|
538
554
|
loop: asyncio.AbstractEventLoop, flags: int,
|
|
539
555
|
backlog: int, sock: Optional[socket.socket],
|
|
540
|
-
reuse_address: bool, reuse_port: bool,
|
|
556
|
+
reuse_address: Optional[bool], reuse_port: Optional[bool],
|
|
541
557
|
conn_factory: Callable[[], _Conn],
|
|
542
558
|
msg: str) -> 'SSHAcceptor':
|
|
543
559
|
"""Make inbound TCP or SSH tunneled listener"""
|
|
@@ -651,7 +667,8 @@ def _expand_algs(alg_type: str, algs: str,
|
|
|
651
667
|
|
|
652
668
|
def _select_algs(alg_type: str, algs: _AlgsArg, config_algs: _AlgsArg,
|
|
653
669
|
possible_algs: List[bytes], default_algs: List[bytes],
|
|
654
|
-
none_value: Optional[bytes] = None
|
|
670
|
+
none_value: Optional[bytes] = None,
|
|
671
|
+
allow_empty: bool = False) -> Sequence[bytes]:
|
|
655
672
|
"""Select a set of allowed algorithms"""
|
|
656
673
|
|
|
657
674
|
if algs == ():
|
|
@@ -682,6 +699,8 @@ def _select_algs(alg_type: str, algs: _AlgsArg, config_algs: _AlgsArg,
|
|
|
682
699
|
return result
|
|
683
700
|
elif none_value:
|
|
684
701
|
return [none_value]
|
|
702
|
+
elif allow_empty:
|
|
703
|
+
return []
|
|
685
704
|
else:
|
|
686
705
|
raise ValueError(f'No {alg_type} algorithms selected')
|
|
687
706
|
|
|
@@ -714,7 +733,7 @@ def _validate_algs(config: SSHConfig, kex_algs_arg: _AlgsArg,
|
|
|
714
733
|
get_default_encryption_algs())
|
|
715
734
|
mac_algs = _select_algs('MAC', mac_algs_arg,
|
|
716
735
|
cast(_AlgsArg, config.get('MACs', ())),
|
|
717
|
-
get_mac_algs(), get_default_mac_algs())
|
|
736
|
+
get_mac_algs(), get_default_mac_algs(), None, True)
|
|
718
737
|
cmp_algs = _select_algs('compression', cmp_algs_arg,
|
|
719
738
|
cast(_AlgsArg, config.get_compression_algs()),
|
|
720
739
|
get_compression_algs(),
|
|
@@ -1090,15 +1109,15 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
|
|
|
1090
1109
|
|
|
1091
1110
|
self._owner = None
|
|
1092
1111
|
|
|
1112
|
+
if self._tunnel:
|
|
1113
|
+
self._tunnel.close()
|
|
1114
|
+
self._tunnel = None
|
|
1115
|
+
|
|
1093
1116
|
self._cancel_login_timer()
|
|
1094
1117
|
self._close_event.set()
|
|
1095
1118
|
|
|
1096
1119
|
self._inpbuf = b''
|
|
1097
1120
|
|
|
1098
|
-
if self._tunnel:
|
|
1099
|
-
self._tunnel.close()
|
|
1100
|
-
self._tunnel = None
|
|
1101
|
-
|
|
1102
1121
|
def _cancel_login_timer(self) -> None:
|
|
1103
1122
|
"""Cancel the login timer"""
|
|
1104
1123
|
|
|
@@ -1489,8 +1508,8 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
|
|
|
1489
1508
|
|
|
1490
1509
|
raise KeyExchangeFailed(
|
|
1491
1510
|
f'No matching {alg_type} algorithm found, sent '
|
|
1492
|
-
f'{b",".join(local_algs).decode("ascii")} and received '
|
|
1493
|
-
f'{b",".join(remote_algs).decode("ascii")}')
|
|
1511
|
+
f'{b",".join(local_algs).decode("ascii") or "<None>"} and received '
|
|
1512
|
+
f'{b",".join(remote_algs).decode("ascii") or "<None>"}')
|
|
1494
1513
|
|
|
1495
1514
|
def _get_extra_kex_algs(self) -> List[bytes]:
|
|
1496
1515
|
"""Return the extra kex algs to add"""
|
|
@@ -1852,7 +1871,7 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
|
|
|
1852
1871
|
self.logger.debug2(' Key exchange algs: %s', kex_algs)
|
|
1853
1872
|
self.logger.debug2(' Host key algs: %s', host_key_algs)
|
|
1854
1873
|
self.logger.debug2(' Encryption algs: %s', self._enc_algs)
|
|
1855
|
-
self.logger.debug2(' MAC algs: %s', self._mac_algs)
|
|
1874
|
+
self.logger.debug2(' MAC algs: %s', self._mac_algs or '<None>')
|
|
1856
1875
|
self.logger.debug2(' Compression algs: %s', self._cmp_algs)
|
|
1857
1876
|
|
|
1858
1877
|
cookie = os.urandom(16)
|
|
@@ -1905,12 +1924,6 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
|
|
|
1905
1924
|
mac_keysize_sc, mac_hashsize_sc, etm_sc = \
|
|
1906
1925
|
get_encryption_params(self._enc_alg_sc, self._mac_alg_sc)
|
|
1907
1926
|
|
|
1908
|
-
if mac_keysize_cs == 0:
|
|
1909
|
-
self._mac_alg_cs = self._enc_alg_cs
|
|
1910
|
-
|
|
1911
|
-
if mac_keysize_sc == 0:
|
|
1912
|
-
self._mac_alg_sc = self._enc_alg_sc
|
|
1913
|
-
|
|
1914
1927
|
cmp_after_auth_cs = get_compression_params(self._cmp_alg_cs)
|
|
1915
1928
|
cmp_after_auth_sc = get_compression_params(self._cmp_alg_sc)
|
|
1916
1929
|
|
|
@@ -2406,11 +2419,11 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
|
|
|
2406
2419
|
self.logger.debug2(' Host key algs: %s', peer_host_key_algs)
|
|
2407
2420
|
self.logger.debug2(' Client to server:')
|
|
2408
2421
|
self.logger.debug2(' Encryption algs: %s', enc_algs_cs)
|
|
2409
|
-
self.logger.debug2(' MAC algs: %s', mac_algs_cs)
|
|
2422
|
+
self.logger.debug2(' MAC algs: %s', mac_algs_cs or '<None>')
|
|
2410
2423
|
self.logger.debug2(' Compression algs: %s', cmp_algs_cs)
|
|
2411
2424
|
self.logger.debug2(' Server to client:')
|
|
2412
2425
|
self.logger.debug2(' Encryption algs: %s', enc_algs_sc)
|
|
2413
|
-
self.logger.debug2(' MAC algs: %s', mac_algs_sc)
|
|
2426
|
+
self.logger.debug2(' MAC algs: %s', mac_algs_sc or '<None>')
|
|
2414
2427
|
self.logger.debug2(' Compression algs: %s', cmp_algs_sc)
|
|
2415
2428
|
|
|
2416
2429
|
kex_alg = self._choose_alg('key exchange', kex_algs, peer_kex_algs)
|
|
@@ -2431,8 +2444,17 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
|
|
|
2431
2444
|
self._enc_alg_sc = self._choose_alg('encryption', self._enc_algs,
|
|
2432
2445
|
enc_algs_sc)
|
|
2433
2446
|
|
|
2434
|
-
|
|
2435
|
-
|
|
2447
|
+
if encryption_needs_mac(self._enc_alg_cs):
|
|
2448
|
+
self._mac_alg_cs = self._choose_alg('MAC', self._mac_algs,
|
|
2449
|
+
mac_algs_cs)
|
|
2450
|
+
else:
|
|
2451
|
+
self._mac_alg_cs = self._enc_alg_cs
|
|
2452
|
+
|
|
2453
|
+
if encryption_needs_mac(self._enc_alg_sc):
|
|
2454
|
+
self._mac_alg_sc = self._choose_alg('MAC', self._mac_algs,
|
|
2455
|
+
mac_algs_sc)
|
|
2456
|
+
else:
|
|
2457
|
+
self._mac_alg_sc = self._enc_alg_sc
|
|
2436
2458
|
|
|
2437
2459
|
self._cmp_alg_cs = self._choose_alg('compression', self._cmp_algs,
|
|
2438
2460
|
cmp_algs_cs)
|
|
@@ -2851,6 +2873,9 @@ class SSHConnection(SSHPacketHandler, asyncio.Protocol):
|
|
|
2851
2873
|
if self._agent:
|
|
2852
2874
|
await self._agent.wait_closed()
|
|
2853
2875
|
|
|
2876
|
+
if self._tunnel:
|
|
2877
|
+
await self._tunnel.wait_closed()
|
|
2878
|
+
|
|
2854
2879
|
await self._close_event.wait()
|
|
2855
2880
|
|
|
2856
2881
|
def disconnect(self, code: int, reason: str,
|
|
@@ -7277,6 +7302,7 @@ class SSHConnectionOptions(Options, Generic[_Options]):
|
|
|
7277
7302
|
waiter: Optional[asyncio.Future]
|
|
7278
7303
|
protocol_factory: _ProtocolFactory
|
|
7279
7304
|
version: bytes
|
|
7305
|
+
orig_host: str
|
|
7280
7306
|
host: str
|
|
7281
7307
|
port: int
|
|
7282
7308
|
tunnel: object
|
|
@@ -7369,6 +7395,7 @@ class SSHConnectionOptions(Options, Generic[_Options]):
|
|
|
7369
7395
|
self.protocol_factory = protocol_factory
|
|
7370
7396
|
self.version = _validate_version(version)
|
|
7371
7397
|
|
|
7398
|
+
self.orig_host = host
|
|
7372
7399
|
self.host = cast(str, config.get('Hostname', host))
|
|
7373
7400
|
self.port = cast(int, port if port != () else
|
|
7374
7401
|
config.get('Port', DEFAULT_PORT))
|
|
@@ -9326,7 +9353,8 @@ async def listen(host = '', port: DefTuple[int] = (), *,
|
|
|
9326
9353
|
tunnel: DefTuple[_TunnelListener] = (),
|
|
9327
9354
|
family: DefTuple[int] = (), flags:int = socket.AI_PASSIVE,
|
|
9328
9355
|
backlog: int = 100, sock: Optional[socket.socket] = None,
|
|
9329
|
-
reuse_address: bool =
|
|
9356
|
+
reuse_address: Optional[bool] = None,
|
|
9357
|
+
reuse_port: Optional[bool] = None,
|
|
9330
9358
|
acceptor: _AcceptHandler = None,
|
|
9331
9359
|
error_handler: _ErrorHandler = None,
|
|
9332
9360
|
config: DefTuple[ConfigPaths] = (),
|
|
@@ -9384,7 +9412,7 @@ async def listen(host = '', port: DefTuple[int] = (), *,
|
|
|
9384
9412
|
port other existing sockets are bound to, so long as they all
|
|
9385
9413
|
set this flag when being created. If not specified, the
|
|
9386
9414
|
default is to not allow this. This option is not supported
|
|
9387
|
-
on Windows
|
|
9415
|
+
on Windows.
|
|
9388
9416
|
:param acceptor: (optional)
|
|
9389
9417
|
A `callable` or coroutine which will be called when the
|
|
9390
9418
|
SSH handshake completes on an accepted connection, taking
|
|
@@ -9414,8 +9442,8 @@ async def listen(host = '', port: DefTuple[int] = (), *,
|
|
|
9414
9442
|
:type flags: flags to pass to :meth:`getaddrinfo() <socket.getaddrinfo>`
|
|
9415
9443
|
:type backlog: `int`
|
|
9416
9444
|
:type sock: :class:`socket.socket` or `None`
|
|
9417
|
-
:type reuse_address: `bool`
|
|
9418
|
-
:type reuse_port: `bool`
|
|
9445
|
+
:type reuse_address: `bool` or `None`
|
|
9446
|
+
:type reuse_port: `bool` or `None`
|
|
9419
9447
|
:type acceptor: `callable` or coroutine
|
|
9420
9448
|
:type error_handler: `callable`
|
|
9421
9449
|
:type config: `list` of `str`
|
|
@@ -9451,7 +9479,8 @@ async def listen_reverse(host = '', port: DefTuple[int] = (), *,
|
|
|
9451
9479
|
family: DefTuple[int] = (),
|
|
9452
9480
|
flags: int = socket.AI_PASSIVE, backlog: int = 100,
|
|
9453
9481
|
sock: Optional[socket.socket] = None,
|
|
9454
|
-
reuse_address: bool =
|
|
9482
|
+
reuse_address: Optional[bool] = None,
|
|
9483
|
+
reuse_port: Optional[bool] = None,
|
|
9455
9484
|
acceptor: _AcceptHandler = None,
|
|
9456
9485
|
error_handler: _ErrorHandler = None,
|
|
9457
9486
|
config: DefTuple[ConfigPaths] = (),
|
|
@@ -9518,7 +9547,7 @@ async def listen_reverse(host = '', port: DefTuple[int] = (), *,
|
|
|
9518
9547
|
port other existing sockets are bound to, so long as they all
|
|
9519
9548
|
set this flag when being created. If not specified, the
|
|
9520
9549
|
default is to not allow this. This option is not supported
|
|
9521
|
-
on Windows
|
|
9550
|
+
on Windows.
|
|
9522
9551
|
:param acceptor: (optional)
|
|
9523
9552
|
A `callable` or coroutine which will be called when the
|
|
9524
9553
|
SSH handshake completes on an accepted connection, taking
|
|
@@ -9553,8 +9582,8 @@ async def listen_reverse(host = '', port: DefTuple[int] = (), *,
|
|
|
9553
9582
|
:type flags: flags to pass to :meth:`getaddrinfo() <socket.getaddrinfo>`
|
|
9554
9583
|
:type backlog: `int`
|
|
9555
9584
|
:type sock: :class:`socket.socket` or `None`
|
|
9556
|
-
:type reuse_address: `bool`
|
|
9557
|
-
:type reuse_port: `bool`
|
|
9585
|
+
:type reuse_address: `bool` or `None`
|
|
9586
|
+
:type reuse_port: `bool` or `None`
|
|
9558
9587
|
:type acceptor: `callable` or coroutine
|
|
9559
9588
|
:type error_handler: `callable`
|
|
9560
9589
|
:type config: `list` of `str`
|
|
@@ -58,10 +58,12 @@ class PQDH:
|
|
|
58
58
|
self.pubkey_bytes, self.privkey_bytes, \
|
|
59
59
|
self.ciphertext_bytes, self.secret_bytes, \
|
|
60
60
|
oqs_name = _pq_algs[alg_name]
|
|
61
|
-
except KeyError:
|
|
62
|
-
raise ValueError(
|
|
61
|
+
except KeyError:
|
|
62
|
+
raise ValueError('Unknown PQ algorithm ' +
|
|
63
|
+
alg_name.decode()) from None
|
|
63
64
|
|
|
64
|
-
if not hasattr(_oqs, 'OQS_' + oqs_name +
|
|
65
|
+
if not hasattr(_oqs, 'OQS_' + oqs_name + # pragma: no cover
|
|
66
|
+
'_keypair'):
|
|
65
67
|
oqs_name += '_ipd'
|
|
66
68
|
|
|
67
69
|
self._keypair = getattr(_oqs, 'OQS_' + oqs_name + '_keypair')
|
|
@@ -46,6 +46,12 @@ class Encryption:
|
|
|
46
46
|
|
|
47
47
|
raise NotImplementedError
|
|
48
48
|
|
|
49
|
+
@classmethod
|
|
50
|
+
def needs_mac(cls) -> bool:
|
|
51
|
+
"""Return whether a MAC algorithm is needed for this encryption"""
|
|
52
|
+
|
|
53
|
+
return True
|
|
54
|
+
|
|
49
55
|
@classmethod
|
|
50
56
|
def get_mac_params(cls, mac_alg: bytes) -> Tuple[int, int, bool]:
|
|
51
57
|
"""Get parameters of the MAC algorithm used with this encryption"""
|
|
@@ -161,6 +167,12 @@ class GCMEncryption(Encryption):
|
|
|
161
167
|
|
|
162
168
|
return cls(GCMCipher(cipher_name, key, iv))
|
|
163
169
|
|
|
170
|
+
@classmethod
|
|
171
|
+
def needs_mac(cls) -> bool:
|
|
172
|
+
"""GCM encryption doesn't need an external MAC algorithm"""
|
|
173
|
+
|
|
174
|
+
return False
|
|
175
|
+
|
|
164
176
|
@classmethod
|
|
165
177
|
def get_mac_params(cls, mac_alg: bytes) -> Tuple[int, int, bool]:
|
|
166
178
|
"""Get parameters of the MAC algorithm used with this encryption"""
|
|
@@ -200,6 +212,12 @@ class ChachaEncryption(Encryption):
|
|
|
200
212
|
|
|
201
213
|
return cls(ChachaCipher(key))
|
|
202
214
|
|
|
215
|
+
@classmethod
|
|
216
|
+
def needs_mac(cls) -> bool:
|
|
217
|
+
"""Chacha20 encryption doesn't need an external MAC algorithm"""
|
|
218
|
+
|
|
219
|
+
return False
|
|
220
|
+
|
|
203
221
|
@classmethod
|
|
204
222
|
def get_mac_params(cls, mac_alg: bytes) -> Tuple[int, int, bool]:
|
|
205
223
|
"""Get parameters of the MAC algorithm used with this encryption"""
|
|
@@ -258,8 +276,14 @@ def get_default_encryption_algs() -> List[bytes]:
|
|
|
258
276
|
return _default_enc_algs
|
|
259
277
|
|
|
260
278
|
|
|
261
|
-
def
|
|
262
|
-
|
|
279
|
+
def encryption_needs_mac(enc_alg: bytes) -> bool:
|
|
280
|
+
"""Return whether an encryption algorithm needs a MAC algorithm"""
|
|
281
|
+
|
|
282
|
+
encryption, _ = _enc_params[enc_alg]
|
|
283
|
+
return encryption.needs_mac()
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def get_encryption_params(enc_alg: bytes, mac_alg: bytes = b'') -> _EncParams:
|
|
263
287
|
"""Get parameters of an encryption and MAC algorithm"""
|
|
264
288
|
|
|
265
289
|
encryption, cipher_name = _enc_params[enc_alg]
|
|
@@ -136,6 +136,10 @@ def _parse_cd_args(args: bytes) -> Tuple[int, int, bytes]:
|
|
|
136
136
|
|
|
137
137
|
try:
|
|
138
138
|
permissions, size, name = args.split(None, 2)
|
|
139
|
+
|
|
140
|
+
if b'/' in name or b'\\' in name or name == b'..':
|
|
141
|
+
raise _scp_error(SFTPBadMessage, 'Invalid filename')
|
|
142
|
+
|
|
139
143
|
return int(permissions, 8), int(size), name
|
|
140
144
|
except ValueError:
|
|
141
145
|
raise _scp_error(SFTPBadMessage,
|
|
@@ -346,7 +350,8 @@ class _SCPHandler:
|
|
|
346
350
|
|
|
347
351
|
if isinstance(exc, SFTPError):
|
|
348
352
|
reason = exc.reason.encode('utf-8')
|
|
349
|
-
elif isinstance(exc, OSError)
|
|
353
|
+
elif isinstance(exc, OSError) and \
|
|
354
|
+
exc.strerror: # pragma: no branch (win32)
|
|
350
355
|
reason = exc.strerror.encode('utf-8')
|
|
351
356
|
|
|
352
357
|
filename = cast(BytesOrStr, exc.filename)
|
|
@@ -854,6 +854,10 @@ class SSHServer:
|
|
|
854
854
|
* An :class:`SSHListener` object
|
|
855
855
|
* `True` to set up standard port forwarding
|
|
856
856
|
* `False` to reject the request
|
|
857
|
+
* A callable to use as an accept handler, taking
|
|
858
|
+
arguments of the original host and port of the
|
|
859
|
+
client and returning a boolean to indicate
|
|
860
|
+
whether or not to allow connection forwarding.
|
|
857
861
|
* A coroutine object which returns one of the above
|
|
858
862
|
|
|
859
863
|
"""
|