Skip to content

Commit

Permalink
python: Replace pyOpenSSL with ssl.
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reported-at: https://bugzilla.redhat.com/1988429
Signed-off-by: Timothy Redaelli <[email protected]>
Acked-by: Terry Wilson <[email protected]>
Tested-by: Terry Wilson <[email protected]>
Signed-off-by: Ilya Maximets <[email protected]>
  • Loading branch information
drizzt authored and igsilya committed Nov 3, 2021
1 parent 3f550fa commit 68543dd
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .ci/linux-prepare.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ make -j4 HAVE_LLVM= HAVE_SQLITE= install
cd ..

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'

if [ "$M32" ]; then
Expand Down
2 changes: 1 addition & 1 deletion .cirrus.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ freebsd_build_task:

env:
DEPENDENCIES: automake libtool gmake gcc wget openssl python3
PY_DEPS: sphinx|openssl
PY_DEPS: sphinx
matrix:
COMPILER: gcc
COMPILER: clang
Expand Down
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ addons:
- libjemalloc-dev
- libnuma-dev
- libpcap-dev
- python3-openssl
- python3-pip
- python3-sphinx
- libelf-dev
Expand Down
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Post-v2.16.0
limiting behavior.
* Add hardware offload support for matching IPv4/IPv6 frag types
(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
Expand Down
6 changes: 3 additions & 3 deletions python/ovs/poller.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
import ovs.winutils as winutils

try:
from OpenSSL import SSL
import ssl
except ImportError:
SSL = None
ssl = None

try:
from eventlet import patcher as eventlet_patcher
Expand Down Expand Up @@ -73,7 +73,7 @@ def __init__(self):
def register(self, fd, events):
if isinstance(fd, socket.socket):
fd = fd.fileno()
if SSL and isinstance(fd, SSL.Connection):
if ssl and isinstance(fd, ssl.SSLSocket):
fd = fd.fileno()

if sys.platform != 'win32':
Expand Down
91 changes: 54 additions & 37 deletions python/ovs/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
import ovs.vlog

try:
from OpenSSL import SSL
import ssl
except ImportError:
SSL = None
ssl = None

if sys.platform == 'win32':
import ovs.winutils as winutils
Expand Down Expand Up @@ -322,6 +322,12 @@ def recv(self, n):
The recv function will not block waiting for data to arrive. If no
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()
if retval != 0:
return (retval, "")
Expand All @@ -331,10 +337,7 @@ def recv(self, n):
if sys.platform == 'win32' and self.socket is None:
return self.__recv_windows(n)

try:
return (0, self.socket.recv(n))
except socket.error as e:
return (ovs.socket_util.get_exception_errno(e), "")
return (0, self.socket.recv(n))

def __recv_windows(self, n):
if self._read_pending:
Expand Down Expand Up @@ -396,6 +399,12 @@ def send(self, buf):
Will not block. If no bytes can be immediately accepted for
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()
if retval != 0:
return -retval
Expand All @@ -409,10 +418,7 @@ def send(self, buf):
if sys.platform == 'win32' and self.socket is None:
return self.__send_windows(buf)

try:
return self.socket.send(buf)
except socket.error as e:
return -ovs.socket_util.get_exception_errno(e)
return self.socket.send(buf)

def __send_windows(self, buf):
if self._write_pending:
Expand Down Expand Up @@ -769,35 +775,42 @@ class SSLStream(Stream):
def check_connection_completion(sock):
try:
return Stream.check_connection_completion(sock)
except SSL.SysCallError as e:
except ssl.SSLSyscallError as e:
return ovs.socket_util.get_exception_errno(e)

@staticmethod
def needs_probes():
return True

@staticmethod
def verify_cb(conn, cert, errnum, depth, ok):
return ok

@staticmethod
def _open(suffix, dscp):
error, sock = TCPStream._open(suffix, dscp)
if error:
return error, None
address = ovs.socket_util.inet_parse_active(suffix, 0)
family, sock = ovs.socket_util.inet_create_socket_active(
socket.SOCK_STREAM, address)
if sock is None:
return family, sock

# Create an SSL context
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.set_verify(SSL.VERIFY_PEER, SSLStream.verify_cb)
ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.options |= ssl.OP_NO_SSLv2
ctx.options |= ssl.OP_NO_SSLv3
# If the client has not set the SSL configuration files
# 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_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)
ssl_sock.set_connect_state()
# Connect
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

def connect(self):
Expand All @@ -809,40 +822,44 @@ def connect(self):
# TCP Connection is successful. Now do the SSL handshake
try:
self.socket.do_handshake()
except SSL.WantReadError:
except ssl.SSLWantReadError:
return errno.EAGAIN
except SSL.SysCallError as e:
except ssl.SSLSyscallError as e:
return ovs.socket_util.get_exception_errno(e)

return 0

def recv(self, n):
try:
return super(SSLStream, self).recv(n)
except SSL.WantReadError:
return super(SSLStream, self)._recv(n)
except ssl.SSLWantReadError:
return (errno.EAGAIN, "")
except SSL.SysCallError as e:
except ssl.SSLSyscallError as e:
return (ovs.socket_util.get_exception_errno(e), "")
except SSL.ZeroReturnError:
except ssl.SSLZeroReturnError:
return (0, "")
except socket.error as e:
return (ovs.socket_util.get_exception_errno(e), "")

def send(self, buf):
try:
return super(SSLStream, self).send(buf)
except SSL.WantWriteError:
return super(SSLStream, self)._send(buf)
except ssl.SSLWantWriteError:
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)

def close(self):
if self.socket:
try:
self.socket.shutdown()
except SSL.Error:
self.socket.shutdown(socket.SHUT_RDWR)
except socket.error:
pass
return super(SSLStream, self).close()


if SSL:
if ssl:
# Register SSL only if the OpenSSL module is available
Stream.register_method("ssl", SSLStream)
2 changes: 1 addition & 1 deletion tests/ovsdb-idl.at
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ m4_define([OVSDB_CHECK_IDL_TCP6_MULTIPLE_REMOTES_PY],
m4_define([OVSDB_CHECK_IDL_SSL_PY],
[AT_SETUP([$1 - Python3 - SSL])
AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
$PYTHON3 -c "import OpenSSL.SSL"
$PYTHON3 -c "import ssl"
SSL_PRESENT=$?
AT_SKIP_IF([test $SSL_PRESENT != 0])
AT_KEYWORDS([ovsdb server idl positive Python with ssl socket $5])
Expand Down

0 comments on commit 68543dd

Please sign in to comment.