2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 06:15:47 +00:00

python: Replace pyOpenSSL with ssl.

Currently, pyOpenSSL is half-deprecated upstream and so it's removed on
some distributions (for example on CentOS Stream 9,
https://issues.redhat.com/browse/CS-336), but since OVS only
supports Python 3 it's possible to replace pyOpenSSL with "import ssl"
included in base Python 3.

Stream recv and send had to be splitted as _recv and _send, since SSLError
is a subclass of socket.error and so it was not possible to except for
SSLWantReadError and SSLWantWriteError in recv and send of SSLStream.

TCPstream._open cannot be used in SSLStream, since Python ssl module
requires the SSL socket to be created before connecting it, so
SSLStream._open needs to create the socket, create SSL socket and then
connect the SSL socket.

Reported-by: Timothy Redaelli <tredaelli@redhat.com>
Reported-at: https://bugzilla.redhat.com/1988429
Signed-off-by: Timothy Redaelli <tredaelli@redhat.com>
Acked-by: Terry Wilson <twilson@redhat.com>
Tested-by: Terry Wilson <twilson@redhat.com>
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
Timothy Redaelli
2021-10-30 01:12:14 +02:00
committed by Ilya Maximets
parent 3f550fa538
commit 68543dd523
7 changed files with 63 additions and 44 deletions

View File

@@ -21,7 +21,7 @@ make -j4 HAVE_LLVM= HAVE_SQLITE= install
cd .. cd ..
pip3 install --disable-pip-version-check --user \ pip3 install --disable-pip-version-check --user \
flake8 hacking sphinx pyOpenSSL wheel setuptools flake8 hacking sphinx wheel setuptools
pip3 install --user 'meson==0.47.1' pip3 install --user 'meson==0.47.1'
if [ "$M32" ]; then if [ "$M32" ]; then

View File

@@ -9,7 +9,7 @@ freebsd_build_task:
env: env:
DEPENDENCIES: automake libtool gmake gcc wget openssl python3 DEPENDENCIES: automake libtool gmake gcc wget openssl python3
PY_DEPS: sphinx|openssl PY_DEPS: sphinx
matrix: matrix:
COMPILER: gcc COMPILER: gcc
COMPILER: clang COMPILER: clang

View File

@@ -17,7 +17,6 @@ addons:
- libjemalloc-dev - libjemalloc-dev
- libnuma-dev - libnuma-dev
- libpcap-dev - libpcap-dev
- python3-openssl
- python3-pip - python3-pip
- python3-sphinx - python3-sphinx
- libelf-dev - libelf-dev

3
NEWS
View File

@@ -10,6 +10,9 @@ Post-v2.16.0
limiting behavior. limiting behavior.
* Add hardware offload support for matching IPv4/IPv6 frag types * Add hardware offload support for matching IPv4/IPv6 frag types
(experimental). (experimental).
- Python:
* For SSL support, the use of the pyOpenSSL library has been replaced
with the native 'ssl' module.
v2.16.0 - 16 Aug 2021 v2.16.0 - 16 Aug 2021

View File

@@ -26,9 +26,9 @@ if sys.platform == "win32":
import ovs.winutils as winutils import ovs.winutils as winutils
try: try:
from OpenSSL import SSL import ssl
except ImportError: except ImportError:
SSL = None ssl = None
try: try:
from eventlet import patcher as eventlet_patcher from eventlet import patcher as eventlet_patcher
@@ -73,7 +73,7 @@ class _SelectSelect(object):
def register(self, fd, events): def register(self, fd, events):
if isinstance(fd, socket.socket): if isinstance(fd, socket.socket):
fd = fd.fileno() fd = fd.fileno()
if SSL and isinstance(fd, SSL.Connection): if ssl and isinstance(fd, ssl.SSLSocket):
fd = fd.fileno() fd = fd.fileno()
if sys.platform != 'win32': if sys.platform != 'win32':

View File

@@ -22,9 +22,9 @@ import ovs.socket_util
import ovs.vlog import ovs.vlog
try: try:
from OpenSSL import SSL import ssl
except ImportError: except ImportError:
SSL = None ssl = None
if sys.platform == 'win32': if sys.platform == 'win32':
import ovs.winutils as winutils import ovs.winutils as winutils
@@ -322,6 +322,12 @@ class Stream(object):
The recv function will not block waiting for data to arrive. If no The recv function will not block waiting for data to arrive. If no
data have been received, it returns (errno.EAGAIN, "") immediately.""" data have been received, it returns (errno.EAGAIN, "") immediately."""
try:
return self._recv(n)
except socket.error as e:
return (ovs.socket_util.get_exception_errno(e), "")
def _recv(self, n):
retval = self.connect() retval = self.connect()
if retval != 0: if retval != 0:
return (retval, "") return (retval, "")
@@ -331,10 +337,7 @@ class Stream(object):
if sys.platform == 'win32' and self.socket is None: if sys.platform == 'win32' and self.socket is None:
return self.__recv_windows(n) return self.__recv_windows(n)
try: return (0, self.socket.recv(n))
return (0, self.socket.recv(n))
except socket.error as e:
return (ovs.socket_util.get_exception_errno(e), "")
def __recv_windows(self, n): def __recv_windows(self, n):
if self._read_pending: if self._read_pending:
@@ -396,6 +399,12 @@ class Stream(object):
Will not block. If no bytes can be immediately accepted for Will not block. If no bytes can be immediately accepted for
transmission, returns -errno.EAGAIN immediately.""" transmission, returns -errno.EAGAIN immediately."""
try:
return self._send(buf)
except socket.error as e:
return -ovs.socket_util.get_exception_errno(e)
def _send(self, buf):
retval = self.connect() retval = self.connect()
if retval != 0: if retval != 0:
return -retval return -retval
@@ -409,10 +418,7 @@ class Stream(object):
if sys.platform == 'win32' and self.socket is None: if sys.platform == 'win32' and self.socket is None:
return self.__send_windows(buf) return self.__send_windows(buf)
try: return self.socket.send(buf)
return self.socket.send(buf)
except socket.error as e:
return -ovs.socket_util.get_exception_errno(e)
def __send_windows(self, buf): def __send_windows(self, buf):
if self._write_pending: if self._write_pending:
@@ -769,35 +775,42 @@ class SSLStream(Stream):
def check_connection_completion(sock): def check_connection_completion(sock):
try: try:
return Stream.check_connection_completion(sock) return Stream.check_connection_completion(sock)
except SSL.SysCallError as e: except ssl.SSLSyscallError as e:
return ovs.socket_util.get_exception_errno(e) return ovs.socket_util.get_exception_errno(e)
@staticmethod @staticmethod
def needs_probes(): def needs_probes():
return True return True
@staticmethod
def verify_cb(conn, cert, errnum, depth, ok):
return ok
@staticmethod @staticmethod
def _open(suffix, dscp): def _open(suffix, dscp):
error, sock = TCPStream._open(suffix, dscp) address = ovs.socket_util.inet_parse_active(suffix, 0)
if error: family, sock = ovs.socket_util.inet_create_socket_active(
return error, None socket.SOCK_STREAM, address)
if sock is None:
return family, sock
# Create an SSL context # Create an SSL context
ctx = SSL.Context(SSL.SSLv23_METHOD) ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.set_verify(SSL.VERIFY_PEER, SSLStream.verify_cb) ctx.verify_mode = ssl.CERT_REQUIRED
ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3) ctx.options |= ssl.OP_NO_SSLv2
ctx.options |= ssl.OP_NO_SSLv3
# If the client has not set the SSL configuration files # If the client has not set the SSL configuration files
# exception would be raised. # exception would be raised.
ctx.use_privatekey_file(Stream._SSL_private_key_file)
ctx.use_certificate_file(Stream._SSL_certificate_file)
ctx.load_verify_locations(Stream._SSL_ca_cert_file) ctx.load_verify_locations(Stream._SSL_ca_cert_file)
ctx.load_cert_chain(Stream._SSL_certificate_file,
Stream._SSL_private_key_file)
ssl_sock = ctx.wrap_socket(sock, do_handshake_on_connect=False)
ssl_sock = SSL.Connection(ctx, sock) # Connect
ssl_sock.set_connect_state() error = ovs.socket_util.inet_connect_active(ssl_sock, address, family,
dscp)
if not error:
try:
ssl_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
except socket.error as e:
ssl_sock.close()
return ovs.socket_util.get_exception_errno(e), None
return error, ssl_sock return error, ssl_sock
def connect(self): def connect(self):
@@ -809,40 +822,44 @@ class SSLStream(Stream):
# TCP Connection is successful. Now do the SSL handshake # TCP Connection is successful. Now do the SSL handshake
try: try:
self.socket.do_handshake() self.socket.do_handshake()
except SSL.WantReadError: except ssl.SSLWantReadError:
return errno.EAGAIN return errno.EAGAIN
except SSL.SysCallError as e: except ssl.SSLSyscallError as e:
return ovs.socket_util.get_exception_errno(e) return ovs.socket_util.get_exception_errno(e)
return 0 return 0
def recv(self, n): def recv(self, n):
try: try:
return super(SSLStream, self).recv(n) return super(SSLStream, self)._recv(n)
except SSL.WantReadError: except ssl.SSLWantReadError:
return (errno.EAGAIN, "") return (errno.EAGAIN, "")
except SSL.SysCallError as e: except ssl.SSLSyscallError as e:
return (ovs.socket_util.get_exception_errno(e), "") return (ovs.socket_util.get_exception_errno(e), "")
except SSL.ZeroReturnError: except ssl.SSLZeroReturnError:
return (0, "") return (0, "")
except socket.error as e:
return (ovs.socket_util.get_exception_errno(e), "")
def send(self, buf): def send(self, buf):
try: try:
return super(SSLStream, self).send(buf) return super(SSLStream, self)._send(buf)
except SSL.WantWriteError: except ssl.SSLWantWriteError:
return -errno.EAGAIN return -errno.EAGAIN
except SSL.SysCallError as e: except ssl.SSLSyscallError as e:
return -ovs.socket_util.get_exception_errno(e)
except socket.error as e:
return -ovs.socket_util.get_exception_errno(e) return -ovs.socket_util.get_exception_errno(e)
def close(self): def close(self):
if self.socket: if self.socket:
try: try:
self.socket.shutdown() self.socket.shutdown(socket.SHUT_RDWR)
except SSL.Error: except socket.error:
pass pass
return super(SSLStream, self).close() return super(SSLStream, self).close()
if SSL: if ssl:
# Register SSL only if the OpenSSL module is available # Register SSL only if the OpenSSL module is available
Stream.register_method("ssl", SSLStream) Stream.register_method("ssl", SSLStream)

View File

@@ -225,7 +225,7 @@ m4_define([OVSDB_CHECK_IDL_TCP6_MULTIPLE_REMOTES_PY],
m4_define([OVSDB_CHECK_IDL_SSL_PY], m4_define([OVSDB_CHECK_IDL_SSL_PY],
[AT_SETUP([$1 - Python3 - SSL]) [AT_SETUP([$1 - Python3 - SSL])
AT_SKIP_IF([test "$HAVE_OPENSSL" = no]) AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
$PYTHON3 -c "import OpenSSL.SSL" $PYTHON3 -c "import ssl"
SSL_PRESENT=$? SSL_PRESENT=$?
AT_SKIP_IF([test $SSL_PRESENT != 0]) AT_SKIP_IF([test $SSL_PRESENT != 0])
AT_KEYWORDS([ovsdb server idl positive Python with ssl socket $5]) AT_KEYWORDS([ovsdb server idl positive Python with ssl socket $5])