Merge remote-tracking branch 'asn-mytor/bug4548_take2'

This commit is contained in:
Nick Mathewson 2011-11-29 18:30:41 -05:00
commit da6c136817
11 changed files with 391 additions and 49 deletions

6
changes/bug4548 Normal file
View File

@ -0,0 +1,6 @@
o Privacy/anonymity features (bridge detection):
- Introduce a new config option 'DynamicDHGroups', enabled by
default, which provides each bridge with a unique prime DH
modulus to be used during SSL handshakes. This option attempts
to help against censors using the Apache DH modulus as a static
identifier for bridges.

View File

@ -245,6 +245,12 @@ Other options can be specified either on the command-line (--option
distinguishable from other users, because you won't believe the same
authorities they do.
**DynamicPrimes** **0**|**1**::
If this option is set to 1, use a dynamic Diffie-Hellman prime
modulus instead of using the modulus of Apache's mod_ssl. This
option might help circumvent censorship based on static
Diffie-Hellman parameters. (Default: 1).
**AlternateDirAuthority** [__nickname__] [**flags**] __address__:__port__ __fingerprint__ +
**AlternateHSAuthority** [__nickname__] [**flags**] __address__:__port__ __fingerprint__ +

View File

@ -1812,6 +1812,9 @@ crypto_hmac_sha256(char *hmac_out,
/* DH */
/** Our DH 'g' parameter */
#define DH_GENERATOR 2
/** Shared P parameter for our circuit-crypto DH key exchanges. */
static BIGNUM *dh_param_p = NULL;
/** Shared P parameter for our TLS DH key exchanges. */
@ -1819,49 +1822,303 @@ static BIGNUM *dh_param_p_tls = NULL;
/** Shared G parameter for our DH key exchanges. */
static BIGNUM *dh_param_g = NULL;
/** Generate and return a reasonable and safe DH parameter p. */
static BIGNUM *
crypto_generate_dynamic_dh_modulus(void)
{
BIGNUM *dynamic_dh_modulus;
DH *dh_parameters;
int r, dh_codes;
char *s;
dynamic_dh_modulus = BN_new();
tor_assert(dynamic_dh_modulus);
dh_parameters = DH_generate_parameters(DH_BYTES*8, DH_GENERATOR, NULL, NULL);
tor_assert(dh_parameters);
r = DH_check(dh_parameters, &dh_codes);
tor_assert(r && !dh_codes);
BN_copy(dynamic_dh_modulus, dh_parameters->p);
tor_assert(dynamic_dh_modulus);
DH_free(dh_parameters);
{ /* log the dynamic DH modulus: */
s = BN_bn2hex(dynamic_dh_modulus);
tor_assert(s);
log_info(LD_OR, "Dynamic DH modulus generated: [%s]", s);
OPENSSL_free(s);
}
return dynamic_dh_modulus;
}
/** Store our dynamic DH modulus (and its group parameters) to
<b>fname</b> for future use. */
static int
crypto_store_dynamic_dh_modulus(const char *fname)
{
int len, new_len;
DH *dh = NULL;
unsigned char *dh_string_repr = NULL, *cp = NULL;
char *base64_encoded_dh = NULL;
int retval = -1;
tor_assert(fname);
if (!dh_param_p_tls) {
log_info(LD_CRYPTO, "Tried to store a DH modulus that does not exist.");
goto done;
}
if (!(dh = DH_new()))
goto done;
if (!(dh->p = BN_dup(dh_param_p_tls)))
goto done;
if (!(dh->g = BN_new()))
goto done;
if (!BN_set_word(dh->g, DH_GENERATOR))
goto done;
len = i2d_DHparams(dh, NULL);
if (len < 0) {
log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (1).");
goto done;
}
cp = dh_string_repr = tor_malloc_zero(len+1);
len = i2d_DHparams(dh, &cp);
if ((len < 0) || ((cp - dh_string_repr) != len)) {
log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2).");
goto done;
}
base64_encoded_dh = tor_malloc_zero(len * 2); /* should be enough */
new_len = base64_encode(base64_encoded_dh, len * 2,
(char *)dh_string_repr, len);
if (new_len < 0) {
log_warn(LD_CRYPTO, "Error occured while base64-encoding DH modulus.");
goto done;
}
if (write_bytes_to_new_file(fname, base64_encoded_dh, new_len, 0) < 0) {
log_info(LD_CRYPTO, "'%s' was already occupied.", fname);
goto done;
}
retval = 0;
done:
if (dh)
DH_free(dh);
tor_free(dh_string_repr);
tor_free(base64_encoded_dh);
return retval;
}
/** Return the dynamic DH modulus stored in <b>fname</b>. If there is no
dynamic DH modulus stored in <b>fname</b>, return NULL. */
static BIGNUM *
crypto_get_stored_dynamic_dh_modulus(const char *fname)
{
int retval;
char *contents = NULL;
int dh_codes;
char *fname_new = NULL;
DH *stored_dh = NULL;
BIGNUM *dynamic_dh_modulus = NULL;
int length = 0;
unsigned char *base64_decoded_dh = NULL;
const unsigned char *cp = NULL;
tor_assert(fname);
contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
if (!contents) {
log_info(LD_CRYPTO, "Could not open file '%s'", fname);
goto done; /*usually means that ENOENT. don't try to move file to broken.*/
}
/* 'fname' contains the DH parameters stored in base64-ed DER
format. We are only interested in the DH modulus. */
cp = base64_decoded_dh = tor_malloc_zero(strlen(contents));
length = base64_decode((char *)base64_decoded_dh, strlen(contents),
contents, strlen(contents));
if (length < 0) {
log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (base64).");
goto err;
}
stored_dh = d2i_DHparams(NULL, &cp, length);
if ((!stored_dh) || (cp - base64_decoded_dh != length)) {
log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (d2i).");
goto err;
}
{ /* check the cryptographic qualities of the stored dynamic DH modulus: */
retval = DH_check(stored_dh, &dh_codes);
if (!retval || dh_codes) {
log_warn(LD_CRYPTO, "Stored dynamic DH modulus is not a safe prime.");
goto err;
}
retval = DH_size(stored_dh);
if (retval < DH_BYTES) {
log_warn(LD_CRYPTO, "Stored dynamic DH modulus is smaller "
"than '%d' bits.", DH_BYTES*8);
goto err;
}
if (!BN_is_word(stored_dh->g, 2)) {
log_warn(LD_CRYPTO, "Stored dynamic DH parameters do not use '2' "
"as the group generator.");
goto err;
}
}
{ /* log the dynamic DH modulus: */
char *s = BN_bn2hex(stored_dh->p);
tor_assert(s);
log_info(LD_OR, "Found stored dynamic DH modulus: [%s]", s);
OPENSSL_free(s);
}
goto done;
err:
{ /* move broken prime to $filename.broken */
fname_new = tor_malloc(strlen(fname) + 8);
/* no can do if these functions return error */
strlcpy(fname_new, fname, strlen(fname) + 8);
strlcat(fname_new, ".broken", strlen(fname) + 8);
log_warn(LD_CRYPTO, "Moving broken dynamic DH prime to '%s'.", fname_new);
if (replace_file(fname, fname_new))
log_notice(LD_CRYPTO, "Error while moving '%s' to '%s'.",
fname, fname_new);
tor_free(fname_new);
}
if (stored_dh) {
DH_free(stored_dh);
stored_dh = NULL;
}
done:
tor_free(contents);
tor_free(base64_decoded_dh);
if (stored_dh) {
dynamic_dh_modulus = BN_dup(stored_dh->p);
DH_free(stored_dh);
}
return dynamic_dh_modulus;
}
/** Set the global TLS Diffie-Hellman modulus.
* If <b>dynamic_dh_modulus_fname</b> is set, try to read a dynamic DH modulus
* off it and use it as the DH modulus. If that's not possible,
* generate a new dynamic DH modulus.
* If <b>dynamic_dh_modulus_fname</b> is NULL, use the Apache mod_ssl DH
* modulus. */
void
crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname)
{
BIGNUM *tls_prime = NULL;
int store_dh_prime_afterwards = 0;
int r;
/* If the space is occupied, free the previous TLS DH prime */
if (dh_param_p_tls) {
BN_free(dh_param_p_tls);
dh_param_p_tls = NULL;
}
if (dynamic_dh_modulus_fname) { /* use dynamic DH modulus: */
log_info(LD_OR, "Using stored dynamic DH modulus.");
tls_prime = crypto_get_stored_dynamic_dh_modulus(dynamic_dh_modulus_fname);
if (!tls_prime) {
log_notice(LD_OR, "Generating fresh dynamic DH modulus. "
"This might take a while...");
tls_prime = crypto_generate_dynamic_dh_modulus();
store_dh_prime_afterwards++;
}
} else { /* use the static DH prime modulus used by Apache in mod_ssl: */
tls_prime = BN_new();
tor_assert(tls_prime);
/* This is the 1024-bit safe prime that Apache uses for its DH stuff; see
* modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
* prime.
*/
r =BN_hex2bn(&tls_prime,
"D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
"BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
"467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
"DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
"B0E7393E0F24218EB3");
tor_assert(r);
}
tor_assert(tls_prime);
dh_param_p_tls = tls_prime;
if (store_dh_prime_afterwards)
/* save the new dynamic DH modulus to disk. */
if (crypto_store_dynamic_dh_modulus(dynamic_dh_modulus_fname)) {
log_notice(LD_CRYPTO, "Failed while storing dynamic DH modulus. "
"Make sure your data directory is sane.");
}
}
/** Initialize dh_param_p and dh_param_g if they are not already
* set. */
static void
init_dh_param(void)
{
BIGNUM *p, *p2, *g;
BIGNUM *circuit_dh_prime, *generator;
int r;
if (dh_param_p && dh_param_g && dh_param_p_tls)
if (dh_param_p && dh_param_g)
return;
p = BN_new();
p2 = BN_new();
g = BN_new();
tor_assert(p);
tor_assert(p2);
tor_assert(g);
circuit_dh_prime = BN_new();
generator = BN_new();
tor_assert(circuit_dh_prime && generator);
/* Set our generator for all DH parameters */
r = BN_set_word(generator, DH_GENERATOR);
tor_assert(r);
/* This is from rfc2409, section 6.2. It's a safe prime, and
supposedly it equals:
2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }.
*/
r = BN_hex2bn(&p,
r = BN_hex2bn(&circuit_dh_prime,
"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
"8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
"302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
"A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
"49286651ECE65381FFFFFFFFFFFFFFFF");
tor_assert(r);
/* This is the 1024-bit safe prime that Apache uses for its DH stuff; see
* modules/ssl/ssl_engine_dh.c */
r = BN_hex2bn(&p2,
"D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
"BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
"467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
"DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
"B0E7393E0F24218EB3");
tor_assert(r);
r = BN_set_word(g, 2);
tor_assert(r);
dh_param_p = p;
dh_param_p_tls = p2;
dh_param_g = g;
/* Set the new values as the global DH parameters. */
dh_param_p = circuit_dh_prime;
dh_param_g = generator;
/* Should be already set by config.c. */
tor_assert(dh_param_p_tls);
}
/** Number of bits to use when choosing the x or y value in a Diffie-Hellman

View File

@ -91,6 +91,8 @@ int crypto_global_cleanup(void);
crypto_pk_env_t *crypto_new_pk_env(void);
void crypto_free_pk_env(crypto_pk_env_t *env);
void crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname);
/* convenience function: wraps crypto_create_crypto_env, set_key, and init. */
crypto_cipher_env_t *crypto_create_init_cipher(const char *key,
int encrypt_mode);

View File

@ -2137,13 +2137,12 @@ write_chunks_to_file(const char *fname, const smartlist_t *chunks, int bin)
return write_chunks_to_file_impl(fname, chunks, flags);
}
/** As write_str_to_file, but does not assume a NUL-terminated
* string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
int
write_bytes_to_file(const char *fname, const char *str, size_t len,
int bin)
/** Write <b>len</b> bytes, starting at <b>str</b>, to <b>fname</b>
using the open() flags passed in <b>flags</b>. */
static int
write_bytes_to_file_impl(const char *fname, const char *str, size_t len,
int flags)
{
int flags = OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT);
int r;
sized_chunk_t c = { str, len };
smartlist_t *chunks = smartlist_create();
@ -2153,20 +2152,35 @@ write_bytes_to_file(const char *fname, const char *str, size_t len,
return r;
}
/** As write_str_to_file, but does not assume a NUL-terminated
* string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
int
write_bytes_to_file(const char *fname, const char *str, size_t len,
int bin)
{
return write_bytes_to_file_impl(fname, str, len,
OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT));
}
/** As write_bytes_to_file, but if the file already exists, append the bytes
* to the end of the file instead of overwriting it. */
int
append_bytes_to_file(const char *fname, const char *str, size_t len,
int bin)
{
int flags = OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT);
int r;
sized_chunk_t c = { str, len };
smartlist_t *chunks = smartlist_create();
smartlist_add(chunks, &c);
r = write_chunks_to_file_impl(fname, chunks, flags);
smartlist_free(chunks);
return r;
return write_bytes_to_file_impl(fname, str, len,
OPEN_FLAGS_APPEND|(bin?O_BINARY:O_TEXT));
}
/** Like write_str_to_file(), but also return -1 if there was a file
already residing in <b>fname</b>. */
int
write_bytes_to_new_file(const char *fname, const char *str, size_t len,
int bin)
{
return write_bytes_to_file_impl(fname, str, len,
OPEN_FLAGS_DONT_REPLACE|
(bin?O_BINARY:O_TEXT));
}
/** Read the contents of <b>filename</b> into a newly allocated

View File

@ -311,6 +311,7 @@ int check_private_dir(const char *dirname, cpd_check_t check,
const char *effective_user);
#define OPEN_FLAGS_REPLACE (O_WRONLY|O_CREAT|O_TRUNC)
#define OPEN_FLAGS_APPEND (O_WRONLY|O_CREAT|O_APPEND)
#define OPEN_FLAGS_DONT_REPLACE (O_CREAT|O_EXCL|O_APPEND|O_WRONLY)
typedef struct open_file_t open_file_t;
int start_writing_to_file(const char *fname, int open_flags, int mode,
open_file_t **data_out);
@ -332,6 +333,8 @@ int write_chunks_to_file(const char *fname, const struct smartlist_t *chunks,
int bin);
int append_bytes_to_file(const char *fname, const char *str, size_t len,
int bin);
int write_bytes_to_new_file(const char *fname, const char *str, size_t len,
int bin);
/** Flag for read_file_to_str: open the file in binary mode. */
#define RFTS_BIN 1

