📄 tortls.c
字号:
/* Copyright (c) 2003, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char tortls_c_id[] =
"$Id$";
/**
* \file tortls.c
* \brief Wrapper functions to present a consistent interface to
* TLS, SSL, and X.509 functions from OpenSSL.
**/
/* (Unlike other tor functions, these
* are prefixed with tor_ in order to avoid conflicting with OpenSSL
* functions and variables.)
*/
#include "orconfig.h"
#include <assert.h>
#include <openssl/ssl.h>
#include <openssl/ssl3.h>
#include <openssl/err.h>
#include <openssl/tls1.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER < 0x00907000l
#error "We require openssl >= 0.9.7"
#endif
#define CRYPTO_PRIVATE /* to import prototypes from crypto.h */
#include "crypto.h"
#include "tortls.h"
#include "util.h"
#include "log.h"
#include "container.h"
#include "ht.h"
#include <string.h>
/* Enable the "v2" TLS handshake.
*/
#define V2_HANDSHAKE_SERVER
#define V2_HANDSHAKE_CLIENT
/* Copied from or.h */
#define LEGAL_NICKNAME_CHARACTERS \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
/** How long do identity certificates live? (sec) */
#define IDENTITY_CERT_LIFETIME (365*24*60*60)
#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer")
/** Structure holding the TLS state for a single connection. */
typedef struct tor_tls_context_t {
int refcnt;
SSL_CTX *ctx;
X509 *my_cert;
X509 *my_id_cert;
crypto_pk_env_t *key;
} tor_tls_context_t;
/** Holds a SSL object and its associated data. Members are only
* accessed from within tortls.c.
*/
struct tor_tls_t {
HT_ENTRY(tor_tls_t) node;
tor_tls_context_t *context; /** A link to the context object for this tls */
SSL *ssl; /**< An OpenSSL SSL object. */
int socket; /**< The underlying file descriptor for this TLS connection. */
char *address; /**< An address to log when describing this connectinon. */
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,
} 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 */
unsigned int wasV2Handshake:1; /**< True iff the original handshake for
* this connection used the updated version
* of the connection protocol (client sends
* different cipher list, server sends only
* one certificate). */
/** True iff we should call negotiated_callback when we're done reading. */
unsigned int got_renegotiate:1;
size_t wantwrite_n; /**< 0 normally, >0 if we returned wantwrite last
* time. */
/** Last values retrieved from BIO_number_read()/write(); see
* tor_tls_get_n_raw_bytes() for usage.
*/
unsigned long last_write_count;
unsigned long last_read_count;
/** If set, a callback to invoke whenever the client tries to renegotiate
* the handshake. */
void (*negotiated_callback)(tor_tls_t *tls, void *arg);
/** Argument to pass to negotiated_callback. */
void *callback_arg;
};
/** Helper: compare tor_tls_t objects by its SSL. */
static INLINE int
tor_tls_entries_eq(const tor_tls_t *a, const tor_tls_t *b)
{
return a->ssl == b->ssl;
}
/** Helper: return a hash value for a tor_tls_t by its SSL. */
static INLINE unsigned int
tor_tls_entry_hash(const tor_tls_t *a)
{
#if SIZEOF_INT == SIZEOF_VOID_P
return ((unsigned int)(uintptr_t)a->ssl);
#else
return (unsigned int) ((((uint64_t)a->ssl)>>2) & UINT_MAX);
#endif
}
/** Map from SSL* pointers to tor_tls_t objects using those pointers.
*/
static HT_HEAD(tlsmap, tor_tls_t) tlsmap_root = HT_INITIALIZER();
HT_PROTOTYPE(tlsmap, tor_tls_t, node, tor_tls_entry_hash,
tor_tls_entries_eq)
HT_GENERATE(tlsmap, tor_tls_t, node, tor_tls_entry_hash,
tor_tls_entries_eq, 0.6, malloc, realloc, free)
/** Helper: given a SSL* pointer, return the tor_tls_t object using that
* pointer. */
static INLINE tor_tls_t *
tor_tls_get_by_ssl(const SSL *ssl)
{
tor_tls_t search, *result;
memset(&search, 0, sizeof(search));
search.ssl = (SSL*)ssl;
result = HT_FIND(tlsmap, &tlsmap_root, &search);
return result;
}
static void tor_tls_context_decref(tor_tls_context_t *ctx);
static void tor_tls_context_incref(tor_tls_context_t *ctx);
static X509* tor_tls_create_certificate(crypto_pk_env_t *rsa,
crypto_pk_env_t *rsa_sign,
const char *cname,
const char *cname_sign,
unsigned int lifetime);
/** Global tls context. We keep it here because nobody else needs to
* touch it. */
static tor_tls_context_t *global_tls_context = NULL;
/** True iff tor_tls_init() has been called. */
static int tls_library_is_initialized = 0;
/* Module-internal error codes. */
#define _TOR_TLS_SYSCALL (_MIN_TOR_TLS_ERROR_VAL - 2)
#define _TOR_TLS_ZERORETURN (_MIN_TOR_TLS_ERROR_VAL - 1)
/** Log all pending tls errors at level <b>severity</b>. Use
* <b>doing</b> to describe our current activities.
*/
static void
tls_log_errors(tor_tls_t *tls, int severity, const char *doing)
{
unsigned long err;
const char *msg, *lib, *func, *addr;
addr = tls ? tls->address : NULL;
while ((err = ERR_get_error()) != 0) {
msg = (const char*)ERR_reason_error_string(err);
lib = (const char*)ERR_lib_error_string(err);
func = (const char*)ERR_func_error_string(err);
if (!msg) msg = "(null)";
if (doing) {
log(severity, LD_NET, "TLS error while %s%s%s: %s (in %s:%s)",
doing, addr?" with ":"", addr?addr:"",
msg, lib, func);
} else {
log(severity, LD_NET, "TLS error%s%s: %s (in %s:%s)",
addr?" with ":"", addr?addr:"",
msg, lib, func);
}
}
}
/** Convert an errno (or a WSAerrno on windows) into a TOR_TLS_* error
* code. */
static int
tor_errno_to_tls_error(int e)
{
#if defined(MS_WINDOWS) && !defined(USE_BSOCKETS)
switch (e) {
case WSAECONNRESET: // most common
return TOR_TLS_ERROR_CONNRESET;
case WSAETIMEDOUT:
return TOR_TLS_ERROR_TIMEOUT;
case WSAENETUNREACH:
case WSAEHOSTUNREACH:
return TOR_TLS_ERROR_NO_ROUTE;
case WSAECONNREFUSED:
return TOR_TLS_ERROR_CONNREFUSED; // least common
default:
return TOR_TLS_ERROR_MISC;
}
#else
switch (e) {
case ECONNRESET: // most common
return TOR_TLS_ERROR_CONNRESET;
case ETIMEDOUT:
return TOR_TLS_ERROR_TIMEOUT;
case EHOSTUNREACH:
case ENETUNREACH:
return TOR_TLS_ERROR_NO_ROUTE;
case ECONNREFUSED:
return TOR_TLS_ERROR_CONNREFUSED; // least common
default:
return TOR_TLS_ERROR_MISC;
}
#endif
}
/** Given a TOR_TLS_* error code, return a string equivalent. */
const char *
tor_tls_err_to_string(int err)
{
if (err >= 0)
return "[Not an error.]";
switch (err) {
case TOR_TLS_ERROR_MISC: return "misc error";
case TOR_TLS_ERROR_IO: return "unexpected close";
case TOR_TLS_ERROR_CONNREFUSED: return "connection refused";
case TOR_TLS_ERROR_CONNRESET: return "connection reset";
case TOR_TLS_ERROR_NO_ROUTE: return "host unreachable";
case TOR_TLS_ERROR_TIMEOUT: return "connection timed out";
case TOR_TLS_CLOSE: return "closed";
case TOR_TLS_WANTREAD: return "want to read";
case TOR_TLS_WANTWRITE: return "want to write";
default: return "(unknown error code)";
}
}
#define CATCH_SYSCALL 1
#define CATCH_ZERO 2
/** Given a TLS object and the result of an SSL_* call, use
* SSL_get_error to determine whether an error has occurred, and if so
* which one. Return one of TOR_TLS_{DONE|WANTREAD|WANTWRITE|ERROR}.
* If extra&CATCH_SYSCALL is true, return _TOR_TLS_SYSCALL instead of
* reporting syscall errors. If extra&CATCH_ZERO is true, return
* _TOR_TLS_ZERORETURN instead of reporting zero-return errors.
*
* If an error has occurred, log it at level <b>severity</b> and describe the
* current action as <b>doing</b>.
*/
static int
tor_tls_get_error(tor_tls_t *tls, int r, int extra,
const char *doing, int severity)
{
int err = SSL_get_error(tls->ssl, r);
int tor_error = TOR_TLS_ERROR_MISC;
switch (err) {
case SSL_ERROR_NONE:
return TOR_TLS_DONE;
case SSL_ERROR_WANT_READ:
return TOR_TLS_WANTREAD;
case SSL_ERROR_WANT_WRITE:
return TOR_TLS_WANTWRITE;
case SSL_ERROR_SYSCALL:
if (extra&CATCH_SYSCALL)
return _TOR_TLS_SYSCALL;
if (r == 0) {
log(severity, LD_NET, "TLS error: unexpected close while %s", doing);
tor_error = TOR_TLS_ERROR_IO;
} else {
int e = tor_socket_errno(tls->socket);
log(severity, LD_NET,
"TLS error: <syscall error while %s> (errno=%d: %s)",
doing, e, tor_socket_strerror(e));
tor_error = tor_errno_to_tls_error(e);
}
tls_log_errors(tls, severity, doing);
return tor_error;
case SSL_ERROR_ZERO_RETURN:
if (extra&CATCH_ZERO)
return _TOR_TLS_ZERORETURN;
log(severity, LD_NET, "TLS connection closed while %s", doing);
tls_log_errors(tls, severity, doing);
return TOR_TLS_CLOSE;
default:
tls_log_errors(tls, severity, doing);
return TOR_TLS_ERROR_MISC;
}
}
/** Initialize OpenSSL, unless it has already been initialized.
*/
static void
tor_tls_init(void)
{
if (!tls_library_is_initialized) {
SSL_library_init();
SSL_load_error_strings();
crypto_global_init(-1);
tls_library_is_initialized = 1;
}
}
/** Free all global TLS structures. */
void
tor_tls_free_all(void)
{
if (global_tls_context) {
tor_tls_context_decref(global_tls_context);
global_tls_context = NULL;
}
if (!HT_EMPTY(&tlsmap_root)) {
log_warn(LD_MM, "Still have entries in the tlsmap at shutdown.");
}
HT_CLEAR(tlsmap, &tlsmap_root);
}
/** We need to give OpenSSL a callback to verify certificates. This is
* it: We always accept peer certs and complete the handshake. We
* don't validate them until later.
*/
static int
always_accept_verify_cb(int preverify_ok,
X509_STORE_CTX *x509_ctx)
{
(void) preverify_ok;
(void) x509_ctx;
return 1;
}
/** Return a newly allocated X509 name with commonName <b>cname</b>. */
static X509_NAME *
tor_x509_name_new(const char *cname)
{
int nid;
X509_NAME *name;
if (!(name = X509_NAME_new()))
return NULL;
if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error;
if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC,
(unsigned char*)cname, -1, -1, 0)))
goto error;
return name;
error:
X509_NAME_free(name);
return NULL;
}
/** Generate and sign an X509 certificate with the public key <b>rsa</b>,
* signed by the private key <b>rsa_sign</b>. The commonName of the
* certificate will be <b>cname</b>; the commonName of the issuer will be
* <b>cname_sign</b>. The cert will be valid for <b>cert_lifetime</b> seconds
* starting from now. Return a certificate on success, NULL on
* failure.
*/
static X509 *
tor_tls_create_certificate(crypto_pk_env_t *rsa,
crypto_pk_env_t *rsa_sign,
const char *cname,
const char *cname_sign,
unsigned int cert_lifetime)
{
time_t start_time, end_time;
EVP_PKEY *sign_pkey = NULL, *pkey=NULL;
X509 *x509 = NULL;
X509_NAME *name = NULL, *name_issuer=NULL;
tor_tls_init();
start_time = time(NULL);
tor_assert(rsa);
tor_assert(cname);
tor_assert(rsa_sign);
tor_assert(cname_sign);
if (!(sign_pkey = _crypto_pk_env_get_evp_pkey(rsa_sign,1)))
goto error;
if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,0)))
goto error;
if (!(x509 = X509_new()))
goto error;
if (!(X509_set_version(x509, 2)))
goto error;
if (!(ASN1_INTEGER_set(X509_get_serialNumber(x509), (long)start_time)))
goto error;
if (!(name = tor_x509_name_new(cname)))
goto error;
if (!(X509_set_subject_name(x509, name)))
goto error;
if (!(name_issuer = tor_x509_name_new(cname_sign)))
goto error;
if (!(X509_set_issuer_name(x509, name_issuer)))
goto error;
if (!X509_time_adj(X509_get_notBefore(x509),0,&start_time))
goto error;
end_time = start_time + cert_lifetime;
if (!X509_time_adj(X509_get_notAfter(x509),0,&end_time))
goto error;
if (!X509_set_pubkey(x509, pkey))
goto error;
if (!X509_sign(x509, sign_pkey, EVP_sha1()))
goto error;
goto done;
error:
if (x509) {
X509_free(x509);
x509 = NULL;
}
done:
tls_log_errors(NULL, LOG_WARN, "generating certificate");
if (sign_pkey)
EVP_PKEY_free(sign_pkey);
if (pkey)
EVP_PKEY_free(pkey);
if (name)
X509_NAME_free(name);
if (name_issuer)
X509_NAME_free(name_issuer);
return x509;
}
#define SERVER_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)
/* Note: for setting up your own private testing network with link crypto
* disabled, set the cipher lists to your cipher list to
* SSL3_TXT_RSA_NULL_SHA. If you do this, you won't be able to communicate
* with any of the "real" Tors, though. */
#if OPENSSL_VERSION_NUMBER >= 0x00908020l
#define CLIENT_CIPHER_LIST \
(TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA ":" \
TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA ":" \
TLS1_TXT_DHE_RSA_WITH_AES_256_SHA ":" \
TLS1_TXT_DHE_DSS_WITH_AES_256_SHA ":" \
TLS1_TXT_ECDH_RSA_WITH_AES_256_CBC_SHA ":" \
TLS1_TXT_ECDH_ECDSA_WITH_AES_256_CBC_SHA ":" \
TLS1_TXT_RSA_WITH_AES_256_SHA ":" \
TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA ":" \
TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA ":" \
TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA ":" \
TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA ":" \
TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
TLS1_TXT_DHE_DSS_WITH_AES_128_SHA ":" \
TLS1_TXT_ECDH_RSA_WITH_RC4_128_SHA":" \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -