Thanks Ben for the feature update.  Much appreciated.

Regards

/Shivaram

On Wed, Mar 20, 2019 at 5:39 PM Ben Pfaff <b...@ovn.org> wrote:

> This TLS extension, introduced in RFC 3546, allows the server to know what
> host the client believes it is contacting, the TLS equivalent of the Host:
> header in HTTP.
>
> Requested-by: Shivaram Mysore <smys...@servicefractal.com>
> Signed-off-by: Ben Pfaff <b...@ovn.org>
> ---
>  NEWS               |  2 ++
>  lib/stream-ssl.c   | 63 ++++++++++++++++++++++++++++++++++++++++++----
>  m4/openvswitch.m4  | 23 ++++++++++++++---
>  tests/atlocal.in   |  2 ++
>  tests/ovs-vsctl.at | 20 +++++++++++++++
>  5 files changed, 102 insertions(+), 8 deletions(-)
>
> diff --git a/NEWS b/NEWS
> index 1e4744dbd244..0f8d02817e61 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -25,6 +25,8 @@ Post-v2.11.0
>     - OVN:
>       * Select IPAM mac_prefix in a random manner if not provided by the
> user
>     - New QoS type "linux-netem" on Linux.
> +   - Added support for TLS Server Name Indication (SNI).
> +
>
>  v2.11.0 - 19 Feb 2019
>  ---------------------
> diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c
> index fed71801b823..81f2409965b2 100644
> --- a/lib/stream-ssl.c
> +++ b/lib/stream-ssl.c
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
> Nicira, Inc.
> + * Copyright (c) 2008-2016, 2019 Nicira, Inc.
>   *
>   * Licensed under the Apache License, Version 2.0 (the "License");
>   * you may not use this file except in compliance with the License.
> @@ -220,9 +220,19 @@ want_to_poll_events(int want)
>      }
>  }
>
> -/* Takes ownership of 'name'. */
> +/* Creates a new SSL connection based on socket 'fd', as either a client
> or a
> + * server according to 'type', initially in 'state'.  On success, returns
> 0 and
> + * stores the new stream in '*streamp', otherwise returns an errno value
> and
> + * doesn't bother with '*streamp'.
> + *
> + * Takes ownership of 'name', which should be the name of the connection
> in the
> + * format that would be used to connect to it, e.g. "ssl:1.2.3.4:5".
> + *
> + * For client connections, 'server_name' should be the host name of the
> server
> + * being connected to, for use with SSL SNI (server name indication).
> Takes
> + * ownership of 'server_name'. */
>  static int
> -new_ssl_stream(char *name, int fd, enum session_type type,
> +new_ssl_stream(char *name, char *server_name, int fd, enum session_type
> type,
>                 enum ssl_state state, struct stream **streamp)
>  {
>      struct ssl_stream *sslv;
> @@ -274,6 +284,14 @@ new_ssl_stream(char *name, int fd, enum session_type
> type,
>      if (!verify_peer_cert || (bootstrap_ca_cert && type == CLIENT)) {
>          SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
>      }
> +#if OPENSSL_SUPPORTS_SNI
> +    if (server_name && !SSL_set_tlsext_host_name(ssl, server_name)) {
> +        VLOG_ERR("%s: failed to set server name indication (%s)",
> +                 server_name, ERR_error_string(ERR_get_error(), NULL));
> +        retval = ENOPROTOOPT;
> +        goto error;
> +    }
> +#endif
>
>      /* Create and return the ssl_stream. */
>      sslv = xmalloc(sizeof *sslv);
> @@ -293,6 +311,7 @@ new_ssl_stream(char *name, int fd, enum session_type
> type,
>      }
>
>      *streamp = &sslv->stream;
> +    free(server_name);
>      return 0;
>
>  error:
> @@ -301,6 +320,7 @@ error:
>      }
>      closesocket(fd);
>      free(name);
> +    free(server_name);
>      return retval;
>  }
>
> @@ -311,6 +331,30 @@ ssl_stream_cast(struct stream *stream)
>      return CONTAINER_OF(stream, struct ssl_stream, stream);
>  }
>
> +/* Extracts and returns the server name from 'suffix'.  The caller must
> + * eventually free it.
> + *
> + * Returns NULL if there is no server name, and particularly if it is an
> IP
> + * address rather than a host name, since RFC 3546 is explicit that IP
> + * addresses are unsuitable as server name indication (SNI). */
> +static char *
> +get_server_name(const char *suffix_)
> +{
> +    char *suffix = xstrdup(suffix_);
> +
> +    char *host, *port;
> +    inet_parse_host_port_tokens(suffix, &host, &port);
> +
> +    ovs_be32 ipv4;
> +    struct in6_addr ipv6;
> +    char *server_name = (ip_parse(host, &ipv4) || ipv6_parse(host, &ipv6)
> +                         ? NULL : xstrdup(host));
> +
> +    free(suffix);
> +
> +    return server_name;
> +}
> +
>  static int
>  ssl_open(const char *name, char *suffix, struct stream **streamp, uint8_t
> dscp)
>  {
> @@ -325,7 +369,8 @@ ssl_open(const char *name, char *suffix, struct stream
> **streamp, uint8_t dscp)
>                               dscp);
>      if (fd >= 0) {
>          int state = error ? STATE_TCP_CONNECTING : STATE_SSL_CONNECTING;
> -        return new_ssl_stream(xstrdup(name), fd, CLIENT, state, streamp);
> +        return new_ssl_stream(xstrdup(name), get_server_name(suffix),
> +                              fd, CLIENT, state, streamp);
>      } else {
>          VLOG_ERR("%s: connect: %s", name, ovs_strerror(error));
>          return error;
> @@ -514,6 +559,14 @@ ssl_connect(struct stream *stream)
>              VLOG_INFO("rejecting SSL connection during bootstrap race
> window");
>              return EPROTO;
>          } else {
> +#if OPENSSL_SUPPORTS_SNI
> +            const char *servername = SSL_get_servername(
> +                sslv->ssl, TLSEXT_NAMETYPE_host_name);
> +            if (servername) {
> +                VLOG_DBG("connection indicated server name %s",
> servername);
> +            }
> +#endif
> +
>              char *cn = get_peer_common_name(sslv);
>
>              if (cn) {
> @@ -899,7 +952,7 @@ pssl_accept(struct pstream *pstream, struct stream
> **new_streamp)
>      ds_put_cstr(&name, "ssl:");
>      ss_format_address(&ss, &name);
>      ds_put_format(&name, ":%"PRIu16, ss_get_port(&ss));
> -    return new_ssl_stream(ds_steal_cstr(&name), new_fd, SERVER,
> +    return new_ssl_stream(ds_steal_cstr(&name), NULL, new_fd, SERVER,
>                            STATE_SSL_CONNECTING, new_streamp);
>  }
>
> diff --git a/m4/openvswitch.m4 b/m4/openvswitch.m4
> index 41042c98e286..b599f17d77a1 100644
> --- a/m4/openvswitch.m4
> +++ b/m4/openvswitch.m4
> @@ -1,6 +1,6 @@
>  # -*- autoconf -*-
>
> -# Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016
> Nicira, Inc.
> +# Copyright (c) 2008-2016, 2019 Nicira, Inc.
>  #
>  # Licensed under the Apache License, Version 2.0 (the "License");
>  # you may not use this file except in compliance with the License.
> @@ -285,7 +285,24 @@ OpenFlow connections over SSL will not be supported.
>     AM_CONDITIONAL([HAVE_OPENSSL], [test "$HAVE_OPENSSL" = yes])
>     if test "$HAVE_OPENSSL" = yes; then
>        AC_DEFINE([HAVE_OPENSSL], [1], [Define to 1 if OpenSSL is
> installed.])
> -   fi])
> +   fi
> +
> +   OPENSSL_SUPPORTS_SNI=no
> +   if test $HAVE_OPENSSL = yes; then
> +      save_CPPFLAGS=$CPPFLAGS
> +      CPPFLAGS="$CPPFLAGS $SSL_INCLUDES"
> +      AC_CHECK_DECL([SSL_set_tlsext_host_name],
> [OPENSSL_SUPPORTS_SNI=yes],
> +                    [], [#include <openssl/ssl.h>
> +])
> +      if test $OPENSSL_SUPPORTS_SNI = yes; then
> +        AC_DEFINE(
> +          [OPENSSL_SUPPORTS_SNI], [1],
> +          [Define to 1 if OpenSSL supports Server Name Indication (SNI).])
> +      fi
> +      CPPFLAGS=$save_CPPFLAGS
> +   fi
> +   AC_SUBST([OPENSSL_SUPPORTS_SNI])
> +])
>
>  dnl Checks for libraries needed by lib/socket-util.c.
>  AC_DEFUN([OVS_CHECK_SOCKET_LIBS],
> @@ -691,7 +708,7 @@ AC_DEFUN([OVS_CHECK_CXX],
>
>  dnl Checks for unbound library.
>  AC_DEFUN([OVS_CHECK_UNBOUND],
> -  [AC_CHECK_LIB(unbound, ub_ctx_create, [HAVE_UNBOUND=yes])
> +  [AC_CHECK_LIB(unbound, ub_ctx_create, [HAVE_UNBOUND=yes],
> [HAVE_UNBOUND=no])
>     if test "$HAVE_UNBOUND" = yes; then
>       AC_DEFINE([HAVE_UNBOUND], [1], [Define to 1 if unbound is detected.])
>       LIBS="$LIBS -lunbound"
> diff --git a/tests/atlocal.in b/tests/atlocal.in
> index 5eff0a0aa216..f37f15430ccd 100644
> --- a/tests/atlocal.in
> +++ b/tests/atlocal.in
> @@ -1,8 +1,10 @@
>  # -*- shell-script -*-
>  HAVE_OPENSSL='@HAVE_OPENSSL@'
> +OPENSSL_SUPPORTS_SNI='@OPENSSL_SUPPORTS_SNI@'
>  HAVE_PYTHON='@HAVE_PYTHON@'
>  HAVE_PYTHON2='@HAVE_PYTHON2@'
>  HAVE_PYTHON3='@HAVE_PYTHON3@'
> +HAVE_UNBOUND='@HAVE_UNBOUND@'
>  EGREP='@EGREP@'
>
>  if test x"$PYTHON" = x; then
> diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
> index 6f37c0da781a..77604c58a2bc 100644
> --- a/tests/ovs-vsctl.at
> +++ b/tests/ovs-vsctl.at
> @@ -1422,3 +1422,23 @@ AT_CHECK([ovs-vsctl -t 5 --no-wait
> --db=ssl:127.0.0.1:$SSL_PORT --private-key=$P
>
>  OVS_VSCTL_CLEANUP
>  AT_CLEANUP
> +
> +AT_SETUP([TLS server name indication (SNI)])
> +AT_KEYWORDS([ovsdb server positive ssl tls sni])
> +AT_SKIP_IF([test "$HAVE_OPENSSL" = no])
> +AT_SKIP_IF([test "$OPENSSL_SUPPORTS_SNI" = no])
> +AT_SKIP_IF([test "$HAVE_UNBOUND" = no])
> +OVSDB_INIT([conf.db])
> +PKIDIR=$abs_top_builddir/tests
> +AT_CHECK([ovsdb-server --log-file --detach --no-chdir --pidfile
> --private-key=$PKIDIR/testpki-privkey2.pem
> --certificate=$PKIDIR/testpki-cert2.pem
> --ca-cert=$PKIDIR/testpki-cacert.pem --remote=pssl:0:127.0.0.1
> -vPATTERN:file:%m -vstream_ssl conf.db], [0], [ignore], [ignore])
> +PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT])
> +
> +AT_CHECK([ovs-vsctl -t 5 --no-wait --db=ssl:localhost:$SSL_PORT
> --private-key=$PKIDIR/testpki-privkey.pem
> --certificate=$PKIDIR/testpki-cert.pem
> --bootstrap-ca-cert=$PKIDIR/testpki-cacert.pem add-br br0])
> +
> +AT_CAPTURE_FILE([ovsdb-server.log])
> +AT_CHECK([grep "server name" ovsdb-server.log], [0],
> +         [connection indicated server name localhost
> +])
> +
> +OVS_VSCTL_CLEANUP
> +AT_CLEANUP
> --
> 2.20.1
>
>

-- 
Transforming data into insights for networking & security -
https://ServiceFractal.com
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to