From: Numan Siddique <nusid...@redhat.com> SSL support is added to the ovs/stream.py. pyOpenSSL library is used to support SSL. If this library is not present, then the SSL stream is not registered with the Stream class.
Signed-off-by: Numan Siddique <nusid...@redhat.com> --- python/ovs/poller.py | 8 +++++ python/ovs/stream.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++-- tests/ovsdb-idl.at | 30 ++++++++++++++++-- tests/test-ovsdb.py | 7 +++++ 4 files changed, 127 insertions(+), 5 deletions(-) diff --git a/python/ovs/poller.py b/python/ovs/poller.py index de6bf22..d7cb7d3 100644 --- a/python/ovs/poller.py +++ b/python/ovs/poller.py @@ -20,6 +20,11 @@ import socket import os try: + from OpenSSL import SSL +except ImportError: + SSL = None + +try: import eventlet.patcher def _using_eventlet_green_select(): @@ -54,6 +59,9 @@ class _SelectSelect(object): def register(self, fd, events): if isinstance(fd, socket.socket): fd = fd.fileno() + if SSL and isinstance(fd, SSL.Connection): + fd = fd.fileno() + assert isinstance(fd, int) if events & POLLIN: self.rlist.append(fd) diff --git a/python/ovs/stream.py b/python/ovs/stream.py index 97b22ac..bb813a6 100644 --- a/python/ovs/stream.py +++ b/python/ovs/stream.py @@ -22,6 +22,11 @@ import ovs.poller import ovs.socket_util import ovs.vlog +try: + from OpenSSL import SSL +except ImportError: + SSL = None + vlog = ovs.vlog.Vlog("stream") @@ -39,7 +44,7 @@ def stream_or_pstream_needs_probes(name): class Stream(object): - """Bidirectional byte stream. Currently only Unix domain sockets + """Bidirectional byte stream. Unix domain sockets, tcp and ssl are implemented.""" # States. @@ -54,6 +59,10 @@ class Stream(object): _SOCKET_METHODS = {} + _SSL_private_key_file = None + _SSL_certificate_file = None + _SSL_ca_cert_file = None + @staticmethod def register_method(method, cls): Stream._SOCKET_METHODS[method + ":"] = cls @@ -68,7 +77,7 @@ class Stream(object): @staticmethod def is_valid_name(name): """Returns True if 'name' is a stream name in the form "TYPE:ARGS" and - TYPE is a supported stream type (currently only "unix:" and "tcp:"), + TYPE is a supported stream type ("unix:", "tcp:" and "ssl:"), otherwise False.""" return bool(Stream._find_method(name)) @@ -116,7 +125,7 @@ class Stream(object): return error, None else: status = ovs.socket_util.check_connection_completion(sock) - return 0, Stream(sock, name, status) + return 0, cls(sock, name, status) @staticmethod def _open(suffix, dscp): @@ -264,6 +273,18 @@ class Stream(object): # Don't delete the file: we might have forked. self.socket.close() + @staticmethod + def ssl_set_private_key_file(file_name): + Stream._SSL_private_key_file = file_name + + @staticmethod + def ssl_set_certificate_file(file_name): + Stream._SSL_certificate_file = file_name + + @staticmethod + def ssl_set_ca_cert_file(file_name): + Stream._SSL_ca_cert_file = file_name + class PassiveStream(object): @staticmethod @@ -362,6 +383,7 @@ def usage(name): Active %s connection methods: unix:FILE Unix domain socket named FILE tcp:IP:PORT TCP socket to IP with port no of PORT + ssl:IP:PORT SSL socket to IP with port no of PORT Passive %s connection methods: punix:FILE Listen on Unix domain socket FILE""" % (name, name) @@ -385,3 +407,62 @@ class TCPStream(Stream): sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) return error, sock Stream.register_method("tcp", TCPStream) + + +class SSLStream(Stream): + + @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 + + # 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.set_session_cache_mode(SSL.SESS_CACHE_OFF) + # 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) + + ssl_sock = SSL.Connection(ctx, sock) + ssl_sock.set_connect_state() + return error, ssl_sock + + def connect(self): + retval = super(SSLStream, self).connect() + + if retval: + return retval + + # TCP Connection is successful. Now do the SSL handshake + try: + self.socket.do_handshake() + except SSL.WantReadError: + return errno.EAGAIN + + return 0 + + def recv(self, n): + try: + return super(SSLStream, self).recv(n) + except SSL.WantReadError: + return (errno.EAGAIN, "") + + def send(self, buf): + try: + return super(SSLStream, self).send(buf) + except SSL.WantWriteError: + return errno.EAGAIN + + +if SSL: + # Register SSL only if the OpenSSL module is available + Stream.register_method("ssl", SSLStream) diff --git a/tests/ovsdb-idl.at b/tests/ovsdb-idl.at index d633dbb..e57a3a4 100644 --- a/tests/ovsdb-idl.at +++ b/tests/ovsdb-idl.at @@ -1198,10 +1198,36 @@ m4_define([OVSDB_CHECK_IDL_NOTIFY_PY], OVSDB_SERVER_SHUTDOWN AT_CLEANUP]) +# This test uses the Python IDL implementation with ssl +m4_define([OVSDB_CHECK_IDL_NOTIFY_SSL_PY], + [AT_SETUP([$1 - SSL]) + AT_SKIP_IF([test $HAVE_PYTHON = no]) + $PYTHON -m OpenSSL.SSL + SSL_PRESENT=$? + AT_SKIP_IF([test $SSL_PRESENT != 0]) + AT_KEYWORDS([ovsdb server idl Python notify - ssl socket]) + AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema], + [0], [stdout], [ignore]) + PKIDIR=$abs_top_builddir/tests + AT_CHECK([ovsdb-server --log-file '-vPATTERN:console:ovsdb-server|%c|%m' \ + --detach --no-chdir --pidfile="`pwd`"/pid \ + --private-key=$PKIDIR/testpki-privkey2.pem \ + --certificate=$PKIDIR/testpki-cert2.pem \ + --ca-cert=$PKIDIR/testpki-cacert.pem \ + --remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore]) + PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT]) + AT_CHECK([$PYTHON $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema \ + ssl:127.0.0.1:$TCP_PORT $PKIDIR/testpki-privkey.pem \ + $PKIDIR/testpki-cert.pem $PKIDIR/testpki-cacert.pem $2], + [0], [stdout], [ignore], [kill `cat pid`]) + AT_CHECK([sort stdout | ${PERL} $srcdir/uuidfilt.pl]m4_if([$5],,, [[| $5]]), + [0], [$3], [], [kill `cat pid`]) + OVSDB_SERVER_SHUTDOWN + AT_CLEANUP]) m4_define([OVSDB_CHECK_IDL_NOTIFY], - [OVSDB_CHECK_IDL_NOTIFY_PY($@)]) - + [OVSDB_CHECK_IDL_NOTIFY_PY($@) + OVSDB_CHECK_IDL_NOTIFY_SSL_PY($@)]) OVSDB_CHECK_IDL_NOTIFY([simple idl verify notify], [['track-notify' \ diff --git a/tests/test-ovsdb.py b/tests/test-ovsdb.py index e1cfdad..b27ad28 100644 --- a/tests/test-ovsdb.py +++ b/tests/test-ovsdb.py @@ -27,6 +27,7 @@ from ovs.db import data import ovs.db.types import ovs.ovsuuid import ovs.poller +import ovs.stream import ovs.util from ovs.fatal_signal import signal_alarm import six @@ -519,6 +520,12 @@ def do_idl(schema_file, remote, *commands): schema_helper = ovs.db.idl.SchemaHelper(schema_file) track_notify = False + if remote.startswith("ssl:"): + ovs.stream.Stream.ssl_set_private_key_file(commands[0]) + ovs.stream.Stream.ssl_set_certificate_file(commands[1]) + ovs.stream.Stream.ssl_set_ca_cert_file(commands[2]) + commands = commands[3:] + if commands and commands[0] == "track-notify": commands = commands[1:] track_notify = True -- 2.7.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev