From c44016e86ec8dfd9147b2ca58e5277cb03a92561 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sat, 24 Apr 2004 22:17:50 +0000 Subject: [PATCH] Merge flagday into main branch. svn:r1683 --- src/common/crypto.c | 11 +++- src/common/tortls.c | 142 ++++++++++++++++++++++++----------------- src/common/tortls.h | 5 +- src/or/config.c | 4 ++ src/or/connection.c | 2 - src/or/connection_or.c | 42 +++++------- src/or/cpuworker.c | 48 +++++++++++--- src/or/dirserv.c | 7 +- src/or/main.c | 33 +++++++++- src/or/onion.c | 33 +++++++--- src/or/or.h | 18 ++++-- src/or/router.c | 88 ++++++++++++++++--------- src/or/routerlist.c | 60 +++++------------ src/or/test.c | 17 ++--- 14 files changed, 311 insertions(+), 199 deletions(-) diff --git a/src/common/crypto.c b/src/common/crypto.c index 88330892bc..831aaaa2c2 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -127,13 +127,18 @@ RSA *_crypto_pk_env_get_rsa(crypto_pk_env_t *env) } /* used by tortls.c */ -EVP_PKEY *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env) +EVP_PKEY *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env, int private) { RSA *key = NULL; EVP_PKEY *pkey = NULL; assert(env->key); - if (!(key = RSAPrivateKey_dup(env->key))) - goto error; + if (private) { + if (!(key = RSAPrivateKey_dup(env->key))) + goto error; + } else { + if (!(key = RSAPublicKey_dup(env->key))) + goto error; + } if (!(pkey = EVP_PKEY_new())) goto error; if (!(EVP_PKEY_assign_RSA(pkey, key))) diff --git a/src/common/tortls.c b/src/common/tortls.c index 4f6ee2146f..d7f54b4733 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -23,8 +23,8 @@ #include #include -/* How long do certificates live? (sec) */ -#define CERT_LIFETIME (365*24*60*60) +/* How long do identity certificates live? (sec) */ +#define IDENTITY_CERT_LIFETIME (365*24*60*60) /* How much clock skew do we tolerate when checking certificates? (sec) */ #define CERT_ALLOW_SKEW (90*60) @@ -44,7 +44,10 @@ struct tor_tls_st { }; static X509* tor_tls_create_certificate(crypto_pk_env_t *rsa, - const char *nickname); + crypto_pk_env_t *rsa_sign, + const char *cname, + const char *cname_sign, + unsigned int lifetime); /* global tls context, keep it here because nobody else needs to touch it */ static tor_tls_context *global_tls_context = NULL; @@ -54,7 +57,7 @@ static int tls_library_is_initialized = 0; #define _TOR_TLS_ZERORETURN -5 /* These functions are declared in crypto.c but not exported. */ -EVP_PKEY *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env); +EVP_PKEY *_crypto_pk_env_get_evp_pkey(crypto_pk_env_t *env, int private); crypto_pk_env_t *_crypto_new_pk_env_rsa(RSA *rsa); DH *_crypto_dh_env_get_dh(crypto_dh_env_t *dh); @@ -129,26 +132,32 @@ static int always_accept_verify_cb(int preverify_ok, } /* Generate a self-signed certificate with the private key 'rsa' and - * commonName 'nickname', and write it, PEM-encoded, to the file named - * by 'certfile'. Return 0 on success, -1 for failure. + * identity key 'identity and commonName 'nickname'. Return a certificate + * on success, NULL on failure. + * DOCDOC */ X509 * tor_tls_create_certificate(crypto_pk_env_t *rsa, - const char *nickname) + 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 *pkey = NULL; + EVP_PKEY *sign_pkey = NULL, *pkey=NULL; X509 *x509 = NULL; - X509_NAME *name = NULL; + X509_NAME *name = NULL, *name_issuer=NULL; int nid; tor_tls_init(); start_time = time(NULL); - assert(rsa && nickname); - if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa))) - return NULL; + assert(rsa && cname && rsa_sign && 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))) @@ -163,20 +172,29 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, "TOR", -1, -1, 0))) goto error; if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; if (!(X509_NAME_add_entry_by_NID(name, nid, MBSTRING_ASC, - (char*)nickname, -1, -1, 0))) goto error; - - if (!(X509_set_issuer_name(x509, name))) - goto error; + (char*)cname, -1, -1, 0))) goto error; if (!(X509_set_subject_name(x509, name))) goto error; + + if (!(name_issuer = X509_NAME_new())) + goto error; + if ((nid = OBJ_txt2nid("organizationName")) == NID_undef) goto error; + if (!(X509_NAME_add_entry_by_NID(name_issuer, nid, MBSTRING_ASC, + "TOR", -1, -1, 0))) goto error; + if ((nid = OBJ_txt2nid("commonName")) == NID_undef) goto error; + if (!(X509_NAME_add_entry_by_NID(name_issuer, nid, MBSTRING_ASC, + (char*)cname_sign, -1, -1, 0))) 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; + 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, pkey, EVP_sha1())) + if (!X509_sign(x509, sign_pkey, EVP_sha1())) goto error; goto done; @@ -186,10 +204,14 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, x509 = NULL; } done: + 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; } @@ -210,26 +232,37 @@ tor_tls_create_certificate(crypto_pk_env_t *rsa, #endif /* Create a new TLS context. If we are going to be using it as a - * server, it must have isServer set to true, certfile set to a - * filename for a certificate file, and RSA set to the private key - * used for that certificate. Return -1 if failure, else 0. + * server, it must have isServer set to true, 'identity' set to the + * identity key used to sign that certificate, and 'nickname' set to + * the server's nickname. Return -1 if failure, else 0. */ int -tor_tls_context_new(crypto_pk_env_t *rsa, - int isServer, const char *nickname) +tor_tls_context_new(crypto_pk_env_t *identity, + int isServer, const char *nickname, + unsigned int key_lifetime) { + crypto_pk_env_t *rsa = NULL; crypto_dh_env_t *dh = NULL; EVP_PKEY *pkey = NULL; - tor_tls_context *result; - X509 *cert = NULL; + tor_tls_context *result = NULL; + X509 *cert = NULL, *idcert = NULL; + char nn2[1024]; + sprintf(nn2, "%s ", nickname); tor_tls_init(); - if (rsa) { - cert = tor_tls_create_certificate(rsa, nickname); - if (!cert) { + if (isServer) { + if (!(rsa = crypto_new_pk_env())) + goto error; + if (crypto_pk_generate_key(rsa)<0) + goto error; + cert = tor_tls_create_certificate(rsa, identity, nickname, nn2, + key_lifetime); + idcert = tor_tls_create_certificate(identity, identity, nn2, nn2, + IDENTITY_CERT_LIFETIME); + if (!cert || !idcert) { log(LOG_WARN, "Error creating certificate"); - return -1; + goto error; } } @@ -249,9 +282,12 @@ tor_tls_context_new(crypto_pk_env_t *rsa, goto error; if (cert && !SSL_CTX_use_certificate(result->ctx,cert)) goto error; + if (idcert && !SSL_CTX_add_extra_chain_cert(result->ctx,idcert)) + goto error; SSL_CTX_set_session_cache_mode(result->ctx, SSL_SESS_CACHE_OFF); - if (rsa) { - if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa))) + if (isServer) { + assert(rsa); + if (!(pkey = _crypto_pk_env_get_evp_pkey(rsa,1))) goto error; if (!SSL_CTX_use_PrivateKey(result->ctx, pkey)) goto error; @@ -283,13 +319,15 @@ tor_tls_context_new(crypto_pk_env_t *rsa, error: if (pkey) EVP_PKEY_free(pkey); + if (rsa) + crypto_free_pk_env(rsa); if (dh) crypto_dh_free(dh); if (result && result->ctx) SSL_CTX_free(result->ctx); if (result) free(result); - + /* leak certs XXXX ? */ return -1; } @@ -509,19 +547,19 @@ tor_tls_get_peer_cert_nickname(tor_tls *tls, char *buf, int buflen) } /* If the provided tls connection is authenticated and has a - * certificate that is currently valid and is correctly self-signed, - * return its public key. Otherwise return NULL. + * certificate that is currently valid and is correctly signed by + * identity_key, return 0. Else, return -1. */ -crypto_pk_env_t * -tor_tls_verify(tor_tls *tls) +int +tor_tls_verify(tor_tls *tls, crypto_pk_env_t *identity_key) { X509 *cert = NULL; - EVP_PKEY *pkey = NULL; - RSA *rsa = NULL; + EVP_PKEY *id_pkey = NULL; time_t now, t; - crypto_pk_env_t *r = NULL; + int r = -1; + if (!(cert = SSL_get_peer_certificate(tls->ssl))) - return NULL; + return -1; now = time(NULL); t = now + CERT_ALLOW_SKEW; @@ -536,33 +574,19 @@ tor_tls_verify(tor_tls *tls) } /* Get the public key. */ - if (!(pkey = X509_get_pubkey(cert))) { - log_fn(LOG_WARN,"X509_get_pubkey returned null"); - goto done; - } - if (X509_verify(cert, pkey) <= 0) { + if (!(id_pkey = _crypto_pk_env_get_evp_pkey(identity_key,0)) || + X509_verify(cert, id_pkey) <= 0) { log_fn(LOG_WARN,"X509_verify on cert and pkey returned <= 0"); goto done; } - rsa = EVP_PKEY_get1_RSA(pkey); - EVP_PKEY_free(pkey); - pkey = NULL; - if (!rsa) { - log_fn(LOG_WARN,"EVP_PKEY_get1_RSA(pkey) returned null"); - goto done; - } - - r = _crypto_new_pk_env_rsa(rsa); - rsa = NULL; + r = 0; done: if (cert) X509_free(cert); - if (pkey) - EVP_PKEY_free(pkey); - if (rsa) - RSA_free(rsa); + if (id_pkey) + EVP_PKEY_free(id_pkey); return r; } diff --git a/src/common/tortls.h b/src/common/tortls.h index d9e326e55c..da22454f2e 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -17,12 +17,13 @@ typedef struct tor_tls_st tor_tls; #define TOR_TLS_DONE 0 /* X509* tor_tls_write_certificate(char *certfile, crypto_pk_env_t *rsa, char *nickname); */ -int tor_tls_context_new(crypto_pk_env_t *rsa, int isServer, const char *nickname); +int tor_tls_context_new(crypto_pk_env_t *rsa, int isServer, + const char *nickname, unsigned int key_lifetime); tor_tls *tor_tls_new(int sock, int isServer); void tor_tls_free(tor_tls *tls); int tor_tls_peer_has_cert(tor_tls *tls); int tor_tls_get_peer_cert_nickname(tor_tls *tls, char *buf, int buflen); -crypto_pk_env_t *tor_tls_verify(tor_tls *tls); +int tor_tls_verify(tor_tls *tls, crypto_pk_env_t *identity); int tor_tls_read(tor_tls *tls, char *cp, int len); int tor_tls_write(tor_tls *tls, char *cp, int n); int tor_tls_handshake(tor_tls *tls); diff --git a/src/or/config.c b/src/or/config.c index 93adb5437e..824e5643cb 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -655,6 +655,10 @@ int getconfig(int argc, char **argv, or_options_t *options) { log(LOG_WARN,"DirFetchPostPeriod option must be positive."); result = -1; } + if(options->DirFetchPostPeriod > MIN_ONION_KEY_LIFETIME/2) { + log(LOG_WARN,"DirFetchPostPeriod is too large; clipping."); + options->DirFetchPostPeriod = MIN_ONION_KEY_LIFETIME/2; + } if(options->KeepalivePeriod < 1) { log(LOG_WARN,"KeepalivePeriod option must be positive."); diff --git a/src/or/connection.c b/src/or/connection.c index f673158e5f..299d0d2d8e 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -116,8 +116,6 @@ void connection_free(connection_t *conn) { if (conn->onion_pkey) crypto_free_pk_env(conn->onion_pkey); - if (conn->link_pkey) - crypto_free_pk_env(conn->link_pkey); if (conn->identity_pkey) crypto_free_pk_env(conn->identity_pkey); tor_free(conn->nickname); diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 5e122c8480..a68c870423 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -84,7 +84,6 @@ void connection_or_init_conn_from_router(connection_t *conn, routerinfo_t *route conn->port = router->or_port; conn->receiver_bucket = conn->bandwidth = router->bandwidthburst; conn->onion_pkey = crypto_pk_dup_key(router->onion_pkey); - conn->link_pkey = crypto_pk_dup_key(router->link_pkey); conn->identity_pkey = crypto_pk_dup_key(router->identity_pkey); conn->nickname = tor_strdup(router->nickname); tor_free(conn->address); @@ -178,7 +177,6 @@ int connection_tls_continue_handshake(connection_t *conn) { } static int connection_tls_finish_handshake(connection_t *conn) { - crypto_pk_env_t *pk; routerinfo_t *router; char nickname[MAX_NICKNAME_LEN+1]; connection_t *c; @@ -203,42 +201,34 @@ static int connection_tls_finish_handshake(connection_t *conn) { return -1; } log_fn(LOG_DEBUG, "Other side claims to be '%s'", nickname); - pk = tor_tls_verify(conn->tls); - if(!pk) { + router = router_get_by_nickname(nickname); + if (!router) { + log_fn(LOG_INFO, "Unrecognized router with nickname '%s'", nickname); + return -1; + } + if(tor_tls_verify(conn->tls, router->identity_pkey)<0) { log_fn(LOG_WARN,"Other side '%s' (%s:%d) has a cert but it's invalid. Closing.", nickname, conn->address, conn->port); return -1; } - router = router_get_by_link_pk(pk); - if (!router) { - log_fn(LOG_INFO,"Unrecognized public key from peer '%s' (%s:%d). Closing.", - nickname, conn->address, conn->port); - crypto_free_pk_env(pk); - return -1; - } - if(conn->link_pkey) { /* I initiated this connection. */ - if(crypto_pk_cmp_keys(conn->link_pkey, pk)) { - log_fn(LOG_WARN,"We connected to '%s' (%s:%d) but he gave us a different key. Closing.", - nickname, conn->address, conn->port); - crypto_free_pk_env(pk); + log_fn(LOG_DEBUG,"The router's cert is valid."); + + if (conn->nickname) { + /* I initiated this connection. */ + if (strcmp(conn->nickname, nickname)) { + log_fn(options.DirPort ? LOG_WARN : LOG_INFO, + "Other side is '%s', but we tried to connect to '%s'", + nickname, conn->nickname); return -1; } - log_fn(LOG_DEBUG,"The router's pk matches the one we meant to connect to. Good."); } else { if((c=connection_exact_get_by_addr_port(router->addr,router->or_port))) { log_fn(LOG_INFO,"Router %s is already connected on fd %d. Dropping fd %d.", router->nickname, c->s, conn->s); - crypto_free_pk_env(pk); return -1; } - connection_or_init_conn_from_router(conn, router); - } - crypto_free_pk_env(pk); - if (strcmp(conn->nickname, nickname)) { - log_fn(options.DirPort ? LOG_WARN : LOG_INFO, - "Other side claims to be '%s', but we expected '%s'", - nickname, conn->nickname); - return -1; + connection_or_init_conn_from_router(conn,router); } + if (!options.ORPort) { /* If I'm an OP... */ conn->receiver_bucket = conn->bandwidth = DEFAULT_BANDWIDTH_OP; } diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c index 17216cc18f..8f6d67a5c7 100644 --- a/src/or/cpuworker.c +++ b/src/or/cpuworker.c @@ -12,8 +12,9 @@ extern or_options_t options; /* command-line and config-file options */ #define LEN_ONION_QUESTION (1+TAG_LEN+ONIONSKIN_CHALLENGE_LEN) #define LEN_ONION_RESPONSE (1+TAG_LEN+ONIONSKIN_REPLY_LEN+40+32) -int num_cpuworkers=0; -int num_cpuworkers_busy=0; +static int num_cpuworkers=0; +static int num_cpuworkers_busy=0; +static time_t last_rotation_time=0; int cpuworker_main(void *data); static int spawn_cpuworker(void); @@ -21,6 +22,7 @@ static void spawn_enough_cpuworkers(void); static void process_pending_task(connection_t *cpuworker); void cpu_init(void) { + last_rotation_time=time(NULL); spawn_enough_cpuworkers(); } @@ -47,6 +49,18 @@ static void tag_unpack(char *tag, uint32_t *addr, uint16_t *port, uint16_t *circ log_fn(LOG_DEBUG,"onion was from %s:%d, circ_id %d.", inet_ntoa(in), *port, *circ_id); } +void cpuworkers_rotate(void) +{ + connection_t *cpuworker; + while ((cpuworker = connection_get_by_type_state(CONN_TYPE_CPUWORKER, + CPUWORKER_STATE_IDLE))) { + connection_mark_for_close(cpuworker,0); + --num_cpuworkers; + } + last_rotation_time = time(NULL); + spawn_enough_cpuworkers(); +} + int connection_cpu_process_inbuf(connection_t *conn) { char success; unsigned char buf[LEN_ONION_RESPONSE]; @@ -111,7 +125,13 @@ int connection_cpu_process_inbuf(connection_t *conn) { done_processing: conn->state = CPUWORKER_STATE_IDLE; num_cpuworkers_busy--; - process_pending_task(conn); + if (conn->timestamp_created < last_rotation_time) { + connection_mark_for_close(conn,0); + num_cpuworkers--; + spawn_enough_cpuworkers(); + } else { + process_pending_task(conn); + } return 0; } @@ -126,6 +146,7 @@ int cpuworker_main(void *data) { unsigned char reply_to_proxy[ONIONSKIN_REPLY_LEN]; unsigned char buf[LEN_ONION_RESPONSE]; char tag[TAG_LEN]; + crypto_pk_env_t *onion_key = NULL, *last_onion_key = NULL; close(fdarray[0]); /* this is the side of the socketpair the parent uses */ fd = fdarray[1]; /* this side is ours */ @@ -133,27 +154,32 @@ int cpuworker_main(void *data) { connection_free_all(); /* so the child doesn't hold the parent's fd's open */ #endif + /* XXXX WINDOWS lock here. */ + onion_key = crypto_pk_dup_key(get_onion_key()); + if (get_previous_onion_key()) + last_onion_key = crypto_pk_dup_key(get_previous_onion_key()); + for(;;) { if(recv(fd, &question_type, 1, 0) != 1) { // log_fn(LOG_ERR,"read type failed. Exiting."); log_fn(LOG_INFO,"cpuworker exiting because tor process died."); - spawn_exit(); + goto end; } assert(question_type == CPUWORKER_TASK_ONION); if(read_all(fd, tag, TAG_LEN, 1) != TAG_LEN) { log_fn(LOG_ERR,"read tag failed. Exiting."); - spawn_exit(); + goto end; } if(read_all(fd, question, ONIONSKIN_CHALLENGE_LEN, 1) != ONIONSKIN_CHALLENGE_LEN) { log_fn(LOG_ERR,"read question failed. Exiting."); - spawn_exit(); + goto end; } if(question_type == CPUWORKER_TASK_ONION) { - if(onion_skin_server_handshake(question, get_onion_key(), + if(onion_skin_server_handshake(question, onion_key, last_onion_key, reply_to_proxy, keys, 40+32) < 0) { /* failure */ log_fn(LOG_WARN,"onion_skin_server_handshake failed."); @@ -173,6 +199,12 @@ int cpuworker_main(void *data) { log_fn(LOG_DEBUG,"finished writing response."); } } + end: + if (onion_key) + crypto_free_pk_env(onion_key); + if (last_onion_key) + crypto_free_pk_env(last_onion_key); + spawn_exit(); return 0; /* windows wants this function to return an int */ } @@ -263,7 +295,7 @@ int assign_to_cpuworker(connection_t *cpuworker, unsigned char question_type, return 0; } - if(!cpuworker) + if (!cpuworker) cpuworker = connection_get_by_type_state(CONN_TYPE_CPUWORKER, CPUWORKER_STATE_IDLE); assert(cpuworker); diff --git a/src/or/dirserv.c b/src/or/dirserv.c index 7ce578c4ff..10b43c77b1 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -437,8 +437,13 @@ dirserv_dump_directory_to_string(char *s, int maxlen, /* These multiple strlcat calls are inefficient, but dwarfed by the RSA signature. */ - if (strlcat(s, "directory-signature\n", maxlen) >= maxlen) + if (strlcat(s, "directory-signature ", maxlen) >= maxlen) goto truncated; + if (strlcat(s, options.Nickname, maxlen) >= maxlen) + goto truncated; + if (strlcat(s, "\n", maxlen) >= maxlen) + goto truncated; + if (router_get_dir_hash(s,digest)) { log_fn(LOG_WARN,"couldn't compute digest"); diff --git a/src/or/main.c b/src/or/main.c index ee3d24cf5d..49a9554176 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -53,6 +53,7 @@ int has_completed_circuit=0; ****************************************************************************/ int connection_add(connection_t *conn) { + assert(conn); if(nfds >= options.MaxConn-1) { log_fn(LOG_WARN,"failing because nfds is too high."); @@ -345,9 +346,38 @@ static void run_connection_housekeeping(int i, time_t now) { static void run_scheduled_events(time_t now) { static long time_to_fetch_directory = 0; static time_t last_uploaded_services = 0; + static time_t last_rotated_certificate = 0; int i; - /* 1. Every DirFetchPostPeriod seconds, we get a new directory and upload + + /* 1a. Every MIN_ONION_KEY_LIFETIME seconds, rotate the onion keys, + * shut down and restart all cpuworkers, and update the directory if + * necessary. + */ + if (options.ORPort && get_onion_key_set_at()+MIN_ONION_KEY_LIFETIME < now) { + rotate_onion_key(); + cpuworkers_rotate(); + if (router_rebuild_descriptor()<0) { + log_fn(LOG_WARN, "Couldn't rebuild router descriptor"); + } + router_rebuild_descriptor(); + router_upload_dir_desc_to_dirservers(); + } + + /* 1b. Every MAX_LINK_KEY_LIFETIME seconds, we change our TLS context. */ + if (!last_rotated_certificate) + last_rotated_certificate = now; + if (options.ORPort && last_rotated_certificate+MAX_SSL_KEY_LIFETIME < now) { + if (tor_tls_context_new(get_identity_key(), 1, options.Nickname, + MAX_SSL_KEY_LIFETIME) < 0) { + log_fn(LOG_WARN, "Error reinitializing TLS context"); + } + last_rotated_certificate = now; + /* XXXX We should rotate TLS connections as well; this code doesn't change + * XXXX them at all. */ + } + + /* 1c. Every DirFetchPostPeriod seconds, we get a new directory and upload * our descriptor (if any). */ if(time_to_fetch_directory < now) { /* it's time to fetch a new directory and/or post our descriptor */ @@ -371,6 +401,7 @@ static void run_scheduled_events(time_t now) { time_to_fetch_directory = now + options.DirFetchPostPeriod; } + /* 2. Every second, we examine pending circuits and prune the * ones which have been pending for more than a few seconds. * We do this before step 3, so it can try building more if diff --git a/src/or/onion.c b/src/or/onion.c index 8939a3d2e3..7ad736e954 100644 --- a/src/or/onion.c +++ b/src/or/onion.c @@ -576,9 +576,6 @@ int onion_extend_cpath(crypt_path_t **head_ptr, cpath_build_state_t *state, rout [16 bytes] Symmetric key for encrypting blob past RSA [112 bytes] g^x part 1 (inside the RSA) [16 bytes] g^x part 2 (symmetrically encrypted) -[ 6 bytes] Meeting point (IP/port) -[ 8 bytes] Meeting cookie -[16 bytes] End-to-end authentication [optional] * Stores the DH private key into handshake_state_out for later completion * of the handshake. @@ -604,7 +601,7 @@ onion_skin_create(crypto_pk_env_t *dest_router_key, pkbytes = crypto_pk_keysize(dest_router_key); assert(dhbytes == 128); assert(pkbytes == 128); - challenge = tor_malloc_zero(ONIONSKIN_CHALLENGE_LEN-CIPHER_KEY_LEN); + challenge = tor_malloc_zero(DH_KEY_LEN); if (crypto_dh_get_public(dh, challenge, dhbytes)) goto err; @@ -626,8 +623,8 @@ onion_skin_create(crypto_pk_env_t *dest_router_key, /* set meeting point, meeting cookie, etc here. Leave zero for now. */ if (crypto_pk_public_hybrid_encrypt(dest_router_key, challenge, - ONIONSKIN_CHALLENGE_LEN-CIPHER_KEY_LEN, - onion_skin_out, PK_NO_PADDING, 1)<0) + DH_KEY_LEN, + onion_skin_out, PK_PKCS1_OAEP_PADDING, 1)<0) goto err; tor_free(challenge); @@ -648,6 +645,7 @@ onion_skin_create(crypto_pk_env_t *dest_router_key, int onion_skin_server_handshake(char *onion_skin, /* ONIONSKIN_CHALLENGE_LEN bytes */ crypto_pk_env_t *private_key, + crypto_pk_env_t *prev_private_key, char *handshake_reply_out, /* ONIONSKIN_REPLY_LEN bytes */ char *key_out, int key_out_len) @@ -656,11 +654,28 @@ onion_skin_server_handshake(char *onion_skin, /* ONIONSKIN_CHALLENGE_LEN bytes * crypto_dh_env_t *dh = NULL; int len; char *key_material=NULL; + int i; + crypto_pk_env_t *k; - if (crypto_pk_private_hybrid_decrypt(private_key, - onion_skin, ONIONSKIN_CHALLENGE_LEN, - challenge, PK_NO_PADDING)<0) + len = -1; + for (i=0;i<2;++i) { + k = i==0?private_key:prev_private_key; + if (!k) + break; + len = crypto_pk_private_hybrid_decrypt(k, + onion_skin, ONIONSKIN_CHALLENGE_LEN, + challenge, PK_PKCS1_OAEP_PADDING); + if (len>0) + break; + } + if (len<0) { + log_fn(LOG_WARN, "Couldn't decrypt onionskin"); goto err; + } else if (len != DH_KEY_LEN) { + log_fn(LOG_WARN, "Unexpected onionskin length after decryption: %d", + len); + goto err; + } dh = crypto_dh_new(); if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) diff --git a/src/or/or.h b/src/or/or.h index bc6af6cb93..0b3110f76c 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -112,6 +112,9 @@ #define MAX_DNS_ENTRY_AGE (15*60) #endif +#define MIN_ONION_KEY_LIFETIME (120*60) +#define MAX_SSL_KEY_LIFETIME (120*60) + #define CIRC_ID_TYPE_LOWER 0 #define CIRC_ID_TYPE_HIGHER 1 @@ -381,7 +384,6 @@ struct connection_t { * strdup into this, because free_connection frees it */ crypto_pk_env_t *onion_pkey; /* public RSA key for the other side's onions */ - crypto_pk_env_t *link_pkey; /* public RSA key for the other side's TLS */ crypto_pk_env_t *identity_pkey; /* public RSA key for the other side's signing */ char *nickname; @@ -445,7 +447,6 @@ typedef struct { time_t published_on; crypto_pk_env_t *onion_pkey; /* public RSA key for onions */ - crypto_pk_env_t *link_pkey; /* public RSA key for TLS */ crypto_pk_env_t *identity_pkey; /* public RSA key for signing */ int is_running; @@ -492,8 +493,10 @@ struct crypt_path_t { }; #define DH_KEY_LEN DH_BYTES -#define ONIONSKIN_CHALLENGE_LEN (16+DH_KEY_LEN) -#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+20) +#define ONIONSKIN_CHALLENGE_LEN (PKCS1_OAEP_PADDING_OVERHEAD+\ + CIPHER_KEY_LEN+\ + DH_KEY_LEN) +#define ONIONSKIN_REPLY_LEN (DH_KEY_LEN+DIGEST_LEN) #define REND_COOKIE_LEN DIGEST_LEN typedef struct crypt_path_t crypt_path_t; @@ -882,6 +885,7 @@ void connection_or_write_cell_to_buf(const cell_t *cell, connection_t *conn); /********************************* cpuworker.c *****************************/ void cpu_init(void); +void cpuworkers_rotate(void); int connection_cpu_finished_flushing(connection_t *conn); int connection_cpu_process_inbuf(connection_t *conn); int cpuworker_main(void *data); @@ -948,6 +952,7 @@ int onion_skin_create(crypto_pk_env_t *router_key, int onion_skin_server_handshake(char *onion_skin, crypto_pk_env_t *private_key, + crypto_pk_env_t *prev_private_key, char *handshake_reply_out, char *key_out, int key_out_len); @@ -964,11 +969,13 @@ cpath_build_state_t *onion_new_cpath_build_state(uint8_t purpose, void set_onion_key(crypto_pk_env_t *k); crypto_pk_env_t *get_onion_key(void); +crypto_pk_env_t *get_previous_onion_key(void); +time_t get_onion_key_set_at(void); void set_identity_key(crypto_pk_env_t *k); crypto_pk_env_t *get_identity_key(void); -crypto_pk_env_t *get_link_key(void); int init_keys(void); crypto_pk_env_t *init_key_from_file(const char *fname); +void rotate_onion_key(void); void router_retry_connections(void); void router_upload_dir_desc_to_dirservers(void); @@ -989,7 +996,6 @@ routerinfo_t *router_choose_random_node(routerlist_t *dir, char *preferred, char *excluded, struct smartlist_t *excludedsmartlist); routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port); -routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk); routerinfo_t *router_get_by_nickname(char *nickname); void router_get_routerlist(routerlist_t **prouterlist); void routerinfo_free(routerinfo_t *router); diff --git a/src/or/router.c b/src/or/router.c index fa841c0896..aed865ada0 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -11,12 +11,14 @@ extern or_options_t options; /* command-line and config-file options */ /************************************************************/ /* private keys */ +static time_t onionkey_set_at=0; static crypto_pk_env_t *onionkey=NULL; -static crypto_pk_env_t *linkkey=NULL; +static crypto_pk_env_t *lastonionkey=NULL; static crypto_pk_env_t *identitykey=NULL; void set_onion_key(crypto_pk_env_t *k) { onionkey = k; + onionkey_set_at = time(NULL); } crypto_pk_env_t *get_onion_key(void) { @@ -24,15 +26,12 @@ crypto_pk_env_t *get_onion_key(void) { return onionkey; } -void set_link_key(crypto_pk_env_t *k) -{ - linkkey = k; +crypto_pk_env_t *get_previous_onion_key(void) { + return lastonionkey; } -crypto_pk_env_t *get_link_key(void) -{ - assert(linkkey); - return linkkey; +time_t get_onion_key_set_at(void) { + return onionkey_set_at; } void set_identity_key(crypto_pk_env_t *k) { @@ -46,6 +45,46 @@ crypto_pk_env_t *get_identity_key(void) { /************************************************************/ +/* Replace the previous onion key with the current onion key, and generate + * a new previous onion key. Immediately after calling this function, + * the OR should: + * a) shedule all previous cpuworker to shut down _after_ processing + * pending work. (This will cause fresh cpuworkers to be generated.) + * b) generate and upload a fresh routerinfo. + */ +void rotate_onion_key(void) +{ + char fname[512]; + crypto_pk_env_t *prkey; + sprintf(fname,"%s/keys/onion.key",options.DataDirectory); + if (!(prkey = crypto_new_pk_env())) { + log(LOG_ERR, "Error creating crypto environment."); + goto error; + } + if (crypto_pk_generate_key(onionkey)) { + log(LOG_ERR, "Error generating key: %s", crypto_perror()); + goto error; + } + if (crypto_pk_write_private_key_to_filename(prkey, fname)) { + log(LOG_ERR, "Couldn't write generated key to %s.", fname); + goto error; + } + if (lastonionkey) + crypto_free_pk_env(lastonionkey); + /* XXXX WINDOWS on windows, we need to protect this next bit with a lock. + */ + lastonionkey = onionkey; + set_onion_key(prkey); + if (router_rebuild_descriptor() <0) { + goto error; + } + router_upload_dir_desc_to_dirservers(); + /* Mark all CPU workers to close. */ + return; + error: + log_fn(LOG_WARN, "Couldn't rotate onion key."); +} + /* Try to read an RSA key from 'fname'. If 'fname' doesn't exist, create a new * RSA key and save it in 'fname'. Return the read/created key, or NULL on * error. @@ -112,7 +151,7 @@ int init_keys(void) { /* OP's don't need keys. Just initialize the TLS context.*/ if (!options.ORPort) { assert(!options.DirPort); - if (tor_tls_context_new(NULL, 0, NULL)<0) { + if (tor_tls_context_new(NULL, 0, NULL, 0)<0) { log_fn(LOG_ERR, "Error creating TLS context for OP."); return -1; } @@ -146,12 +185,10 @@ int init_keys(void) { set_onion_key(prkey); /* 3. Initialize link key and TLS context. */ - strcpy(cp, "/link.key"); - log_fn(LOG_INFO,"Reading/making link key %s...",keydir); - prkey = init_key_from_file(keydir); - if (!prkey) return -1; - set_link_key(prkey); - if (tor_tls_context_new(prkey, 1, options.Nickname) < 0) { + /* XXXX use actual rotation interval as cert lifetime, once we do + * connection rotation. */ + if (tor_tls_context_new(get_identity_key(), 1, options.Nickname, + MAX_SSL_KEY_LIFETIME) < 0) { log_fn(LOG_ERR, "Error initializing TLS context"); return -1; } @@ -370,7 +407,6 @@ int router_rebuild_descriptor(void) { ri->dir_port = options.DirPort; ri->published_on = time(NULL); ri->onion_pkey = crypto_pk_dup_key(get_onion_key()); - ri->link_pkey = crypto_pk_dup_key(get_link_key()); ri->identity_pkey = crypto_pk_dup_key(get_identity_key()); get_platform_str(platform, sizeof(platform)); ri->platform = tor_strdup(platform); @@ -403,13 +439,12 @@ void get_platform_str(char *platform, int len) int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router, crypto_pk_env_t *ident_key) { char *onion_pkey; - char *link_pkey; char *identity_pkey; struct in_addr in; char digest[20]; char signature[128]; char published[32]; - int onion_pkeylen, link_pkeylen, identity_pkeylen; + int onion_pkeylen, identity_pkeylen; int written; int result=0; struct exit_policy_t *tmpe; @@ -436,33 +471,28 @@ int router_dump_router_to_string(char *s, int maxlen, routerinfo_t *router, return -1; } - if(crypto_pk_write_public_key_to_string(router->link_pkey, - &link_pkey,&link_pkeylen)<0) { - log_fn(LOG_WARN,"write link_pkey to string failed!"); - return -1; - } strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&router->published_on)); + /* XXXX eventually, don't include link key */ result = snprintf(s, maxlen, - "router %s %s %d %d %d %d\n" + "router %s %s %d %d %d\n" "platform %s\n" "published %s\n" + "bandwidth %d %d\n" "onion-key\n%s" - "link-key\n%s" "signing-key\n%s", router->nickname, router->address, router->or_port, router->socks_port, router->dir_port, - (int) router->bandwidthrate, -/* XXXBC also write bandwidthburst */ router->platform, published, - onion_pkey, link_pkey, identity_pkey); + (int) router->bandwidthrate, + (int) router->bandwidthburst, + onion_pkey, identity_pkey); free(onion_pkey); - free(link_pkey); free(identity_pkey); if(result < 0 || result >= maxlen) { diff --git a/src/or/routerlist.c b/src/or/routerlist.c index df0d6b7d04..6aabb3b3bc 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -30,7 +30,7 @@ typedef enum { K_SIGNED_DIRECTORY, K_SIGNING_KEY, K_ONION_KEY, - K_LINK_KEY, + K_LINK_KEY, /* XXXX obsolete */ K_ROUTER_SIGNATURE, K_PUBLISHED, K_RUNNING_ROUTERS, @@ -83,7 +83,7 @@ static struct { char *t; int v; arg_syntax s; obj_syntax os; where_syntax ws; } token_table[] = { { "accept", K_ACCEPT, ARGS, NO_OBJ, RTR_ONLY }, - { "directory-signature", K_DIRECTORY_SIGNATURE, NO_ARGS, NEED_OBJ, DIR_ONLY}, + { "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ, DIR_ONLY}, { "reject", K_REJECT, ARGS, NO_OBJ, RTR_ONLY }, { "router", K_ROUTER, ARGS, NO_OBJ, RTR_ONLY }, { "recommended-software", K_RECOMMENDED_SOFTWARE, ARGS, NO_OBJ, DIR_ONLY }, @@ -305,21 +305,6 @@ routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { return NULL; } -routerinfo_t *router_get_by_link_pk(crypto_pk_env_t *pk) -{ - int i; - routerinfo_t *router; - - assert(routerlist); - - for(i=0;irouters);i++) { - router = smartlist_get(routerlist->routers, i); - if (0 == crypto_pk_cmp_keys(router->link_pkey, pk)) - return router; - } - return NULL; -} - routerinfo_t *router_get_by_nickname(char *nickname) { int i; @@ -354,8 +339,6 @@ void routerinfo_free(routerinfo_t *router) tor_free(router->platform); if (router->onion_pkey) crypto_free_pk_env(router->onion_pkey); - if (router->link_pkey) - crypto_free_pk_env(router->link_pkey); if (router->identity_pkey) crypto_free_pk_env(router->identity_pkey); while (router->exit_policy) { @@ -380,8 +363,6 @@ routerinfo_t *routerinfo_copy(const routerinfo_t *router) r->platform = tor_strdup(r->platform); if (r->onion_pkey) r->onion_pkey = crypto_pk_dup_key(r->onion_pkey); - if (r->link_pkey) - r->link_pkey = crypto_pk_dup_key(r->link_pkey); if (r->identity_pkey) r->identity_pkey = crypto_pk_dup_key(r->identity_pkey); e = &r->exit_policy; @@ -949,10 +930,10 @@ routerinfo_t *router_get_entry_from_string(const char *s, } router = tor_malloc_zero(sizeof(routerinfo_t)); - router->onion_pkey = router->identity_pkey = router->link_pkey = NULL; + router->onion_pkey = router->identity_pkey = NULL; ports_set = bw_set = 0; - if (tok->n_args == 2 || tok->n_args == 6) { + if (tok->n_args == 2 || tok->n_args == 5 || tok->n_args == 6) { router->nickname = tor_strdup(tok->args[0]); if (strlen(router->nickname) > MAX_NICKNAME_LEN) { log_fn(LOG_WARN,"Router nickname too long."); @@ -966,15 +947,20 @@ routerinfo_t *router_get_entry_from_string(const char *s, router->address = tor_strdup(tok->args[1]); router->addr = 0; - if (tok->n_args == 6) { + if (tok->n_args >= 5) { router->or_port = atoi(tok->args[2]); router->socks_port = atoi(tok->args[3]); router->dir_port = atoi(tok->args[4]); - router->bandwidthrate = atoi(tok->args[5]); - ports_set = bw_set = 1; + ports_set = 1; + /* XXXX Remove this after everyone has moved to 0.0.6 */ + if (tok->n_args == 6) { + router->bandwidthrate = atoi(tok->args[5]); + router->bandwidthburst = router->bandwidthrate * 10; + bw_set = 1; + } } } else { - log_fn(LOG_WARN,"Wrong # of arguments to \"router\""); + log_fn(LOG_WARN,"Wrong # of arguments to \"router\" (%d)",tok->n_args); goto err; } @@ -998,11 +984,12 @@ routerinfo_t *router_get_entry_from_string(const char *s, log_fn(LOG_WARN,"Redundant bandwidth line"); goto err; } else if (tok) { - if (tok->n_args < 1) { + if (tok->n_args < 2) { log_fn(LOG_WARN,"Not enough arguments to \"bandwidth\""); goto err; } router->bandwidthrate = atoi(tok->args[0]); + router->bandwidthburst = atoi(tok->args[1]); bw_set = 1; } @@ -1020,12 +1007,9 @@ routerinfo_t *router_get_entry_from_string(const char *s, router->onion_pkey = tok->key; tok->key = NULL; /* Prevent free */ - if (!(tok = find_first_by_keyword(tokens, K_LINK_KEY))) { - log_fn(LOG_WARN, "Missing onion key"); goto err; + if ((tok = find_first_by_keyword(tokens, K_LINK_KEY))) { + log_fn(LOG_INFO, "Skipping obsolete link-key"); goto err; } - /* XXX Check key length */ - router->link_pkey = tok->key; - tok->key = NULL; /* Prevent free */ if (!(tok = find_first_by_keyword(tokens, K_SIGNING_KEY))) { log_fn(LOG_WARN, "Missing onion key"); goto err; @@ -1077,16 +1061,6 @@ routerinfo_t *router_get_entry_from_string(const char *s, router->platform = tor_strdup(""); } -#if XXXBC - router->bandwidthburst = atoi(ARGS[6]); - if (!router->bandwidthburst) { - log_fn(LOG_WARN,"bandwidthburst unreadable or 0. Failing."); - goto err; - } -#else - router->bandwidthburst = 10*router->bandwidthrate; -#endif - log_fn(LOG_DEBUG,"or_port %d, socks_port %d, dir_port %d, bandwidthrate %u, bandwidthburst %u.", router->or_port, router->socks_port, router->dir_port, (unsigned) router->bandwidthrate, (unsigned) router->bandwidthburst); diff --git a/src/or/test.c b/src/or/test.c index 61daaf589b..ab0f23275d 100644 --- a/src/or/test.c +++ b/src/or/test.c @@ -650,7 +650,7 @@ test_onion_handshake() { /* server handshake */ memset(s_buf, 0, ONIONSKIN_REPLY_LEN); memset(s_keys, 0, 40); - test_assert(! onion_skin_server_handshake(c_buf, pk, s_buf, s_keys, 40)); + test_assert(! onion_skin_server_handshake(c_buf, pk, NULL, s_buf, s_keys, 40)); /* client handshake 2 */ memset(c_keys, 0, 40); @@ -701,8 +701,8 @@ test_dir_format() r1.dir_port = 9003; r1.onion_pkey = pk1; r1.identity_pkey = pk2; - r1.link_pkey = pk3; - r1.bandwidthrate = r1.bandwidthburst = 1000; + r1.bandwidthrate = 1000; + r1.bandwidthburst = 5000; r1.exit_policy = NULL; r1.nickname = "Magri"; r1.platform = tor_strdup(platform); @@ -727,7 +727,6 @@ test_dir_format() r2.dir_port = 0; r2.onion_pkey = pk2; r2.identity_pkey = pk1; - r2.link_pkey = pk2; r2.bandwidthrate = r2.bandwidthburst = 3000; r2.exit_policy = &ex1; r2.nickname = "Fred"; @@ -742,15 +741,14 @@ test_dir_format() memset(buf, 0, 2048); test_assert(router_dump_router_to_string(buf, 2048, &r1, pk2)>0); - strcpy(buf2, "router Magri testaddr1.foo.bar 9000 9002 9003 1000\n" + strcpy(buf2, "router Magri testaddr1.foo.bar 9000 9002 9003\n" "platform Tor "VERSION" on "); strcat(buf2, get_uname()); strcat(buf2, "\n" "published 1970-01-01 00:00:00\n" + "bandwidth 1000 5000\n" "onion-key\n"); strcat(buf2, pk1_str); - strcat(buf2, "link-key\n"); - strcat(buf2, pk3_str); strcat(buf2, "signing-key\n"); strcat(buf2, pk2_str); strcat(buf2, "router-signature\n"); @@ -767,9 +765,8 @@ test_dir_format() test_eq(rp1->socks_port, r1.socks_port); test_eq(rp1->dir_port, r1.dir_port); test_eq(rp1->bandwidthrate, r1.bandwidthrate); -// test_eq(rp1->bandwidthburst, r1.bandwidthburst); + test_eq(rp1->bandwidthburst, r1.bandwidthburst); test_assert(crypto_pk_cmp_keys(rp1->onion_pkey, pk1) == 0); - test_assert(crypto_pk_cmp_keys(rp1->link_pkey, pk3) == 0); test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0); test_assert(rp1->exit_policy == NULL); @@ -908,7 +905,7 @@ main(int c, char**v){ test_onion(); test_onion_handshake(); puts("\n========================= Directory Formats ==============="); -// add_stream_log(LOG_DEBUG, NULL, stdout); + /* add_stream_log(LOG_DEBUG, NULL, stdout); */ test_dir_format(); puts("\n========================= Rendezvous functionality ========"); test_rend_fns();