X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fstream-ssl.c;h=1944cb9ed302dae7460764ddbd8cbb9c21ed8182;hb=4f1361e8572832bcd5fa52bc0ec66234b49ac777;hp=27c9d4c92de6a440df9e41e8ada5978953f4a992;hpb=26efd2563bf76547cf38da49ec83997e091b2517;p=openvswitch diff --git a/lib/stream-ssl.c b/lib/stream-ssl.c index 27c9d4c9..1944cb9e 100644 --- a/lib/stream-ssl.c +++ b/lib/stream-ssl.c @@ -66,6 +66,7 @@ struct ssl_stream int fd; SSL *ssl; struct ofpbuf *txbuf; + unsigned int session_nr; /* rx_want and tx_want record the result of the last call to SSL_read() * and SSL_write(), respectively: @@ -124,6 +125,10 @@ struct ssl_stream * deadlock and livelock situations above. */ int rx_want, tx_want; + + /* A few bytes of header data in case SSL negotiation fails. */ + uint8_t head[2]; + short int n_head; }; /* SSL context created by ssl_init(). */ @@ -152,6 +157,10 @@ static bool verify_peer_cert = true; * this case, 'bootstrap_ca_cert' is true. */ static bool bootstrap_ca_cert; +/* Session number. Used in debug logging messages to uniquely identify a + * session. */ +static unsigned int next_session_nr; + /* Who knows what can trigger various SSL errors, so let's throttle them down * quite a bit. */ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 25); @@ -167,6 +176,8 @@ static DH *tmp_dh_callback(SSL *ssl, int is_export OVS_UNUSED, int keylength); static void log_ca_cert(const char *file_name, X509 *cert); static void stream_ssl_set_ca_cert_file__(const char *file_name, bool bootstrap); +static void ssl_protocol_cb(int write_p, int version, int content_type, + const void *, size_t, SSL *, void *sslv_); static short int want_to_poll_events(int want) @@ -264,6 +275,14 @@ 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->session_nr = next_session_nr++; + sslv->n_head = 0; + + if (VLOG_IS_DBG_ENABLED()) { + SSL_set_msg_callback(ssl, ssl_protocol_cb); + SSL_set_msg_callback_arg(ssl, sslv); + } + *streamp = &sslv->stream; return 0; @@ -414,6 +433,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) { @@ -425,6 +451,8 @@ 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) { @@ -464,6 +492,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); @@ -1144,5 +1177,98 @@ stream_ssl_set_ca_cert_file(const char *file_name, bool bootstrap) stream_ssl_set_ca_cert_file__(file_name, bootstrap); } + +/* SSL protocol logging. */ + +static const char * +ssl_alert_level_to_string(uint8_t type) +{ + switch (type) { + case 1: return "warning"; + case 2: return "fatal"; + default: return ""; + } +} +static const char * +ssl_alert_description_to_string(uint8_t type) +{ + switch (type) { + case 0: return "close_notify"; + case 10: return "unexpected_message"; + case 20: return "bad_record_mac"; + case 21: return "decryption_failed"; + case 22: return "record_overflow"; + case 30: return "decompression_failure"; + case 40: return "handshake_failure"; + case 42: return "bad_certificate"; + case 43: return "unsupported_certificate"; + case 44: return "certificate_revoked"; + case 45: return "certificate_expired"; + case 46: return "certificate_unknown"; + case 47: return "illegal_parameter"; + case 48: return "unknown_ca"; + case 49: return "access_denied"; + case 50: return "decode_error"; + case 51: return "decrypt_error"; + case 60: return "export_restriction"; + case 70: return "protocol_version"; + case 71: return "insufficient_security"; + case 80: return "internal_error"; + case 90: return "user_canceled"; + case 100: return "no_renegotiation"; + default: return ""; + } +} +static const char * +ssl_handshake_type_to_string(uint8_t type) +{ + switch (type) { + case 0: return "hello_request"; + case 1: return "client_hello"; + case 2: return "server_hello"; + case 11: return "certificate"; + case 12: return "server_key_exchange"; + case 13: return "certificate_request"; + case 14: return "server_hello_done"; + case 15: return "certificate_verify"; + case 16: return "client_key_exchange"; + case 20: return "finished"; + default: return ""; + } +} + +static void +ssl_protocol_cb(int write_p, int version OVS_UNUSED, int content_type, + const void *buf_, size_t len, SSL *ssl OVS_UNUSED, void *sslv_) +{ + const struct ssl_stream *sslv = sslv_; + const uint8_t *buf = buf_; + struct ds details; + + if (!VLOG_IS_DBG_ENABLED()) { + return; + } + + ds_init(&details); + if (content_type == 20) { + ds_put_cstr(&details, "change_cipher_spec"); + } else if (content_type == 21) { + ds_put_format(&details, "alert: %s, %s", + ssl_alert_level_to_string(buf[0]), + ssl_alert_description_to_string(buf[1])); + } else if (content_type == 22) { + ds_put_format(&details, "handshake: %s", + ssl_handshake_type_to_string(buf[0])); + } else { + ds_put_format(&details, "type %d", content_type); + } + + VLOG_DBG("%s%u%s%s %s (%zu bytes)", + sslv->type == CLIENT ? "client" : "server", + sslv->session_nr, write_p ? "-->" : "<--", + stream_get_name(&sslv->stream), ds_cstr(&details), len); + + ds_destroy(&details); +}