X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=lib%2Fstream-ssl.c;h=c2a77a008a7840e679f9d4e68d4c3c5f80bf5930;hb=8bf4bbe390af3f370e7e95d9237572ff750047a8;hp=785829085046fb927a4d67b2297c362a29230d9d;hpb=415f6c0b1c61c4d957e14062ca4cf47a732e9a24;p=openvswitch diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c index 78582908..c2a77a00 100644 --- a/lib/stream-ssl.c +++ b/lib/stream-ssl.c @@ -37,7 +37,6 @@ #include "packets.h" #include "poll-loop.h" #include "socket-util.h" -#include "socket-util.h" #include "util.h" #include "stream-provider.h" #include "stream.h" @@ -125,6 +124,10 @@ struct ssl_stream * deadlock and livelock situations above. */ int rx_want, tx_want; + + /* A few bytes of header data in case SSL negotation fails. */ + uint8_t head[2]; + short int n_head; }; /* SSL context created by ssl_init(). */ @@ -133,8 +136,7 @@ static SSL_CTX *ctx; struct ssl_config_file { bool read; /* Whether the file was successfully read. */ char *file_name; /* Configured file name, if any. */ - long long int next_retry; /* If 'file_name' but not 'read', next time to - * retry reading. */ + struct timespec mtime; /* File mtime as of last time we read it. */ }; /* SSL configuration files. */ @@ -142,6 +144,11 @@ static struct ssl_config_file private_key; static struct ssl_config_file certificate; static struct ssl_config_file ca_cert; +/* 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 + * security risk.) */ +static bool verify_peer_cert = true; + /* Ordinarily, we require a CA certificate for the peer to be locally * available. We can, however, bootstrap the CA certificate from the peer at * the beginning of our first connection then use that certificate on all @@ -205,7 +212,7 @@ new_ssl_stream(const char *name, int fd, enum session_type type, VLOG_ERR("Certificate must be configured to use SSL"); retval = ENOPROTOOPT; } - if (!ca_cert.read && !bootstrap_ca_cert) { + if (!ca_cert.read && verify_peer_cert && !bootstrap_ca_cert) { VLOG_ERR("CA certificate must be configured to use SSL"); retval = ENOPROTOOPT; } @@ -244,7 +251,7 @@ new_ssl_stream(const char *name, int fd, enum session_type type, retval = ENOPROTOOPT; goto error; } - if (bootstrap_ca_cert && type == CLIENT) { + if (!verify_peer_cert || (bootstrap_ca_cert && type == CLIENT)) { SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); } @@ -261,6 +268,7 @@ new_ssl_stream(const char *name, int fd, enum session_type type, sslv->ssl = ssl; sslv->txbuf = NULL; sslv->rx_want = sslv->tx_want = SSL_NOTHING; + sslv->n_head = 0; *streamp = &sslv->stream; return 0; @@ -337,7 +345,7 @@ do_ca_cert_bootstrap(struct stream *stream) if (errno == EEXIST) { VLOG_INFO("reading CA cert %s created by another process", ca_cert.file_name); - stream_ssl_set_ca_cert_file__(ca_cert.file_name, true); + stream_ssl_set_ca_cert_file(ca_cert.file_name, true); return EPROTO; } else { VLOG_ERR("could not bootstrap CA cert: creating %s failed: %s", @@ -411,6 +419,13 @@ ssl_connect(struct stream *stream) /* Fall through. */ case STATE_SSL_CONNECTING: + /* Capture the first few bytes of received data so that we can guess + * what kind of funny data we've been sent if SSL negotation fails. */ + if (sslv->n_head <= 0) { + sslv->n_head = recv(sslv->fd, sslv->head, sizeof sslv->head, + MSG_PEEK); + } + retval = (sslv->type == CLIENT ? SSL_connect(sslv->ssl) : SSL_accept(sslv->ssl)); if (retval != 1) { @@ -422,13 +437,16 @@ ssl_connect(struct stream *stream) interpret_ssl_error((sslv->type == CLIENT ? "SSL_connect" : "SSL_accept"), retval, error, &unused); shutdown(sslv->fd, SHUT_RDWR); + stream_report_content(sslv->head, sslv->n_head, STREAM_SSL, + THIS_MODULE, stream_get_name(stream)); return EPROTO; } } else if (bootstrap_ca_cert) { return do_ca_cert_bootstrap(stream); - } else if ((SSL_get_verify_mode(sslv->ssl) - & (SSL_VERIFY_NONE | SSL_VERIFY_PEER)) - != SSL_VERIFY_PEER) { + } else if (verify_peer_cert + && ((SSL_get_verify_mode(sslv->ssl) + & (SSL_VERIFY_NONE | SSL_VERIFY_PEER)) + != SSL_VERIFY_PEER)) { /* Two or more SSL connections completed at the same time while we * were in bootstrap mode. Only one of these can finish the * bootstrap successfully. The other one(s) must be rejected @@ -460,6 +478,11 @@ ssl_close(struct stream *stream) * background. */ SSL_shutdown(sslv->ssl); + /* 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. */ + ERR_clear_error(); + SSL_free(sslv->ssl); close(sslv->fd); free(sslv); @@ -913,17 +936,28 @@ stream_ssl_is_configured(void) static bool update_ssl_config(struct ssl_config_file *config, const char *file_name) { - if (ssl_init() - || !file_name - || (config->file_name - && !strcmp(config->file_name, file_name) - && time_msec() < config->next_retry)) { + struct timespec mtime; + + if (ssl_init() || !file_name) { return false; } - config->next_retry = time_msec() + 60 * 1000; - free(config->file_name); - config->file_name = xstrdup(file_name); + /* If the file name hasn't changed and neither has the file contents, stop + * here. */ + get_mtime(file_name, &mtime); + if (config->file_name + && !strcmp(config->file_name, file_name) + && mtime.tv_sec == config->mtime.tv_sec + && mtime.tv_nsec == config->mtime.tv_nsec) { + return false; + } + + /* Update 'config'. */ + config->mtime = mtime; + if (file_name != config->file_name) { + free(config->file_name); + config->file_name = xstrdup(file_name); + } return true; } @@ -1079,7 +1113,11 @@ stream_ssl_set_ca_cert_file__(const char *file_name, bool bootstrap) size_t n_certs; struct stat s; - if (bootstrap && stat(file_name, &s) && errno == ENOENT) { + if (!strcmp(file_name, "none")) { + verify_peer_cert = false; + VLOG_WARN("Peer certificate validation disabled " + "(this is a security risk)"); + } else if (bootstrap && stat(file_name, &s) && errno == ENOENT) { bootstrap_ca_cert = true; } else if (!read_cert_file(file_name, &certs, &n_certs)) { size_t i;