View File

@ -249,6 +249,7 @@ static config_var_t _option_vars[] = {
V(DisableAllSwap, BOOL, "0"),
V(DisableDebuggerAttachment, BOOL, "1"),
V(DisableIOCP, BOOL, "1"),
V(DynamicDHGroups, BOOL, "1"),
V(DNSPort, LINELIST, NULL),
V(DNSListenAddress, LINELIST, NULL),
V(DownloadExtraInfo, BOOL, "0"),
@ -1285,6 +1286,24 @@ get_effective_bwburst(const or_options_t *options)
return (uint32_t)bw;
}
/** Return True if any changes from <b>old_options</b> to
* <b>new_options</b> needs us to refresh our TLS context. */
static int
options_transition_requires_fresh_tls_context(const or_options_t *old_options,
const or_options_t *new_options)
{
tor_assert(new_options);
if (!old_options)
return 0;
if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) {
return 1;
}
return 0;
}
/** Fetch the active option list, and take actions based on it. All of the
* things we do should survive being done repeatedly. If present,
* <b>old_options</b> contains the previous value of the options.
@ -1388,6 +1407,29 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
/* If needed, generate a new TLS DH prime according to the current torrc. */
if (server_mode(options)) {
if (!old_options) {
if (options->DynamicDHGroups) {
char *fname = get_datadir_fname2("keys", "dynamic_dh_params");
crypto_set_tls_dh_prime(fname);
tor_free(fname);
} else {
crypto_set_tls_dh_prime(NULL);
}
} else {
if (options->DynamicDHGroups && !old_options->DynamicDHGroups) {
char *fname = get_datadir_fname2("keys", "dynamic_dh_params");
crypto_set_tls_dh_prime(fname);
tor_free(fname);
} else if (!options->DynamicDHGroups && old_options->DynamicDHGroups) {
crypto_set_tls_dh_prime(NULL);
}
}
} else { /* clients don't need a dynamic DH prime. */
crypto_set_tls_dh_prime(NULL);
}
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@ -1397,6 +1439,13 @@ options_act(const or_options_t *old_options)
log_warn(LD_BUG,"Error initializing keys; exiting");
return -1;
}
} else if (old_options &&
options_transition_requires_fresh_tls_context(old_options,
options)) {
if (router_initialize_tls_context() < 0) {
log_warn(LD_BUG,"Error initializing TLS context.");
return -1;
}
}
/* Write our PID to the PID file. If we do not have write permissions we

View File

@ -1189,10 +1189,7 @@ run_scheduled_events(time_t now)
last_rotated_x509_certificate = now;
if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
log_info(LD_GENERAL,"Rotating tls context.");
if (tor_tls_context_init(public_server_mode(options),
get_tlsclient_identity_key(),
is_server ? get_server_identity_key() : NULL,
MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
if (router_initialize_tls_context() < 0) {
log_warn(LD_BUG, "Error reinitializing TLS context");
/* XXX is it a bug here, that we just keep going? -RD */
}

