diff mbox

[ovs-dev] Add support for specifying SSL connection parameters to ovsdb

Message ID 1478128769-24626-2-git-send-email-erahn@arista.com
State Accepted
Headers show

Commit Message

Ethan Rahn Nov. 2, 2016, 11:19 p.m. UTC
Signed-off-by: Ethan Rahn <erahn@arista.com>
---
 AUTHORS                                   |  1 +
 lib/automake.mk                           |  2 +
 lib/ssl-connect-syn.man                   |  5 +++
 lib/ssl-connect.man                       | 16 +++++++
 lib/stream-ssl.c                          | 70 +++++++++++++++++++++++++++++++
 lib/stream-ssl.h                          | 20 ++++++++-
 manpages.mk                               |  8 ++++
 ovn/controller-vtep/ovn-controller-vtep.c |  3 +-
 ovn/controller/ovn-controller.c           |  3 +-
 ovn/northd/ovn-northd.c                   |  1 +
 ovn/utilities/ovn-nbctl.c                 |  3 +-
 ovn/utilities/ovn-sbctl.c                 |  3 +-
 ovn/utilities/ovn-trace.c                 |  1 +
 ovsdb/ovsdb-client.1.in                   |  3 ++
 ovsdb/ovsdb-client.c                      |  3 +-
 ovsdb/ovsdb-server.1.in                   |  3 ++
 ovsdb/ovsdb-server.c                      | 23 ++++++++--
 tests/ovsdb-server.at                     | 68 +++++++++++++++++++++++++++++-
 tests/test-jsonrpc.c                      |  3 +-
 utilities/ovs-ofctl.c                     |  3 +-
 utilities/ovs-testcontroller.c            |  3 +-
 utilities/ovs-vsctl.c                     |  3 +-
 vswitchd/ovs-vswitchd.c                   |  1 +
 vtep/vtep-ctl.c                           |  3 +-
 24 files changed, 234 insertions(+), 18 deletions(-)
 create mode 100644 lib/ssl-connect-syn.man
 create mode 100644 lib/ssl-connect.man

Comments

Ethan Rahn Nov. 2, 2016, 11:21 p.m. UTC | #1
Added a 'Signed-off-by' line. Thanks for getting back to me and this being
patient while I learned how to use 'git send-email'. I'm glad I could
contribute upstream to the project.

Cheers,

Ethan

On Wed, Nov 2, 2016 at 4:19 PM, Ethan Rahn <erahn@arista.com> wrote:

>
> Signed-off-by: Ethan Rahn <erahn@arista.com>
> ---
>  AUTHORS                                   |  1 +
>  lib/automake.mk                           |  2 +
>  lib/ssl-connect-syn.man                   |  5 +++
>  lib/ssl-connect.man                       | 16 +++++++
>  lib/stream-ssl.c                          | 70
> +++++++++++++++++++++++++++++++
>  lib/stream-ssl.h                          | 20 ++++++++-
>  manpages.mk                               |  8 ++++
>  ovn/controller-vtep/ovn-controller-vtep.c |  3 +-
>  ovn/controller/ovn-controller.c           |  3 +-
>  ovn/northd/ovn-northd.c                   |  1 +
>  ovn/utilities/ovn-nbctl.c                 |  3 +-
>  ovn/utilities/ovn-sbctl.c                 |  3 +-
>  ovn/utilities/ovn-trace.c                 |  1 +
>  ovsdb/ovsdb-client.1.in                   |  3 ++
>  ovsdb/ovsdb-client.c                      |  3 +-
>  ovsdb/ovsdb-server.1.in                   |  3 ++
>  ovsdb/ovsdb-server.c                      | 23 ++++++++--
>  tests/ovsdb-server.at                     | 68
> +++++++++++++++++++++++++++++-
>  tests/test-jsonrpc.c                      |  3 +-
>  utilities/ovs-ofctl.c                     |  3 +-
>  utilities/ovs-testcontroller.c            |  3 +-
>  utilities/ovs-vsctl.c                     |  3 +-
>  vswitchd/ovs-vswitchd.c                   |  1 +
>  vtep/vtep-ctl.c                           |  3 +-
>  24 files changed, 234 insertions(+), 18 deletions(-)
>  create mode 100644 lib/ssl-connect-syn.man
>  create mode 100644 lib/ssl-connect.man
>
> diff --git a/AUTHORS b/AUTHORS
> index c089d59..197142f 100644
> --- a/AUTHORS
> +++ b/AUTHORS
> @@ -80,6 +80,7 @@ Eitan Eliahu            eliahue@vmware.com
>  Eohyung Lee             liquidnuker@gmail.com
>  Eric Sesterhenn         eric.sesterhenn@lsexperts.de
>  Ethan J. Jackson        ejj@eecs.berkeley.edu
> +Ethan Rahn              erahn@arista.com
>  Eziz Durdyyev           ezizdurdy@gmail.com
>  Flavio Fernandes        flavio@flaviof.com
>  Flavio Leitner          fbl@redhat.com
> diff --git a/lib/automake.mk b/lib/automake.mk
> index 165e6a8..62bb17b 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -462,6 +462,8 @@ MAN_FRAGMENTS += \
>         lib/ssl-peer-ca-cert-syn.man \
>         lib/ssl.man \
>         lib/ssl-syn.man \
> +       lib/ssl-connect.man \
> +       lib/ssl-connect-syn.man \
>         lib/table.man \
>         lib/unixctl.man \
>         lib/unixctl-syn.man \
> diff --git a/lib/ssl-connect-syn.man b/lib/ssl-connect-syn.man
> new file mode 100644
> index 0000000..0510a59
> --- /dev/null
> +++ b/lib/ssl-connect-syn.man
> @@ -0,0 +1,5 @@
> +.IP "SSL connection options:"
> +[\fB\-\-ssl\-protocols=\fITLSv1,TLSv1.1,TLSv1.2\fR]
> +.br
> +[\fB\-\-ssl\-ciphers=\fIHIGH:!aNULL:!MD5\fR]
> +.br
> diff --git a/lib/ssl-connect.man b/lib/ssl-connect.man
> new file mode 100644
> index 0000000..dcc6a79
> --- /dev/null
> +++ b/lib/ssl-connect.man
> @@ -0,0 +1,16 @@
> +.de IQ
> +.  br
> +.  ns
> +.  IP "\\$1"
> +..
> +.IQ "\fB\-\-ssl\-protocols=\fITLSv1,TLSv1.1,TLSv1.2\fR"
> +Specifies, in a comma or white-list delimited, list the SSL protocols
> \fB\*(PN\fR
> +will support for SSL connections. Supported protocols are: TLSv1, TLSv1.1,
> +TLSv1.2. Order does not matter, the highest protocol supported by both
> sides
> +will be choosen when making the connection.
> +.
> +.IQ "\fB\-\-ssl\-ciphers=\fIHIGH:!aNULL:!MD5\fR"
> +Specifies, in OpenSSL cipher string format, the ciphers \fB\*(PN\fR will
> +support for SSL connections.
> +
> +
> diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c
> index a5c32a1..87b8de9 100644
> --- a/lib/stream-ssl.c
> +++ b/lib/stream-ssl.c
> @@ -162,6 +162,8 @@ struct ssl_config_file {
>  static struct ssl_config_file private_key;
>  static struct ssl_config_file certificate;
>  static struct ssl_config_file ca_cert;
> +static char *ssl_protocols = "TLSv1,TLSv1.1,TLSv1.2";
> +static char *ssl_ciphers = "HIGH:!aNULL:!MD5";
>
>  /* Ordinarily, the SSL client and server verify each other's certificates
> using
>   * a CA certificate.  Setting this to false disables this behavior.
> (This is a
> @@ -966,6 +968,7 @@ do_ssl_init(void)
>      SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_
> CERT,
>                         NULL);
>      SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
> +    SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!MD5");
>
>      return 0;
>  }
> @@ -1114,6 +1117,73 @@ stream_ssl_set_key_and_cert(const char
> *private_key_file,
>      }
>  }
>
> +/* Sets SSL ciphers based on string input. Aborts with an error message
> + * if 'arg' is invalid. */
> +void
> +stream_ssl_set_ciphers(const char *arg)
> +{
> +    if (ssl_init() || !arg || !strcmp(ssl_ciphers, arg)){
> +       return;
> +    }
> +    if (SSL_CTX_set_cipher_list(ctx,arg) == 0)
> +    {
> +     VLOG_ERR("SSL_CTX_set_cipher_list: %s",
> +              ERR_error_string(ERR_get_error(), NULL));
> +    }
> +    ssl_ciphers = xstrdup(arg);
> +}
> +
> +/* Set SSL protocols based on the string input. Aborts with an error
> message
> + * if 'arg' is invalid. */
> +void
> +stream_ssl_set_protocols(const char *arg)
> +{
> +    char *s;
> +    char *save_ptr = NULL;
> +    char *word;
> +    long protocolFlags;
> +
> +    if (ssl_init() || !arg || !strcmp(arg, ssl_protocols)){
> +       return;
> +    }
> +
> +    s = xstrdup(arg);
> +    /* Start with all the flags off and turn them on as requested. */
> +    protocolFlags |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
> +    protocolFlags |= SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
> +
> +    word = strtok_r(s, " ,\t", &save_ptr);
> +    if (word == NULL) {
> +       VLOG_ERR( "SSL protocol settings invalid" );
> +       goto exit;
> +    }
> +    while (word != NULL) {
> +       long onFlag;
> +       if (!strcasecmp(word, "TLSv1.2")){
> +          onFlag = SSL_OP_NO_TLSv1_2;
> +       } else if (!strcasecmp(word, "TLSv1.1")){
> +          onFlag = SSL_OP_NO_TLSv1_1;
> +       } else if (!strcasecmp(word, "TLSv1")){
> +          onFlag = SSL_OP_NO_TLSv1;
> +       } else {
> +          VLOG_ERR( "SSL Protocol not recognized" );
> +          goto exit;
> +       }
> +       /* Reverse the no flag and mask it out in the flags
> +        * to turn on that protocol. */
> +       protocolFlags &= ~onFlag;
> +       word = strtok_r(NULL, " ,\t", &save_ptr);
> +    };
> +
> +    // Finally, set the actual options
> +    SSL_CTX_set_options(ctx, protocolFlags);
> +
> +    ssl_protocols = xstrdup(arg);
> +
> +exit:
> +    free(s);
> +}
> +
>  /* Reads the X509 certificate or certificates in file 'file_name'.  On
> success,
>   * stores the address of the first element in an array of pointers to
>   * certificates in '*certs' and the number of certificates in the array in
> diff --git a/lib/stream-ssl.h b/lib/stream-ssl.h
> index 030f662..4bfe09b 100644
> --- a/lib/stream-ssl.h
> +++ b/lib/stream-ssl.h
> @@ -25,11 +25,19 @@ void stream_ssl_set_ca_cert_file(const char
> *file_name, bool bootstrap);
>  void stream_ssl_set_peer_ca_cert_file(const char *file_name);
>  void stream_ssl_set_key_and_cert(const char *private_key_file,
>                                   const char *certificate_file);
> +void stream_ssl_set_protocols(const char *arg);
> +void stream_ssl_set_ciphers(const char *arg);
> +
> +#define SSL_OPTION_ENUMS \
> +        OPT_SSL_PROTOCOLS, \
> +        OPT_SSL_CIPHERS
>
>  #define STREAM_SSL_LONG_OPTIONS                     \
>          {"private-key", required_argument, NULL, 'p'}, \
>          {"certificate", required_argument, NULL, 'c'}, \
> -        {"ca-cert",     required_argument, NULL, 'C'}
> +        {"ca-cert",     required_argument, NULL, 'C'}, \
> +        {"ssl-protocols", required_argument, NULL, OPT_SSL_PROTOCOLS}, \
> +        {"ssl-ciphers", required_argument, NULL, OPT_SSL_CIPHERS}
>
>  #define STREAM_SSL_OPTION_HANDLERS                      \
>          case 'p':                                       \
> @@ -42,6 +50,14 @@ void stream_ssl_set_key_and_cert(const char
> *private_key_file,
>                                                          \
>          case 'C':                                       \
>              stream_ssl_set_ca_cert_file(optarg, false); \
> -            break;
> +            break;                                      \
> +                                                        \
> +        case OPT_SSL_PROTOCOLS:                         \
> +            stream_ssl_set_protocols(optarg);           \
> +            break;                                      \
> +                                                        \
> +        case OPT_SSL_CIPHERS:                           \
> +            stream_ssl_set_ciphers(optarg);             \
> +            break;
>
>  #endif /* stream-ssl.h */
> diff --git a/manpages.mk b/manpages.mk
> index fa9e59b..2ff7658 100644
> --- a/manpages.mk
> +++ b/manpages.mk
> @@ -20,6 +20,8 @@ ovsdb/ovsdb-client.1: \
>         lib/daemon.man \
>         lib/ssl-bootstrap-syn.man \
>         lib/ssl-bootstrap.man \
> +       lib/ssl-connect-syn.man \
> +       lib/ssl-connect.man \
>         lib/ssl-syn.man \
>         lib/ssl.man \
>         lib/table.man \
> @@ -34,6 +36,8 @@ lib/daemon-syn.man:
>  lib/daemon.man:
>  lib/ssl-bootstrap-syn.man:
>  lib/ssl-bootstrap.man:
> +lib/ssl-connect-syn.man:
> +lib/ssl-connect.man:
>  lib/ssl-syn.man:
>  lib/ssl.man:
>  lib/table.man:
> @@ -54,6 +58,8 @@ ovsdb/ovsdb-server.1: \
>         lib/service.man \
>         lib/ssl-bootstrap-syn.man \
>         lib/ssl-bootstrap.man \
> +       lib/ssl-connect-syn.man \
> +       lib/ssl-connect.man \
>         lib/ssl-peer-ca-cert-syn.man \
>         lib/ssl-peer-ca-cert.man \
>         lib/ssl-syn.man \
> @@ -78,6 +84,8 @@ lib/service-syn.man:
>  lib/service.man:
>  lib/ssl-bootstrap-syn.man:
>  lib/ssl-bootstrap.man:
> +lib/ssl-connect-syn.man:
> +lib/ssl-connect.man:
>  lib/ssl-peer-ca-cert-syn.man:
>  lib/ssl-peer-ca-cert.man:
>  lib/ssl-syn.man:
> diff --git a/ovn/controller-vtep/ovn-controller-vtep.c
> b/ovn/controller-vtep/ovn-controller-vtep.c
> index baee789..f70d9bc 100644
> --- a/ovn/controller-vtep/ovn-controller-vtep.c
> +++ b/ovn/controller-vtep/ovn-controller-vtep.c
> @@ -169,7 +169,8 @@ parse_options(int argc, char *argv[])
>          OPT_PEER_CA_CERT = UCHAR_MAX + 1,
>          OPT_BOOTSTRAP_CA_CERT,
>          VLOG_OPTION_ENUMS,
> -        DAEMON_OPTION_ENUMS
> +        DAEMON_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>
>      static struct option long_options[] = {
> diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-
> controller.c
> index 66a364f..0e57cb9 100644
> --- a/ovn/controller/ovn-controller.c
> +++ b/ovn/controller/ovn-controller.c
> @@ -552,7 +552,8 @@ parse_options(int argc, char *argv[])
>          OPT_PEER_CA_CERT = UCHAR_MAX + 1,
>          OPT_BOOTSTRAP_CA_CERT,
>          VLOG_OPTION_ENUMS,
> -        DAEMON_OPTION_ENUMS
> +        DAEMON_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>
>      static struct option long_options[] = {
> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> index 0874a9c..c0e8e98 100644
> --- a/ovn/northd/ovn-northd.c
> +++ b/ovn/northd/ovn-northd.c
> @@ -4201,6 +4201,7 @@ parse_options(int argc OVS_UNUSED, char *argv[]
> OVS_UNUSED)
>      enum {
>          DAEMON_OPTION_ENUMS,
>          VLOG_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option long_options[] = {
>          {"ovnsb-db", required_argument, NULL, 'd'},
> diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
> index d6d64ea..3785073 100644
> --- a/ovn/utilities/ovn-nbctl.c
> +++ b/ovn/utilities/ovn-nbctl.c
> @@ -167,7 +167,8 @@ parse_options(int argc, char *argv[], struct shash
> *local_options)
>          OPT_COMMANDS,
>          OPT_OPTIONS,
>          VLOG_OPTION_ENUMS,
> -        TABLE_OPTION_ENUMS
> +        TABLE_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option global_long_options[] = {
>          {"db", required_argument, NULL, OPT_DB},
> diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
> index b894b8b..a067de6 100644
> --- a/ovn/utilities/ovn-sbctl.c
> +++ b/ovn/utilities/ovn-sbctl.c
> @@ -162,7 +162,8 @@ parse_options(int argc, char *argv[], struct shash
> *local_options)
>          OPT_COMMANDS,
>          OPT_OPTIONS,
>          VLOG_OPTION_ENUMS,
> -        TABLE_OPTION_ENUMS
> +        TABLE_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option global_long_options[] = {
>          {"db", required_argument, NULL, OPT_DB},
> diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
> index df2ff21..4d8abe9 100644
> --- a/ovn/utilities/ovn-trace.c
> +++ b/ovn/utilities/ovn-trace.c
> @@ -153,6 +153,7 @@ parse_options(int argc, char *argv[])
>          OPT_MINIMAL,
>          OPT_ALL,
>          DAEMON_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>          VLOG_OPTION_ENUMS
>      };
>      static const struct option long_options[] = {
> diff --git a/ovsdb/ovsdb-client.1.in b/ovsdb/ovsdb-client.1.in
> index 1bb7419..9658291 100644
> --- a/ovsdb/ovsdb-client.1.in
> +++ b/ovsdb/ovsdb-client.1.in
> @@ -55,6 +55,7 @@ ovsdb\-client \- command-line interface to
> \fBovsdb-server\fR(1)
>  .so lib/vlog-syn.man
>  .so lib/ssl-syn.man
>  .so lib/ssl-bootstrap-syn.man
> +.so lib/ssl-connect-syn.man
>  .so lib/common-syn.man
>  .
>  .SH DESCRIPTION
> @@ -205,6 +206,8 @@ With any other command, they have no effect.
>  .SS "Public Key Infrastructure Options"
>  .so lib/ssl.man
>  .so lib/ssl-bootstrap.man
> +.SS "SSL Connection Options"
> +.so lib/ssl-connect.man
>  .SS "Other Options"
>  .so lib/common.man
>  .SH "SEE ALSO"
> diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c
> index 1f83f3b..452c416 100644
> --- a/ovsdb/ovsdb-client.c
> +++ b/ovsdb/ovsdb-client.c
> @@ -174,7 +174,8 @@ parse_options(int argc, char *argv[])
>          OPT_TIMESTAMP,
>          VLOG_OPTION_ENUMS,
>          DAEMON_OPTION_ENUMS,
> -        TABLE_OPTION_ENUMS
> +        TABLE_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option long_options[] = {
>          {"help", no_argument, NULL, 'h'},
> diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in
> index d1ba83b..470645b 100644
> --- a/ovsdb/ovsdb-server.1.in
> +++ b/ovsdb/ovsdb-server.1.in
> @@ -23,6 +23,7 @@ ovsdb\-server \- Open vSwitch database server
>  .so lib/ssl-syn.man
>  .so lib/ssl-bootstrap-syn.man
>  .so lib/ssl-peer-ca-cert-syn.man
> +.so lib/ssl-connect-syn.man
>  .so lib/unixctl-syn.man
>  .so lib/common-syn.man
>  .
> @@ -133,6 +134,8 @@ one row in \fItable\fR.)
>  .so lib/ssl.man
>  .so lib/ssl-bootstrap.man
>  .so lib/ssl-peer-ca-cert.man
> +.SS "SSL Connection Options"
> +.so lib/ssl-connect.man
>  .SS "Other Options"
>  .so lib/unixctl.man
>  .so lib/common.man
> diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
> index e08c341..f34d0d8 100644
> --- a/ovsdb/ovsdb-server.c
> +++ b/ovsdb/ovsdb-server.c
> @@ -66,6 +66,8 @@ struct db;
>  static char *private_key_file;
>  static char *certificate_file;
>  static char *ca_cert_file;
> +static char *ssl_protocols;
> +static char *ssl_ciphers;
>  static bool bootstrap_ca_cert;
>
>  /* Replication configuration. */
> @@ -1024,13 +1026,19 @@ reconfigure_ssl(const struct shash *all_dbs)
>      const char *resolved_private_key;
>      const char *resolved_certificate;
>      const char *resolved_ca_cert;
> +    const char *resolved_ssl_protocols;
> +    const char *resolved_ssl_ciphers;
>
>      resolved_private_key = query_db_string(all_dbs, private_key_file,
> &errors);
>      resolved_certificate = query_db_string(all_dbs, certificate_file,
> &errors);
>      resolved_ca_cert = query_db_string(all_dbs, ca_cert_file, &errors);
> +    resolved_ssl_protocols = query_db_string(all_dbs, ssl_protocols,
> &errors);
> +    resolved_ssl_ciphers = query_db_string(all_dbs, ssl_ciphers, &errors);
>
>      stream_ssl_set_key_and_cert(resolved_private_key,
> resolved_certificate);
>      stream_ssl_set_ca_cert_file(resolved_ca_cert, bootstrap_ca_cert);
> +    stream_ssl_set_protocols(resolved_ssl_protocols);
> +    stream_ssl_set_ciphers(resolved_ssl_ciphers);
>
>      return errors.string;
>  }
> @@ -1384,7 +1392,8 @@ parse_options(int *argcp, char **argvp[],
>          OPT_SYNC_FROM,
>          OPT_SYNC_EXCLUDE,
>          VLOG_OPTION_ENUMS,
> -        DAEMON_OPTION_ENUMS
> +        DAEMON_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option long_options[] = {
>          {"remote",      required_argument, NULL, OPT_REMOTE},
> @@ -1398,9 +1407,7 @@ parse_options(int *argcp, char **argvp[],
>          VLOG_LONG_OPTIONS,
>          {"bootstrap-ca-cert", required_argument, NULL,
> OPT_BOOTSTRAP_CA_CERT},
>          {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
> -        {"private-key", required_argument, NULL, 'p'},
> -        {"certificate", required_argument, NULL, 'c'},
> -        {"ca-cert",     required_argument, NULL, 'C'},
> +        STREAM_SSL_LONG_OPTIONS,
>          {"sync-from",   required_argument, NULL, OPT_SYNC_FROM},
>          {"sync-exclude-tables", required_argument, NULL,
> OPT_SYNC_EXCLUDE},
>          {NULL, 0, NULL, 0},
> @@ -1454,6 +1461,14 @@ parse_options(int *argcp, char **argvp[],
>              bootstrap_ca_cert = false;
>              break;
>
> +        case OPT_SSL_PROTOCOLS:
> +            ssl_protocols = optarg;
> +            break;
> +
> +        case OPT_SSL_CIPHERS:
> +            ssl_ciphers = optarg;
> +            break;
> +
>          case OPT_BOOTSTRAP_CA_CERT:
>              ca_cert_file = optarg;
>              bootstrap_ca_cert = true;
> diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
> index 0436de8..6347829 100644
> --- a/tests/ovsdb-server.at
> +++ b/tests/ovsdb-server.at
> @@ -513,9 +513,13 @@ AT_DATA([schema],
>           "columns": {
>             "private_key": {"type": "string"},
>             "certificate": {"type": "string"},
> -           "ca_cert": {"type": "string"}}}}}
> +           "ca_cert": {"type": "string"},
> +           "ssl_protocols" : {"type": "string"},
> +           "ssl_ciphers" : {"type" : "string"}}}}}
>  ]])
>  AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
> +# The !ECDHE-ECDSA-AES256-GCM-SHA384 in the ssl_ciphers is so that
> +# a cipher negotiation failure can be tested for later.
>  AT_CHECK(
>    [[ovsdb-tool transact db \
>       '["mydb",
> @@ -523,13 +527,17 @@ AT_CHECK(
>          "table": "SSL",
>          "row": {"private_key": "'"$PKIDIR/testpki-privkey2.pem"'",
>                  "certificate": "'"$PKIDIR/testpki-cert2.pem"'",
> -                "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'"}}]']],
> +                "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'",
> +                "ssl_protocols": "'"TLSv1.2,TLSv1.1"'",
> +                "ssl_ciphers": "'"HIGH:!aNULL:!MD5:!ECDHE-
> ECDSA-AES256-GCM-SHA384"'"}}]']],
>    [0], [ignore], [ignore])
>  AT_CHECK(
>    [ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid \
>          --private-key=db:mydb,SSL,private_key \
>          --certificate=db:mydb,SSL,certificate \
>          --ca-cert=db:mydb,SSL,ca_cert \
> +        --ssl-protocols=db:mydb,SSL,ssl_protocols \
> +        --ssl-ciphers=db:mydb,SSL,ssl_ciphers \
>          --remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db],
>    [0], [ignore], [ignore])
>  PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT])
> @@ -538,6 +546,8 @@ AT_CHECK(
>          --private-key=$PKIDIR/testpki-privkey.pem \
>          --certificate=$PKIDIR/testpki-cert.pem \
>          --ca-cert=$PKIDIR/testpki-cacert.pem \
> +        --ssl-protocols=TLSv1.2,TLSv1.1 \
> +        --ssl-ciphers=HIGH:!aNULL:!MD5 \
>          transact ssl:127.0.0.1:$SSL_PORT \
>          '["mydb",
>            {"op": "select",
> @@ -550,6 +560,60 @@ AT_CHECK_UNQUOTED(
>    [cat output], [0],
>    [[@<:@{"rows":@<:@{"private_key":"$PKIDIR/testpki-
> privkey2.pem"}@:>@}@:>@
>  ]], [ignore], [test ! -e pid || kill `cat pid`])
> +# Check that when the server has TLSv1.1+ and the client has
> +# TLSv1 that the connection fails.
> +AT_CHECK(
> +  [[ovsdb-client \
> +        --private-key=$PKIDIR/testpki-privkey.pem \
> +        --certificate=$PKIDIR/testpki-cert.pem \
> +        --ca-cert=$PKIDIR/testpki-cacert.pem \
> +        --ssl-protocols=TLSv1 \
> +        --ssl-ciphers=HIGH:!aNULL:!MD5 \
> +        transact ssl:127.0.0.1:$SSL_PORT \
> +        '["mydb",
> +          {"op": "select",
> +           "table": "SSL",
> +           "where": [],
> +           "columns": ["private_key"]}]']],
> +  [1], [stdout],
> +  [stderr],
> +  [test ! -e pid || kill `cat pid`])
> +cat stderr > output
> +AT_CHECK_UNQUOTED(
> +  [grep "failed to connect" output], [0],
> +  [ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT"
> (Protocol error)
> +],
> +  [ignore], [test ! -e pid || kill `cat pid`])
> +# Check that when ciphers are not compatible, that a negotiation
> +# failure occurs.
> +AT_CHECK(
> +  [[ovsdb-client \
> +        --private-key=$PKIDIR/testpki-privkey.pem \
> +        --certificate=$PKIDIR/testpki-cert.pem \
> +        --ca-cert=$PKIDIR/testpki-cacert.pem \
> +        --ssl-protocols=TLSv1.2,TLSv1.1 \
> +        --ssl-ciphers=ECDHE-ECDSA-AES256-GCM-SHA384 \
> +        transact ssl:127.0.0.1:$SSL_PORT \
> +        '["mydb",
> +          {"op": "select",
> +           "table": "SSL",
> +           "where": [],
> +           "columns": ["private_key"]}]']],
> +  [1], [stdout],
> +  [stderr],
> +  [test ! -e pid || kill `cat pid`])
> +cat stderr > output
> +AT_CHECK_UNQUOTED(
> +  [grep "failed to connect" output], [0],
> +  [ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT"
> (Protocol error)
> +],
> +  [ignore], [test ! -e pid || kill `cat pid`])
> +# The error message for being unable to negotiate a shared ciphersuite
> +# is 'sslv3 alert handshake failure'. This is not the clearest message.
> +AT_CHECK_UNQUOTED(
> +  [grep "sslv3 alert handshake failure" output], [0],
> +  [stdout],
> +  [ignore], [test ! -e pid || kill `cat pid`])
>  OVSDB_SERVER_SHUTDOWN
>  AT_CLEANUP
>
> diff --git a/tests/test-jsonrpc.c b/tests/test-jsonrpc.c
> index 684601a..a7ca691 100644
> --- a/tests/test-jsonrpc.c
> +++ b/tests/test-jsonrpc.c
> @@ -55,7 +55,8 @@ parse_options(int argc, char *argv[])
>  {
>      enum {
>          OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,
> -        DAEMON_OPTION_ENUMS
> +        DAEMON_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option long_options[] = {
>          {"verbose", optional_argument, NULL, 'v'},
> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> index 6fd3818..fa2466e 100644
> --- a/utilities/ovs-ofctl.c
> +++ b/utilities/ovs-ofctl.c
> @@ -190,7 +190,8 @@ parse_options(int argc, char *argv[])
>          OPT_READ_ONLY,
>          DAEMON_OPTION_ENUMS,
>          OFP_VERSION_OPTION_ENUMS,
> -        VLOG_OPTION_ENUMS
> +        VLOG_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option long_options[] = {
>          {"timeout", required_argument, NULL, 't'},
> diff --git a/utilities/ovs-testcontroller.c b/utilities/ovs-
> testcontroller.c
> index 1db3bbe..99f5bf8 100644
> --- a/utilities/ovs-testcontroller.c
> +++ b/utilities/ovs-testcontroller.c
> @@ -258,7 +258,8 @@ parse_options(int argc, char *argv[])
>          OPT_UNIXCTL,
>          VLOG_OPTION_ENUMS,
>          DAEMON_OPTION_ENUMS,
> -        OFP_VERSION_OPTION_ENUMS
> +        OFP_VERSION_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option long_options[] = {
>          {"hub",         no_argument, NULL, 'H'},
> diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
> index e710095..25f0e8a 100644
> --- a/utilities/ovs-vsctl.c
> +++ b/utilities/ovs-vsctl.c
> @@ -207,7 +207,8 @@ parse_options(int argc, char *argv[], struct shash
> *local_options)
>          OPT_COMMANDS,
>          OPT_OPTIONS,
>          VLOG_OPTION_ENUMS,
> -        TABLE_OPTION_ENUMS
> +        TABLE_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option global_long_options[] = {
>          {"db", required_argument, NULL, OPT_DB},
> diff --git a/vswitchd/ovs-vswitchd.c b/vswitchd/ovs-vswitchd.c
> index 72448bb..0e3b6d0 100644
> --- a/vswitchd/ovs-vswitchd.c
> +++ b/vswitchd/ovs-vswitchd.c
> @@ -145,6 +145,7 @@ parse_options(int argc, char *argv[], char
> **unixctl_pathp)
>          OPT_DISABLE_SYSTEM,
>          DAEMON_OPTION_ENUMS,
>          OPT_DPDK,
> +        SSL_OPTION_ENUMS,
>          OPT_DUMMY_NUMA,
>      };
>      static const struct option long_options[] = {
> diff --git a/vtep/vtep-ctl.c b/vtep/vtep-ctl.c
> index 245ba0d..af20f83 100644
> --- a/vtep/vtep-ctl.c
> +++ b/vtep/vtep-ctl.c
> @@ -166,7 +166,8 @@ parse_options(int argc, char *argv[], struct shash
> *local_options)
>          OPT_PEER_CA_CERT,
>          OPT_LOCAL,
>          VLOG_OPTION_ENUMS,
> -        TABLE_OPTION_ENUMS
> +        TABLE_OPTION_ENUMS,
> +        SSL_OPTION_ENUMS,
>      };
>      static const struct option global_long_options[] = {
>          {"db", required_argument, NULL, OPT_DB},
> --
> 1.8.1.4
>
>
diff mbox

Patch

diff --git a/AUTHORS b/AUTHORS
index c089d59..197142f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -80,6 +80,7 @@  Eitan Eliahu            eliahue@vmware.com
 Eohyung Lee             liquidnuker@gmail.com
 Eric Sesterhenn         eric.sesterhenn@lsexperts.de
 Ethan J. Jackson        ejj@eecs.berkeley.edu
+Ethan Rahn              erahn@arista.com
 Eziz Durdyyev           ezizdurdy@gmail.com
 Flavio Fernandes        flavio@flaviof.com
 Flavio Leitner          fbl@redhat.com
diff --git a/lib/automake.mk b/lib/automake.mk
index 165e6a8..62bb17b 100644
--- a/lib/automake.mk
+++ b/lib/automake.mk
@@ -462,6 +462,8 @@  MAN_FRAGMENTS += \
 	lib/ssl-peer-ca-cert-syn.man \
 	lib/ssl.man \
 	lib/ssl-syn.man \
+	lib/ssl-connect.man \
+	lib/ssl-connect-syn.man \
 	lib/table.man \
 	lib/unixctl.man \
 	lib/unixctl-syn.man \
diff --git a/lib/ssl-connect-syn.man b/lib/ssl-connect-syn.man
new file mode 100644
index 0000000..0510a59
--- /dev/null
+++ b/lib/ssl-connect-syn.man
@@ -0,0 +1,5 @@ 
+.IP "SSL connection options:"
+[\fB\-\-ssl\-protocols=\fITLSv1,TLSv1.1,TLSv1.2\fR]
+.br
+[\fB\-\-ssl\-ciphers=\fIHIGH:!aNULL:!MD5\fR]
+.br
diff --git a/lib/ssl-connect.man b/lib/ssl-connect.man
new file mode 100644
index 0000000..dcc6a79
--- /dev/null
+++ b/lib/ssl-connect.man
@@ -0,0 +1,16 @@ 
+.de IQ
+.  br
+.  ns
+.  IP "\\$1"
+..
+.IQ "\fB\-\-ssl\-protocols=\fITLSv1,TLSv1.1,TLSv1.2\fR"
+Specifies, in a comma or white-list delimited, list the SSL protocols \fB\*(PN\fR
+will support for SSL connections. Supported protocols are: TLSv1, TLSv1.1,
+TLSv1.2. Order does not matter, the highest protocol supported by both sides
+will be choosen when making the connection.
+.
+.IQ "\fB\-\-ssl\-ciphers=\fIHIGH:!aNULL:!MD5\fR"
+Specifies, in OpenSSL cipher string format, the ciphers \fB\*(PN\fR will 
+support for SSL connections.
+
+
diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c
index a5c32a1..87b8de9 100644
--- a/lib/stream-ssl.c
+++ b/lib/stream-ssl.c
@@ -162,6 +162,8 @@  struct ssl_config_file {
 static struct ssl_config_file private_key;
 static struct ssl_config_file certificate;
 static struct ssl_config_file ca_cert;
+static char *ssl_protocols = "TLSv1,TLSv1.1,TLSv1.2";
+static char *ssl_ciphers = "HIGH:!aNULL:!MD5";
 
 /* Ordinarily, the SSL client and server verify each other's certificates using
  * a CA certificate.  Setting this to false disables this behavior.  (This is a
@@ -966,6 +968,7 @@  do_ssl_init(void)
     SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
                        NULL);
     SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+    SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!MD5");
 
     return 0;
 }
@@ -1114,6 +1117,73 @@  stream_ssl_set_key_and_cert(const char *private_key_file,
     }
 }
 
+/* Sets SSL ciphers based on string input. Aborts with an error message
+ * if 'arg' is invalid. */
+void
+stream_ssl_set_ciphers(const char *arg)
+{
+    if (ssl_init() || !arg || !strcmp(ssl_ciphers, arg)){
+       return;
+    }
+    if (SSL_CTX_set_cipher_list(ctx,arg) == 0)
+    {
+     VLOG_ERR("SSL_CTX_set_cipher_list: %s",
+              ERR_error_string(ERR_get_error(), NULL));
+    }
+    ssl_ciphers = xstrdup(arg);
+}
+
+/* Set SSL protocols based on the string input. Aborts with an error message
+ * if 'arg' is invalid. */
+void
+stream_ssl_set_protocols(const char *arg)
+{
+    char *s;
+    char *save_ptr = NULL;
+    char *word;
+    long protocolFlags;
+
+    if (ssl_init() || !arg || !strcmp(arg, ssl_protocols)){
+       return;
+    }
+
+    s = xstrdup(arg);
+    /* Start with all the flags off and turn them on as requested. */
+    protocolFlags |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
+    protocolFlags |= SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
+
+    word = strtok_r(s, " ,\t", &save_ptr);
+    if (word == NULL) {
+       VLOG_ERR( "SSL protocol settings invalid" );
+       goto exit;
+    }
+    while (word != NULL) {
+       long onFlag;
+       if (!strcasecmp(word, "TLSv1.2")){
+          onFlag = SSL_OP_NO_TLSv1_2;
+       } else if (!strcasecmp(word, "TLSv1.1")){
+          onFlag = SSL_OP_NO_TLSv1_1;
+       } else if (!strcasecmp(word, "TLSv1")){
+          onFlag = SSL_OP_NO_TLSv1;
+       } else {
+          VLOG_ERR( "SSL Protocol not recognized" );
+          goto exit;
+       }
+       /* Reverse the no flag and mask it out in the flags
+        * to turn on that protocol. */
+       protocolFlags &= ~onFlag;
+       word = strtok_r(NULL, " ,\t", &save_ptr);
+    };
+
+    // Finally, set the actual options
+    SSL_CTX_set_options(ctx, protocolFlags);
+
+    ssl_protocols = xstrdup(arg);
+
+exit:
+    free(s);
+}
+
 /* Reads the X509 certificate or certificates in file 'file_name'.  On success,
  * stores the address of the first element in an array of pointers to
  * certificates in '*certs' and the number of certificates in the array in
diff --git a/lib/stream-ssl.h b/lib/stream-ssl.h
index 030f662..4bfe09b 100644
--- a/lib/stream-ssl.h
+++ b/lib/stream-ssl.h
@@ -25,11 +25,19 @@  void stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap);
 void stream_ssl_set_peer_ca_cert_file(const char *file_name);
 void stream_ssl_set_key_and_cert(const char *private_key_file,
                                  const char *certificate_file);
+void stream_ssl_set_protocols(const char *arg);
+void stream_ssl_set_ciphers(const char *arg);
+
+#define SSL_OPTION_ENUMS \
+        OPT_SSL_PROTOCOLS, \
+        OPT_SSL_CIPHERS
 
 #define STREAM_SSL_LONG_OPTIONS                     \
         {"private-key", required_argument, NULL, 'p'}, \
         {"certificate", required_argument, NULL, 'c'}, \
-        {"ca-cert",     required_argument, NULL, 'C'}
+        {"ca-cert",     required_argument, NULL, 'C'}, \
+        {"ssl-protocols", required_argument, NULL, OPT_SSL_PROTOCOLS}, \
+        {"ssl-ciphers", required_argument, NULL, OPT_SSL_CIPHERS}
 
 #define STREAM_SSL_OPTION_HANDLERS                      \
         case 'p':                                       \
@@ -42,6 +50,14 @@  void stream_ssl_set_key_and_cert(const char *private_key_file,
                                                         \
         case 'C':                                       \
             stream_ssl_set_ca_cert_file(optarg, false); \
-            break;
+            break;                                      \
+                                                        \
+        case OPT_SSL_PROTOCOLS:                         \
+            stream_ssl_set_protocols(optarg);           \
+            break;                                      \
+                                                        \
+        case OPT_SSL_CIPHERS:                           \
+            stream_ssl_set_ciphers(optarg);             \
+            break;                                      
 
 #endif /* stream-ssl.h */
diff --git a/manpages.mk b/manpages.mk
index fa9e59b..2ff7658 100644
--- a/manpages.mk
+++ b/manpages.mk
@@ -20,6 +20,8 @@  ovsdb/ovsdb-client.1: \
 	lib/daemon.man \
 	lib/ssl-bootstrap-syn.man \
 	lib/ssl-bootstrap.man \
+	lib/ssl-connect-syn.man \
+	lib/ssl-connect.man \
 	lib/ssl-syn.man \
 	lib/ssl.man \
 	lib/table.man \
@@ -34,6 +36,8 @@  lib/daemon-syn.man:
 lib/daemon.man:
 lib/ssl-bootstrap-syn.man:
 lib/ssl-bootstrap.man:
+lib/ssl-connect-syn.man:
+lib/ssl-connect.man:
 lib/ssl-syn.man:
 lib/ssl.man:
 lib/table.man:
@@ -54,6 +58,8 @@  ovsdb/ovsdb-server.1: \
 	lib/service.man \
 	lib/ssl-bootstrap-syn.man \
 	lib/ssl-bootstrap.man \
+	lib/ssl-connect-syn.man \
+	lib/ssl-connect.man \
 	lib/ssl-peer-ca-cert-syn.man \
 	lib/ssl-peer-ca-cert.man \
 	lib/ssl-syn.man \
@@ -78,6 +84,8 @@  lib/service-syn.man:
 lib/service.man:
 lib/ssl-bootstrap-syn.man:
 lib/ssl-bootstrap.man:
+lib/ssl-connect-syn.man:
+lib/ssl-connect.man:
 lib/ssl-peer-ca-cert-syn.man:
 lib/ssl-peer-ca-cert.man:
 lib/ssl-syn.man:
diff --git a/ovn/controller-vtep/ovn-controller-vtep.c b/ovn/controller-vtep/ovn-controller-vtep.c
index baee789..f70d9bc 100644
--- a/ovn/controller-vtep/ovn-controller-vtep.c
+++ b/ovn/controller-vtep/ovn-controller-vtep.c
@@ -169,7 +169,8 @@  parse_options(int argc, char *argv[])
         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
         OPT_BOOTSTRAP_CA_CERT,
         VLOG_OPTION_ENUMS,
-        DAEMON_OPTION_ENUMS
+        DAEMON_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
 
     static struct option long_options[] = {
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
index 66a364f..0e57cb9 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -552,7 +552,8 @@  parse_options(int argc, char *argv[])
         OPT_PEER_CA_CERT = UCHAR_MAX + 1,
         OPT_BOOTSTRAP_CA_CERT,
         VLOG_OPTION_ENUMS,
-        DAEMON_OPTION_ENUMS
+        DAEMON_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
 
     static struct option long_options[] = {
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 0874a9c..c0e8e98 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -4201,6 +4201,7 @@  parse_options(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
     enum {
         DAEMON_OPTION_ENUMS,
         VLOG_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option long_options[] = {
         {"ovnsb-db", required_argument, NULL, 'd'},
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index d6d64ea..3785073 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -167,7 +167,8 @@  parse_options(int argc, char *argv[], struct shash *local_options)
         OPT_COMMANDS,
         OPT_OPTIONS,
         VLOG_OPTION_ENUMS,
-        TABLE_OPTION_ENUMS
+        TABLE_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option global_long_options[] = {
         {"db", required_argument, NULL, OPT_DB},
diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c
index b894b8b..a067de6 100644
--- a/ovn/utilities/ovn-sbctl.c
+++ b/ovn/utilities/ovn-sbctl.c
@@ -162,7 +162,8 @@  parse_options(int argc, char *argv[], struct shash *local_options)
         OPT_COMMANDS,
         OPT_OPTIONS,
         VLOG_OPTION_ENUMS,
-        TABLE_OPTION_ENUMS
+        TABLE_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option global_long_options[] = {
         {"db", required_argument, NULL, OPT_DB},
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index df2ff21..4d8abe9 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -153,6 +153,7 @@  parse_options(int argc, char *argv[])
         OPT_MINIMAL,
         OPT_ALL,
         DAEMON_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
         VLOG_OPTION_ENUMS
     };
     static const struct option long_options[] = {
diff --git a/ovsdb/ovsdb-client.1.in b/ovsdb/ovsdb-client.1.in
index 1bb7419..9658291 100644
--- a/ovsdb/ovsdb-client.1.in
+++ b/ovsdb/ovsdb-client.1.in
@@ -55,6 +55,7 @@  ovsdb\-client \- command-line interface to \fBovsdb-server\fR(1)
 .so lib/vlog-syn.man
 .so lib/ssl-syn.man
 .so lib/ssl-bootstrap-syn.man
+.so lib/ssl-connect-syn.man
 .so lib/common-syn.man
 .
 .SH DESCRIPTION
@@ -205,6 +206,8 @@  With any other command, they have no effect.
 .SS "Public Key Infrastructure Options"
 .so lib/ssl.man
 .so lib/ssl-bootstrap.man
+.SS "SSL Connection Options"
+.so lib/ssl-connect.man
 .SS "Other Options"
 .so lib/common.man
 .SH "SEE ALSO"
diff --git a/ovsdb/ovsdb-client.c b/ovsdb/ovsdb-client.c
index 1f83f3b..452c416 100644
--- a/ovsdb/ovsdb-client.c
+++ b/ovsdb/ovsdb-client.c
@@ -174,7 +174,8 @@  parse_options(int argc, char *argv[])
         OPT_TIMESTAMP,
         VLOG_OPTION_ENUMS,
         DAEMON_OPTION_ENUMS,
-        TABLE_OPTION_ENUMS
+        TABLE_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option long_options[] = {
         {"help", no_argument, NULL, 'h'},
diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in
index d1ba83b..470645b 100644
--- a/ovsdb/ovsdb-server.1.in
+++ b/ovsdb/ovsdb-server.1.in
@@ -23,6 +23,7 @@  ovsdb\-server \- Open vSwitch database server
 .so lib/ssl-syn.man
 .so lib/ssl-bootstrap-syn.man
 .so lib/ssl-peer-ca-cert-syn.man
+.so lib/ssl-connect-syn.man
 .so lib/unixctl-syn.man
 .so lib/common-syn.man
 .
@@ -133,6 +134,8 @@  one row in \fItable\fR.)
 .so lib/ssl.man
 .so lib/ssl-bootstrap.man
 .so lib/ssl-peer-ca-cert.man
+.SS "SSL Connection Options"
+.so lib/ssl-connect.man
 .SS "Other Options"
 .so lib/unixctl.man
 .so lib/common.man
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
index e08c341..f34d0d8 100644
--- a/ovsdb/ovsdb-server.c
+++ b/ovsdb/ovsdb-server.c
@@ -66,6 +66,8 @@  struct db;
 static char *private_key_file;
 static char *certificate_file;
 static char *ca_cert_file;
+static char *ssl_protocols;
+static char *ssl_ciphers;
 static bool bootstrap_ca_cert;
 
 /* Replication configuration. */
@@ -1024,13 +1026,19 @@  reconfigure_ssl(const struct shash *all_dbs)
     const char *resolved_private_key;
     const char *resolved_certificate;
     const char *resolved_ca_cert;
+    const char *resolved_ssl_protocols;
+    const char *resolved_ssl_ciphers;
 
     resolved_private_key = query_db_string(all_dbs, private_key_file, &errors);
     resolved_certificate = query_db_string(all_dbs, certificate_file, &errors);
     resolved_ca_cert = query_db_string(all_dbs, ca_cert_file, &errors);
+    resolved_ssl_protocols = query_db_string(all_dbs, ssl_protocols, &errors);
+    resolved_ssl_ciphers = query_db_string(all_dbs, ssl_ciphers, &errors);
 
     stream_ssl_set_key_and_cert(resolved_private_key, resolved_certificate);
     stream_ssl_set_ca_cert_file(resolved_ca_cert, bootstrap_ca_cert);
+    stream_ssl_set_protocols(resolved_ssl_protocols);
+    stream_ssl_set_ciphers(resolved_ssl_ciphers);
 
     return errors.string;
 }
@@ -1384,7 +1392,8 @@  parse_options(int *argcp, char **argvp[],
         OPT_SYNC_FROM,
         OPT_SYNC_EXCLUDE,
         VLOG_OPTION_ENUMS,
-        DAEMON_OPTION_ENUMS
+        DAEMON_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option long_options[] = {
         {"remote",      required_argument, NULL, OPT_REMOTE},
@@ -1398,9 +1407,7 @@  parse_options(int *argcp, char **argvp[],
         VLOG_LONG_OPTIONS,
         {"bootstrap-ca-cert", required_argument, NULL, OPT_BOOTSTRAP_CA_CERT},
         {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
-        {"private-key", required_argument, NULL, 'p'},
-        {"certificate", required_argument, NULL, 'c'},
-        {"ca-cert",     required_argument, NULL, 'C'},
+        STREAM_SSL_LONG_OPTIONS,
         {"sync-from",   required_argument, NULL, OPT_SYNC_FROM},
         {"sync-exclude-tables", required_argument, NULL, OPT_SYNC_EXCLUDE},
         {NULL, 0, NULL, 0},
@@ -1454,6 +1461,14 @@  parse_options(int *argcp, char **argvp[],
             bootstrap_ca_cert = false;
             break;
 
+        case OPT_SSL_PROTOCOLS:
+            ssl_protocols = optarg;
+            break;
+
+        case OPT_SSL_CIPHERS:
+            ssl_ciphers = optarg;
+            break;
+
         case OPT_BOOTSTRAP_CA_CERT:
             ca_cert_file = optarg;
             bootstrap_ca_cert = true;
diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
index 0436de8..6347829 100644
--- a/tests/ovsdb-server.at
+++ b/tests/ovsdb-server.at
@@ -513,9 +513,13 @@  AT_DATA([schema],
          "columns": {
            "private_key": {"type": "string"},
            "certificate": {"type": "string"},
-           "ca_cert": {"type": "string"}}}}}
+           "ca_cert": {"type": "string"},
+           "ssl_protocols" : {"type": "string"},
+           "ssl_ciphers" : {"type" : "string"}}}}}
 ]])
 AT_CHECK([ovsdb-tool create db schema], [0], [stdout], [ignore])
+# The !ECDHE-ECDSA-AES256-GCM-SHA384 in the ssl_ciphers is so that
+# a cipher negotiation failure can be tested for later.
 AT_CHECK(
   [[ovsdb-tool transact db \
      '["mydb",
@@ -523,13 +527,17 @@  AT_CHECK(
         "table": "SSL",
         "row": {"private_key": "'"$PKIDIR/testpki-privkey2.pem"'",
                 "certificate": "'"$PKIDIR/testpki-cert2.pem"'",
-                "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'"}}]']],
+                "ca_cert": "'"$PKIDIR/testpki-cacert.pem"'",
+                "ssl_protocols": "'"TLSv1.2,TLSv1.1"'",
+                "ssl_ciphers": "'"HIGH:!aNULL:!MD5:!ECDHE-ECDSA-AES256-GCM-SHA384"'"}}]']],
   [0], [ignore], [ignore])
 AT_CHECK(
   [ovsdb-server --log-file --detach --no-chdir --pidfile="`pwd`"/pid \
         --private-key=db:mydb,SSL,private_key \
         --certificate=db:mydb,SSL,certificate \
         --ca-cert=db:mydb,SSL,ca_cert \
+        --ssl-protocols=db:mydb,SSL,ssl_protocols \
+        --ssl-ciphers=db:mydb,SSL,ssl_ciphers \
         --remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db],
   [0], [ignore], [ignore])
 PARSE_LISTENING_PORT([ovsdb-server.log], [SSL_PORT])
@@ -538,6 +546,8 @@  AT_CHECK(
         --private-key=$PKIDIR/testpki-privkey.pem \
         --certificate=$PKIDIR/testpki-cert.pem \
         --ca-cert=$PKIDIR/testpki-cacert.pem \
+        --ssl-protocols=TLSv1.2,TLSv1.1 \
+        --ssl-ciphers=HIGH:!aNULL:!MD5 \
         transact ssl:127.0.0.1:$SSL_PORT \
         '["mydb",
           {"op": "select",
@@ -550,6 +560,60 @@  AT_CHECK_UNQUOTED(
   [cat output], [0],
   [[@<:@{"rows":@<:@{"private_key":"$PKIDIR/testpki-privkey2.pem"}@:>@}@:>@
 ]], [ignore], [test ! -e pid || kill `cat pid`])
+# Check that when the server has TLSv1.1+ and the client has
+# TLSv1 that the connection fails.
+AT_CHECK(
+  [[ovsdb-client \
+        --private-key=$PKIDIR/testpki-privkey.pem \
+        --certificate=$PKIDIR/testpki-cert.pem \
+        --ca-cert=$PKIDIR/testpki-cacert.pem \
+        --ssl-protocols=TLSv1 \
+        --ssl-ciphers=HIGH:!aNULL:!MD5 \
+        transact ssl:127.0.0.1:$SSL_PORT \
+        '["mydb",
+          {"op": "select",
+           "table": "SSL",
+           "where": [],
+           "columns": ["private_key"]}]']], 
+  [1], [stdout], 
+  [stderr], 
+  [test ! -e pid || kill `cat pid`])
+cat stderr > output
+AT_CHECK_UNQUOTED(
+  [grep "failed to connect" output], [0],
+  [ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT" (Protocol error)
+], 
+  [ignore], [test ! -e pid || kill `cat pid`])
+# Check that when ciphers are not compatible, that a negotiation
+# failure occurs.
+AT_CHECK(
+  [[ovsdb-client \
+        --private-key=$PKIDIR/testpki-privkey.pem \
+        --certificate=$PKIDIR/testpki-cert.pem \
+        --ca-cert=$PKIDIR/testpki-cacert.pem \
+        --ssl-protocols=TLSv1.2,TLSv1.1 \
+        --ssl-ciphers=ECDHE-ECDSA-AES256-GCM-SHA384 \
+        transact ssl:127.0.0.1:$SSL_PORT \
+        '["mydb",
+          {"op": "select",
+           "table": "SSL",
+           "where": [],
+           "columns": ["private_key"]}]']], 
+  [1], [stdout], 
+  [stderr], 
+  [test ! -e pid || kill `cat pid`])
+cat stderr > output
+AT_CHECK_UNQUOTED(
+  [grep "failed to connect" output], [0],
+  [ovsdb-client: failed to connect to "ssl:127.0.0.1:$SSL_PORT" (Protocol error)
+], 
+  [ignore], [test ! -e pid || kill `cat pid`])
+# The error message for being unable to negotiate a shared ciphersuite
+# is 'sslv3 alert handshake failure'. This is not the clearest message.
+AT_CHECK_UNQUOTED(
+  [grep "sslv3 alert handshake failure" output], [0],
+  [stdout],
+  [ignore], [test ! -e pid || kill `cat pid`])
 OVSDB_SERVER_SHUTDOWN
 AT_CLEANUP
 
diff --git a/tests/test-jsonrpc.c b/tests/test-jsonrpc.c
index 684601a..a7ca691 100644
--- a/tests/test-jsonrpc.c
+++ b/tests/test-jsonrpc.c
@@ -55,7 +55,8 @@  parse_options(int argc, char *argv[])
 {
     enum {
         OPT_BOOTSTRAP_CA_CERT = UCHAR_MAX + 1,
-        DAEMON_OPTION_ENUMS
+        DAEMON_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option long_options[] = {
         {"verbose", optional_argument, NULL, 'v'},
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 6fd3818..fa2466e 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -190,7 +190,8 @@  parse_options(int argc, char *argv[])
         OPT_READ_ONLY,
         DAEMON_OPTION_ENUMS,
         OFP_VERSION_OPTION_ENUMS,
-        VLOG_OPTION_ENUMS
+        VLOG_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option long_options[] = {
         {"timeout", required_argument, NULL, 't'},
diff --git a/utilities/ovs-testcontroller.c b/utilities/ovs-testcontroller.c
index 1db3bbe..99f5bf8 100644
--- a/utilities/ovs-testcontroller.c
+++ b/utilities/ovs-testcontroller.c
@@ -258,7 +258,8 @@  parse_options(int argc, char *argv[])
         OPT_UNIXCTL,
         VLOG_OPTION_ENUMS,
         DAEMON_OPTION_ENUMS,
-        OFP_VERSION_OPTION_ENUMS
+        OFP_VERSION_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option long_options[] = {
         {"hub",         no_argument, NULL, 'H'},
diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c
index e710095..25f0e8a 100644
--- a/utilities/ovs-vsctl.c
+++ b/utilities/ovs-vsctl.c
@@ -207,7 +207,8 @@  parse_options(int argc, char *argv[], struct shash *local_options)
         OPT_COMMANDS,
         OPT_OPTIONS,
         VLOG_OPTION_ENUMS,
-        TABLE_OPTION_ENUMS
+        TABLE_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option global_long_options[] = {
         {"db", required_argument, NULL, OPT_DB},
diff --git a/vswitchd/ovs-vswitchd.c b/vswitchd/ovs-vswitchd.c
index 72448bb..0e3b6d0 100644
--- a/vswitchd/ovs-vswitchd.c
+++ b/vswitchd/ovs-vswitchd.c
@@ -145,6 +145,7 @@  parse_options(int argc, char *argv[], char **unixctl_pathp)
         OPT_DISABLE_SYSTEM,
         DAEMON_OPTION_ENUMS,
         OPT_DPDK,
+        SSL_OPTION_ENUMS,
         OPT_DUMMY_NUMA,
     };
     static const struct option long_options[] = {
diff --git a/vtep/vtep-ctl.c b/vtep/vtep-ctl.c
index 245ba0d..af20f83 100644
--- a/vtep/vtep-ctl.c
+++ b/vtep/vtep-ctl.c
@@ -166,7 +166,8 @@  parse_options(int argc, char *argv[], struct shash *local_options)
         OPT_PEER_CA_CERT,
         OPT_LOCAL,
         VLOG_OPTION_ENUMS,
-        TABLE_OPTION_ENUMS
+        TABLE_OPTION_ENUMS,
+        SSL_OPTION_ENUMS,
     };
     static const struct option global_long_options[] = {
         {"db", required_argument, NULL, OPT_DB},