|
@@ -44,7 +44,14 @@
|
|
|
#error "We require OpenSSL >= 0.9.7"
|
|
|
#endif
|
|
|
|
|
|
+#ifdef USE_BUFFEREVENTS
|
|
|
+#include <event2/bufferevent_ssl.h>
|
|
|
+#include <event2/buffer.h>
|
|
|
+#include "compat_libevent.h"
|
|
|
+#endif
|
|
|
+
|
|
|
#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */
|
|
|
+#define TORTLS_PRIVATE
|
|
|
|
|
|
#include "crypto.h"
|
|
|
#include "tortls.h"
|
|
@@ -107,6 +114,7 @@ struct tor_tls_t {
|
|
|
enum {
|
|
|
TOR_TLS_ST_HANDSHAKE, TOR_TLS_ST_OPEN, TOR_TLS_ST_GOTCLOSE,
|
|
|
TOR_TLS_ST_SENTCLOSE, TOR_TLS_ST_CLOSED, TOR_TLS_ST_RENEGOTIATE,
|
|
|
+ TOR_TLS_ST_BUFFEREVENT
|
|
|
} state : 3; /**< The current SSL state, depending on which operations have
|
|
|
* completed successfully. */
|
|
|
unsigned int isServer:1; /**< True iff this is a server-side connection */
|
|
@@ -1192,56 +1200,76 @@ tor_tls_handshake(tor_tls_t *tls)
|
|
|
}
|
|
|
if (r == TOR_TLS_DONE) {
|
|
|
tls->state = TOR_TLS_ST_OPEN;
|
|
|
- if (tls->isServer) {
|
|
|
- SSL_set_info_callback(tls->ssl, NULL);
|
|
|
- SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
|
|
|
- /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
|
|
|
- tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
|
|
|
+ return tor_tls_finish_handshake(tls);
|
|
|
+ }
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+int
|
|
|
+tor_tls_finish_handshake(tor_tls_t *tls)
|
|
|
+{
|
|
|
+ int r = TOR_TLS_DONE;
|
|
|
+ if (tls->isServer) {
|
|
|
+ SSL_set_info_callback(tls->ssl, NULL);
|
|
|
+ SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
|
|
|
+ /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
|
|
|
+ tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
|
|
|
#ifdef V2_HANDSHAKE_SERVER
|
|
|
- if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
|
|
|
- /* This check is redundant, but back when we did it in the callback,
|
|
|
- * we might have not been able to look up the tor_tls_t if the code
|
|
|
- * was buggy. Fixing that. */
|
|
|
- if (!tls->wasV2Handshake) {
|
|
|
- log_warn(LD_BUG, "For some reason, wasV2Handshake didn't"
|
|
|
- " get set. Fixing that.");
|
|
|
- }
|
|
|
- tls->wasV2Handshake = 1;
|
|
|
- log_debug(LD_HANDSHAKE,
|
|
|
- "Completed V2 TLS handshake with client; waiting "
|
|
|
- "for renegotiation.");
|
|
|
- } else {
|
|
|
- tls->wasV2Handshake = 0;
|
|
|
+ if (tor_tls_client_is_using_v2_ciphers(tls->ssl, ADDR(tls))) {
|
|
|
+ /* This check is redundant, but back when we did it in the callback,
|
|
|
+ * we might have not been able to look up the tor_tls_t if the code
|
|
|
+ * was buggy. Fixing that. */
|
|
|
+ if (!tls->wasV2Handshake) {
|
|
|
+ log_warn(LD_BUG, "For some reason, wasV2Handshake didn't"
|
|
|
+ " get set. Fixing that.");
|
|
|
}
|
|
|
-#endif
|
|
|
+ tls->wasV2Handshake = 1;
|
|
|
+ log_debug(LD_HANDSHAKE, "Completed V2 TLS handshake with client; waiting "
|
|
|
+ "for renegotiation.");
|
|
|
} else {
|
|
|
+ tls->wasV2Handshake = 0;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ } else {
|
|
|
#ifdef V2_HANDSHAKE_CLIENT
|
|
|
- /* If we got no ID cert, we're a v2 handshake. */
|
|
|
- X509 *cert = SSL_get_peer_certificate(tls->ssl);
|
|
|
- STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
|
|
|
- int n_certs = sk_X509_num(chain);
|
|
|
- if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) {
|
|
|
- log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it "
|
|
|
- "looks like a v1 handshake on %p", tls);
|
|
|
- tls->wasV2Handshake = 0;
|
|
|
- } else {
|
|
|
- log_debug(LD_HANDSHAKE,
|
|
|
- "Server sent back a single certificate; looks like "
|
|
|
- "a v2 handshake on %p.", tls);
|
|
|
- tls->wasV2Handshake = 1;
|
|
|
- }
|
|
|
- if (cert)
|
|
|
- X509_free(cert);
|
|
|
+ /* If we got no ID cert, we're a v2 handshake. */
|
|
|
+ X509 *cert = SSL_get_peer_certificate(tls->ssl);
|
|
|
+ STACK_OF(X509) *chain = SSL_get_peer_cert_chain(tls->ssl);
|
|
|
+ int n_certs = sk_X509_num(chain);
|
|
|
+ if (n_certs > 1 || (n_certs == 1 && cert != sk_X509_value(chain, 0))) {
|
|
|
+ log_debug(LD_HANDSHAKE, "Server sent back multiple certificates; it "
|
|
|
+ "looks like a v1 handshake on %p", tls);
|
|
|
+ tls->wasV2Handshake = 0;
|
|
|
+ } else {
|
|
|
+ log_debug(LD_HANDSHAKE,
|
|
|
+ "Server sent back a single certificate; looks like "
|
|
|
+ "a v2 handshake on %p.", tls);
|
|
|
+ tls->wasV2Handshake = 1;
|
|
|
+ }
|
|
|
+ if (cert)
|
|
|
+ X509_free(cert);
|
|
|
#endif
|
|
|
- if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
|
|
|
- tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
|
|
|
- r = TOR_TLS_ERROR_MISC;
|
|
|
- }
|
|
|
+ if (SSL_set_cipher_list(tls->ssl, SERVER_CIPHER_LIST) == 0) {
|
|
|
+ tls_log_errors(NULL, LOG_WARN, LD_HANDSHAKE, "re-setting ciphers");
|
|
|
+ r = TOR_TLS_ERROR_MISC;
|
|
|
}
|
|
|
}
|
|
|
return r;
|
|
|
}
|
|
|
|
|
|
+#ifdef USE_BUFFEREVENTS
|
|
|
+/** DOCDOC */
|
|
|
+int
|
|
|
+tor_tls_start_renegotiating(tor_tls_t *tls)
|
|
|
+{
|
|
|
+ int r = SSL_renegotiate(tls->ssl);
|
|
|
+ if (r <= 0) {
|
|
|
+ return tor_tls_get_error(tls, r, 0, "renegotiating", LOG_WARN, LD_HANDSHAKE);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
/** Client only: Renegotiate a TLS session. When finished, returns
|
|
|
* TOR_TLS_DONE. On failure, returns TOR_TLS_ERROR, TOR_TLS_WANTREAD, or
|
|
|
* TOR_TLS_WANTWRITE.
|
|
@@ -1458,6 +1486,8 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
|
|
|
log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
|
|
|
goto done;
|
|
|
}
|
|
|
+ tls_log_errors(tls, severity, LD_HANDSHAKE, "before verifying certificate");
|
|
|
+
|
|
|
if (!(id_pkey = X509_get_pubkey(id_cert)) ||
|
|
|
X509_verify(cert, id_pkey) <= 0) {
|
|
|
log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
|
|
@@ -1629,3 +1659,43 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
|
|
|
*wbuf_bytes = tls->ssl->s3->wbuf.left;
|
|
|
}
|
|
|
|
|
|
+#ifdef USE_BUFFEREVENTS
|
|
|
+/** DOCDOC may free bufev_in */
|
|
|
+struct bufferevent *
|
|
|
+tor_tls_init_bufferevent(tor_tls_t *tls, struct bufferevent *bufev_in,
|
|
|
+ evutil_socket_t socket, int receiving)
|
|
|
+{
|
|
|
+ struct bufferevent *out;
|
|
|
+ const enum bufferevent_ssl_state state = receiving ?
|
|
|
+ BUFFEREVENT_SSL_ACCEPTING : BUFFEREVENT_SSL_CONNECTING;
|
|
|
+
|
|
|
+#if 0
|
|
|
+ (void) socket;
|
|
|
+ out = bufferevent_openssl_filter_new(tor_libevent_get_base(),
|
|
|
+ bufev_in,
|
|
|
+ tls->ssl,
|
|
|
+ state,
|
|
|
+ BEV_OPT_DEFER_CALLBACKS);
|
|
|
+#else
|
|
|
+ /* Disabled: just use filter for now. */
|
|
|
+ if (bufev_in) {
|
|
|
+ evutil_socket_t s = bufferevent_getfd(bufev_in);
|
|
|
+ tor_assert(s == -1 || s == socket);
|
|
|
+ tor_assert(evbuffer_get_length(bufferevent_get_input(bufev_in)) == 0);
|
|
|
+ tor_assert(evbuffer_get_length(bufferevent_get_output(bufev_in)) == 0);
|
|
|
+ tor_assert(BIO_number_read(SSL_get_rbio(tls->ssl)) == 0);
|
|
|
+ tor_assert(BIO_number_written(SSL_get_rbio(tls->ssl)) == 0);
|
|
|
+ }
|
|
|
+ tls->state = TOR_TLS_ST_BUFFEREVENT;
|
|
|
+ bufferevent_free(bufev_in);
|
|
|
+ out = bufferevent_openssl_socket_new(tor_libevent_get_base(),
|
|
|
+ socket,
|
|
|
+ tls->ssl,
|
|
|
+ state,
|
|
|
+ 0);
|
|
|
+ //BEV_OPT_DEFER_CALLBACKS);
|
|
|
+#endif
|
|
|
+ return out;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|