View File

@ -2889,6 +2889,8 @@ typedef struct {
char *Address; /**< OR only: configured address for this onion router. */
char *PidFile; /**< Where to store PID of Tor process. */
int DynamicDHGroups; /**< Dynamic generation of prime moduli for use in DH.*/
routerset_t *ExitNodes; /**< Structure containing nicknames, digests,
* country codes and IP address patterns of ORs to
* consider as exits. */

View File

@ -484,6 +484,16 @@ v3_authority_check_key_expiry(void)
last_warned = now;
}
int
router_initialize_tls_context(void)
{
return tor_tls_context_init(public_server_mode(get_options()),
get_tlsclient_identity_key(),
server_mode(get_options()) ?
get_server_identity_key() : NULL,
MAX_SSL_KEY_LIFETIME_ADVERTISED);
}
/** Initialize all OR private keys, and the TLS context, as necessary.
* On OPs, this only initializes the tls context. Return 0 on success,
* or -1 if Tor should die.
@ -530,10 +540,7 @@ init_keys(void)
}
set_client_identity_key(prkey);
/* Create a TLS context. */
if (tor_tls_context_init(0,
get_tlsclient_identity_key(),
NULL,
MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
if (router_initialize_tls_context() < 0) {
log_err(LD_GENERAL,"Error creating TLS context for Tor client.");
return -1;
}
@ -626,13 +633,11 @@ init_keys(void)
tor_free(keydir);
/* 3. Initialize link key and TLS context. */
if (tor_tls_context_init(public_server_mode(options),
get_tlsclient_identity_key(),
get_server_identity_key(),
MAX_SSL_KEY_LIFETIME_ADVERTISED) < 0) {
if (router_initialize_tls_context() < 0) {
log_err(LD_GENERAL,"Error initializing TLS context");
return -1;
}
/* 4. Build our router descriptor. */
/* Must be called after keys are initialized. */
mydesc = router_get_my_descriptor();

View File

@ -30,6 +30,7 @@ crypto_pk_env_t *init_key_from_file(const char *fname, int generate,
int severity);
void v3_authority_check_key_expiry(void);
int router_initialize_tls_context(void);
int init_keys(void);
int check_whether_orport_reachable(void);