⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tortls.c

📁 关于tor匿名通信的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* 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 + -