X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fstream-ssl.c;h=4874bbe482a79b9bc1b80b7ee7d7595bc8e74f26;hb=d55c25660b7dbedda886207a303895609c8407ef;hp=9c7533d1e6346b8e4634f0220262d852dd43c773;hpb=2a022368f4b37559de5d5621a88c648023493f75;p=sliver-openvswitch.git diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c index 9c7533d1e..4874bbe48 100644 --- a/lib/stream-ssl.c +++ b/lib/stream-ssl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -45,7 +47,10 @@ #include "timeval.h" #include "vlog.h" -VLOG_DEFINE_THIS_MODULE(stream_ssl) +VLOG_DEFINE_THIS_MODULE(stream_ssl); + +COVERAGE_DEFINE(ssl_session); +COVERAGE_DEFINE(ssl_session_reused); /* Active SSL. */ @@ -185,6 +190,7 @@ static int do_ssl_init(void); static bool ssl_wants_io(int ssl_error); static void ssl_close(struct stream *); static void ssl_clear_txbuf(struct ssl_stream *); +static void interpret_queued_ssl_error(const char *function); static int interpret_ssl_error(const char *function, int ret, int error, int *want); static DH *tmp_dh_callback(SSL *ssl, int is_export OVS_UNUSED, int keylength); @@ -276,6 +282,13 @@ new_ssl_stream(const 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 (type == CLIENT) { + /* Grab SSL session information from the cache. */ + SSL_SESSION *session = shash_find_data(&client_sessions, name); + if (session && SSL_set_session(ssl, session) != 1) { + interpret_queued_ssl_error("SSL_set_session"); + } + } /* Create and return the ssl_stream. */ sslv = xmalloc(sizeof *sslv); @@ -460,12 +473,6 @@ ssl_cache_session(struct stream *stream) struct ssl_stream *sslv = ssl_stream_cast(stream); SSL_SESSION *session; - /* Statistics. */ - COVERAGE_INC(ssl_session); - if (SSL_session_reused(sslv->ssl)) { - COVERAGE_INC(ssl_session_reused); - } - /* Get session from stream. */ session = SSL_get1_session(sslv->ssl); if (session) { @@ -487,12 +494,6 @@ ssl_cache_session(struct stream *stream) } } } - } else { - /* There is no new session. This doesn't really make sense because - * this function is only called upon successful connection and there - * should always be a new session in that case. But I don't trust - * OpenSSL so I'd rather handle this case anyway. */ - ssl_flush_session(stream); } } @@ -519,15 +520,6 @@ ssl_connect(struct stream *stream) MSG_PEEK); } - /* Grab SSL session information from the cache. */ - if (sslv->type == CLIENT) { - SSL_SESSION *session = shash_find_data(&client_sessions, - stream_get_name(stream)); - if (session) { - SSL_set_session(sslv->ssl, session); - } - } - retval = (sslv->type == CLIENT ? SSL_connect(sslv->ssl) : SSL_accept(sslv->ssl)); if (retval != 1) { @@ -572,8 +564,10 @@ ssl_connect(struct stream *stream) VLOG_ERR("rejecting SSL connection during bootstrap race window"); return EPROTO; } else { - if (sslv->type == CLIENT) { - ssl_cache_session(stream); + /* Statistics. */ + COVERAGE_INC(ssl_session); + if (SSL_session_reused(sslv->ssl)) { + COVERAGE_INC(ssl_session_reused); } return 0; } @@ -595,6 +589,8 @@ ssl_close(struct stream *stream) * background. */ SSL_shutdown(sslv->ssl); + ssl_cache_session(stream); + /* SSL_shutdown() might have signaled an error, in which case we need to * flush it out of the OpenSSL error queue or the next OpenSSL operation * will falsely signal an error. */ @@ -605,6 +601,18 @@ ssl_close(struct stream *stream) free(sslv); } +static void +interpret_queued_ssl_error(const char *function) +{ + int queued_error = ERR_get_error(); + if (queued_error != 0) { + VLOG_WARN_RL(&rl, "%s: %s", + function, ERR_error_string(queued_error, NULL)); + } else { + VLOG_ERR_RL(&rl, "%s: SSL_ERROR_SSL without queued error", function); + } +} + static int interpret_ssl_error(const char *function, int ret, int error, int *want) @@ -661,17 +669,9 @@ interpret_ssl_error(const char *function, int ret, int error, } } - case SSL_ERROR_SSL: { - int queued_error = ERR_get_error(); - if (queued_error != 0) { - VLOG_WARN_RL(&rl, "%s: %s", - function, ERR_error_string(queued_error, NULL)); - } else { - VLOG_ERR_RL(&rl, "%s: SSL_ERROR_SSL without queued error", - function); - } + case SSL_ERROR_SSL: + interpret_queued_ssl_error(function); break; - } default: VLOG_ERR_RL(&rl, "%s: bad SSL error code %d", function, error); @@ -988,7 +988,9 @@ do_ssl_init(void) SSL_library_init(); SSL_load_error_strings(); - method = TLSv1_method(); + /* New OpenSSL changed TLSv1_method() to return a "const" pointer, so the + * cast is needed to avoid a warning with those newer versions. */ + method = (SSL_METHOD *) TLSv1_method(); if (method == NULL) { VLOG_ERR("TLSv1_method: %s", ERR_error_string(ERR_get_error(), NULL)); return ENOPROTOOPT; @@ -1006,6 +1008,17 @@ do_ssl_init(void) SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + /* We have to set a session context ID string in 'ctx' because OpenSSL + * otherwise refuses to use a cached session on the server side when + * SSL_VERIFY_PEER is set. And it not only refuses to use the cached + * session, it actually generates an error and kills the connection. + * According to a comment in ssl_get_prev_session() in OpenSSL's + * ssl/ssl_sess.c, this is intentional behavior. + * + * Any context string is OK, as long as one is set. */ + SSL_CTX_set_session_id_context(ctx, (const unsigned char *) PACKAGE, + strlen(PACKAGE)); + return 0; } @@ -1286,7 +1299,7 @@ stream_ssl_set_ca_cert_file__(const char *file_name, bool bootstrap) for (i = 0; i < n_certs; i++) { /* SSL_CTX_add_client_CA makes a copy of the relevant data. */ if (SSL_CTX_add_client_CA(ctx, certs[i]) != 1) { - VLOG_ERR("failed to add client certificate %d from %s: %s", + VLOG_ERR("failed to add client certificate %zu from %s: %s", i, file_name, ERR_error_string(ERR_get_error(), NULL)); } else {