📄 tortls.c
字号:
TLS1_TXT_ECDH_RSA_WITH_AES_128_CBC_SHA":" \
TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA ":" \
TLS1_TXT_ECDH_ECDSA_WITH_AES_128_CBC_SHA ":" \
SSL3_TXT_RSA_RC4_128_MD5 ":" \
SSL3_TXT_RSA_RC4_128_SHA ":" \
TLS1_TXT_RSA_WITH_AES_128_SHA ":" \
TLS1_TXT_ECDHE_ECDSA_WITH_DES_192_CBC3_SHA ":" \
TLS1_TXT_ECDHE_RSA_WITH_DES_192_CBC3_SHA ":" \
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA ":" \
SSL3_TXT_EDH_DSS_DES_192_CBC3_SHA ":" \
TLS1_TXT_ECDH_RSA_WITH_DES_192_CBC3_SHA ":" \
TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA ":" \
/*SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA ":"*/ \
SSL3_TXT_RSA_DES_192_CBC3_SHA)
/* SSL3_TXT_RSA_FIPS_WITH_3DES_EDE_CBC_SHA is commented out because it doesn't
* really exist; if I understand correctly, it's a bit of silliness that
* netscape did on its own before any standard for what they wanted was
* formally approved. Nonetheless, Firefox still uses it, so we need to
* fake it at some point soon. XXXX021 -NM */
#else
/* Ug. We don't have as many ciphers with openssl 0.9.7 as we'd like. Fix
* this list into something that sucks less. */
#define CLIENT_CIPHER_LIST \
(TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \
TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA ":" \
SSL3_TXT_RSA_RC4_128_SHA)
#endif
#ifndef V2_HANDSHAKE_CLIENT
#undef CLIENT_CIPHER_LIST
#define CLIENT_CIPHER_LIST (TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
#endif
/** Remove a reference to <b>ctx</b>, and free it if it has no more
* references. */
static void
tor_tls_context_decref(tor_tls_context_t *ctx)
{
tor_assert(ctx);
if (--ctx->refcnt == 0) {
SSL_CTX_free(ctx->ctx);
X509_free(ctx->my_cert);
X509_free(ctx->my_id_cert);
crypto_free_pk_env(ctx->key);
tor_free(ctx);
}
}
/** Increase the reference count of <b>ctx</b>. */
static void
tor_tls_context_incref(tor_tls_context_t *ctx)
{
++ctx->refcnt;
}
/** Create a new TLS context for use with Tor TLS handshakes.
* <b>identity</b> should be set to the identity key used to sign the
* certificate, and <b>nickname</b> set to the nickname to use.
*
* You can call this function multiple times. Each time you call it,
* it generates new certificates; all new connections will use
* the new SSL context.
*/
int
tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime)
{
crypto_pk_env_t *rsa = NULL;
crypto_dh_env_t *dh = NULL;
EVP_PKEY *pkey = NULL;
tor_tls_context_t *result = NULL;
X509 *cert = NULL, *idcert = NULL;
char *nickname = NULL, *nn2 = NULL;
tor_tls_init();
nickname = crypto_random_hostname(8, 20, "www.", ".net");
nn2 = crypto_random_hostname(8, 20, "www.", ".net");
/* Generate short-term RSA key. */
if (!(rsa = crypto_new_pk_env()))
goto error;
if (crypto_pk_generate_key(rsa)<0)
goto error;
/* Create certificate signed by identity key. */
cert = tor_tls_create_certificate(rsa, identity, nickname, nn2,
key_lifetime);
/* Create self-signed certificate for identity key. */
idcert = tor_tls_create_certificate(identity, identity, nn2, nn2,
IDENTITY_CERT_LIFETIME);
if (!cert || !idcert) {
log(LOG_WARN, LD_CRYPTO, "Error creating certificate");
goto error;
}
result = tor_malloc_zero(sizeof(tor_tls_context_t));
result->refcnt = 1;
result->my_cert = X509_dup(cert);
result->my_id_cert = X509_dup(idcert);
result->key = crypto_pk_dup_key(rsa);
#ifdef EVERYONE_HAS_AES
/* Tell OpenSSL to only use TLS1 */
if (!(result->ctx = SSL_CTX_new(TLSv1_method())))
goto error;
#else
/* Tell OpenSSL to use SSL3 or TLS1 but not SSL2. */
if (!(result->ctx = SSL_CTX_new(SSLv23_method())))
goto error;
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
#endif
SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE);
if (cert && !SSL_CTX_use_certificate(result->ctx,cert))
goto error;
X509_free(cert); /* We just added a reference to cert. */
cert=NULL;
if (idcert) {
X509_STORE *s = SSL_CTX_get_cert_store(result->ctx);
tor_assert(s);
X509_STORE_add_cert(s, idcert);
X509_free(idcert); /* The context now owns the reference to idcert */
idcert = NULL;
}
SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF);
tor_assert(rsa);
if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,1)))
goto error;
if (!SSL_CTX_use_PrivateKey(result->ctx, pkey))
goto error;
EVP_PKEY_free(pkey);
pkey = NULL;
if (!SSL_CTX_check_private_key(result->ctx))
goto error;
dh = crypto_dh_new();
SSL_CTX_set_tmp_dh(result->ctx, _crypto_dh_env_get_dh(dh));
crypto_dh_free(dh);
SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
always_accept_verify_cb);
/* let us realloc bufs that we're writing from */
SSL_CTX_set_mode(result->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
/* Free the old context if one exists. */
if (global_tls_context) {
/* This is safe even if there are open connections: OpenSSL does
* reference counting with SSL and SSL_CTX objects. */
tor_tls_context_decref(global_tls_context);
}
global_tls_context = result;
if (rsa)
crypto_free_pk_env(rsa);
tor_free(nickname);
tor_free(nn2);
return 0;
error:
tls_log_errors(NULL, LOG_WARN, "creating TLS context");
tor_free(nickname);
tor_free(nn2);
if (pkey)
EVP_PKEY_free(pkey);
if (rsa)
crypto_free_pk_env(rsa);
if (dh)
crypto_dh_free(dh);
if (result)
tor_tls_context_decref(result);
if (cert)
X509_free(cert);
if (idcert)
X509_free(idcert);
return -1;
}
#ifdef V2_HANDSHAKE_SERVER
/** Return true iff the cipher list suggested by the client for <b>ssl</b> is
* a list that indicates that the client knows how to do the v2 TLS connection
* handshake. */
static int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl, const char *address)
{
int i;
SSL_SESSION *session;
/* If we reached this point, we just got a client hello. See if there is
* a cipher list. */
if (!(session = SSL_get_session((SSL *)ssl))) {
log_warn(LD_NET, "No session on TLS?");
return 0;
}
if (!session->ciphers) {
log_warn(LD_NET, "No ciphers on session");
return 0;
}
/* Now we need to see if there are any ciphers whose presence means we're
* dealing with an updated Tor. */
for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) {
SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i);
const char *ciphername = SSL_CIPHER_get_name(cipher);
if (strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA) &&
strcmp(ciphername, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA) &&
strcmp(ciphername, SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA) &&
strcmp(ciphername, "(NONE)")) {
/* XXXX should be ld_debug */
log_info(LD_NET, "Got a non-version-1 cipher called '%s'", ciphername);
// return 1;
goto dump_list;
}
}
return 0;
dump_list:
{
smartlist_t *elts = smartlist_create();
char *s;
for (i = 0; i < sk_SSL_CIPHER_num(session->ciphers); ++i) {
SSL_CIPHER *cipher = sk_SSL_CIPHER_value(session->ciphers, i);
const char *ciphername = SSL_CIPHER_get_name(cipher);
smartlist_add(elts, (char*)ciphername);
}
s = smartlist_join_strings(elts, ":", 0, NULL);
log_info(LD_NET, "Got a non-version-1 cipher list from %s. It is: '%s'",
address, s);
tor_free(s);
smartlist_free(elts);
}
return 1;
}
/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
* changes state. We use this:
* <ul><li>To alter the state of the handshake partway through, so we
* do not send or request extra certificates in v2 handshakes.</li>
* <li>To detect renegotiation</li></ul>
*/
static void
tor_tls_server_info_callback(const SSL *ssl, int type, int val)
{
tor_tls_t *tls;
(void) val;
if (type != SSL_CB_ACCEPT_LOOP)
return;
if (ssl->state != SSL3_ST_SW_SRVR_HELLO_A)
return;
tls = tor_tls_get_by_ssl(ssl);
if (tls) {
/* Check whether we're watching for renegotiates. If so, this is one! */
if (tls->negotiated_callback)
tls->got_renegotiate = 1;
} else {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
}
/* Now check the cipher list. */
if (tor_tls_client_is_using_v2_ciphers(ssl, ADDR(tls))) {
/*XXXX_TLS keep this from happening more than once! */
/* Yes, we're casting away the const from ssl. This is very naughty of us.
* Let's hope openssl doesn't notice! */
/* Set SSL_MODE_NO_AUTO_CHAIN to keep from sending back any extra certs. */
SSL_set_mode((SSL*) ssl, SSL_MODE_NO_AUTO_CHAIN);
/* Don't send a hello request. */
SSL_set_verify((SSL*) ssl, SSL_VERIFY_NONE, NULL);
if (tls) {
tls->wasV2Handshake = 1;
} else {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
}
}
}
#endif
/** Create a new TLS object from a file descriptor, and a flag to
* determine whether it is functioning as a server.
*/
tor_tls_t *
tor_tls_new(int sock, int isServer)
{
BIO *bio = NULL;
tor_tls_t *result = tor_malloc_zero(sizeof(tor_tls_t));
tor_assert(global_tls_context); /* make sure somebody made it first */
if (!(result->ssl = SSL_new(global_tls_context->ctx))) {
tls_log_errors(NULL, LOG_WARN, "generating TLS context");
tor_free(result);
return NULL;
}
if (!SSL_set_cipher_list(result->ssl,
isServer ? SERVER_CIPHER_LIST : CLIENT_CIPHER_LIST)) {
SSL_free(result->ssl);
tor_free(result);
return NULL;
}
result->socket = sock;
#ifdef USE_BSOCKETS
bio = BIO_new_bsocket(sock, BIO_NOCLOSE);
#else
bio = BIO_new_socket(sock, BIO_NOCLOSE);
#endif
if (! bio) {
tls_log_errors(NULL, LOG_WARN, "opening BIO");
SSL_free(result->ssl);
tor_free(result);
return NULL;
}
HT_INSERT(tlsmap, &tlsmap_root, result);
SSL_set_bio(result->ssl, bio, bio);
tor_tls_context_incref(global_tls_context);
result->context = global_tls_context;
result->state = TOR_TLS_ST_HANDSHAKE;
result->isServer = isServer;
result->wantwrite_n = 0;
result->last_write_count = BIO_number_written(bio);
result->last_read_count = BIO_number_read(bio);
if (result->last_write_count || result->last_read_count) {
log_warn(LD_NET, "Newly created BIO has read count %lu, write count %lu",
result->last_read_count, result->last_write_count);
}
#ifdef V2_HANDSHAKE_SERVER
if (isServer) {
SSL_set_info_callback(result->ssl, tor_tls_server_info_callback);
}
#endif
/* Not expected to get called. */
tls_log_errors(NULL, LOG_WARN, "generating TLS context");
return result;
}
/** Make future log messages about <b>tls</b> display the address
* <b>address</b>.
*/
void
tor_tls_set_logged_address(tor_tls_t *tls, const char *address)
{
tor_assert(tls);
tor_free(tls->address);
tls->address = tor_strdup(address);
}
/** Set <b>cb</b> to be called with argument <b>arg</b> whenever <b>tls</b>
* next gets a client-side renegotiate in the middle of a read. Do not
* invoke this function untile <em>after</em> initial handshaking is done!
*/
void
tor_tls_set_renegotiate_callback(tor_tls_t *tls,
void (*cb)(tor_tls_t *, void *arg),
void *arg)
{
tls->negotiated_callback = cb;
tls->callback_arg = arg;
tls->got_renegotiate = 0;
#ifdef V2_HANDSHAKE_SERVER
if (cb) {
SSL_set_info_callback(tls->ssl, tor_tls_server_info_callback);
} else {
SSL_set_info_callback(tls->ssl, NULL);
}
#endif
}
/** Return whether this tls initiated the connect (client) or
* received it (server). */
int
tor_tls_is_server(tor_tls_t *tls)
{
tor_assert(tls);
return tls->isServer;
}
/** Release resources associated with a TLS object. Does not close the
* underlying file descriptor.
*/
void
tor_tls_free(tor_tls_t *tls)
{
tor_tls_t *removed;
tor_assert(tls && tls->ssl);
removed = HT_REMOVE(tlsmap, &tlsmap_root, tls);
if (!removed) {
log_warn(LD_BUG, "Freeing a TLS that was not in the ssl->tls map.");
}
SSL_free(tls->ssl);
tls->ssl = NULL;
tls->negotiated_callback = NULL;
if (tls->context)
tor_tls_context_decref(tls->context);
tor_free(tls->address);
tor_free(tls);
}
/** Underlying function for TLS reading. Reads up to <b>len</b>
* characters from <b>tls</b> into <b>cp</b>. On success, returns the
* number of characters read. On failure, returns TOR_TLS_ERROR,
* TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
*/
int
tor_tls_read(tor_tls_t *tls, char *cp, size_t len)
{
int r, err;
tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_OPEN);
tor_assert(len<INT_MAX);
r = SSL_read(tls->ssl, cp, (int)len);
if (r > 0) {
#ifdef V2_HANDSHAKE_SERVER
if (tls->got_renegotiate) {
/* Renegotiation happened! */
log_info(LD_NET, "Got a TLS renegotiation from %s", ADDR(tls));
if (tls->negotiated_callback)
tls->negotiated_callback(tls, tls->callback_arg);
tls->got_renegotiate = 0;
}
#endif
return r;
}
err = tor_tls_get_error(tls, r, CATCH_ZERO, "reading", LOG_DEBUG);
if (err == _TOR_TLS_ZERORETURN || err == TOR_TLS_CLOSE) {
log_debug(LD_NET,"read returned r=%d; TLS is closed",r);
tls->state = TOR_TLS_ST_CLOSED;
return TOR_TLS_CLOSE;
} else {
tor_assert(err != TOR_TLS_DONE);
log_debug(LD_NET,"read returned r=%d, err=%d",r,err);
return err;
}
}
/** Underlying function for TLS writing. Write up to <b>n</b>
* characters from <b>cp</b> onto <b>tls</b>. On success, returns the
* number of characters written. On failure, returns TOR_TLS_ERROR,
* TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
*/
int
tor_tls_write(tor_tls_t *tls, const char *cp, size_t n)
{
int r, err;
tor_assert(tls);
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_OPEN);
tor_assert(n < INT_MAX);
if (n == 0)
return 0;
if (tls->wantwrite_n) {
/* if WANTWRITE last time, we must use the _same_ n as before */
tor_assert(n >= tls->wantwrite_n);
log_debug(LD_NET,"resuming pending-write, (%d to flush, reusing %d)",
(int)n, (int)tls->wantwrite_n);
n = tls->wantwrite_n;
tls->wantwrite_n = 0;
}
r = SSL_write(tls->ssl, cp, (int)n);
err = tor_tls_get_error(tls, r, 0, "writing", LOG_INFO);
if (err == TOR_TLS_DONE) {
return r;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -