diff --git a/src/common/tortls.c b/src/common/tortls.c index 2b12eea8dc..0ba47baa44 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -207,6 +207,7 @@ static int tor_tls_context_init_one(tor_tls_context_t **ppcontext, unsigned int key_lifetime); static tor_tls_context_t *tor_tls_context_new(crypto_pk_env_t *identity, unsigned int key_lifetime); +static int check_cert_lifetime_internal(const X509 *cert, int tolerance); /** Global TLS contexts. We keep them here because nobody else needs * to touch them. */ @@ -823,6 +824,84 @@ tor_tls_get_my_certs(int server, return 0; } +/** Return true iff a and b represent the same public key. */ +static int +pkey_eq(EVP_PKEY *a, EVP_PKEY *b) +{ + /* We'd like to do this, but openssl 0.9.7 doesn't have it: + return EVP_PKEY_cmp(a,b) == 1; + */ + unsigned char *a_enc=NULL, *b_enc=NULL, *a_ptr, *b_ptr; + int a_len1, b_len1, a_len2, b_len2, result; + a_len1 = i2d_PublicKey(a, NULL); + b_len1 = i2d_PublicKey(b, NULL); + if (a_len1 != b_len1) + return 0; + a_ptr = a_enc = tor_malloc(a_len1); + b_ptr = b_enc = tor_malloc(b_len1); + a_len2 = i2d_PublicKey(a, &a_ptr); + b_len2 = i2d_PublicKey(b, &b_ptr); + tor_assert(a_len2 == a_len1); + tor_assert(b_len2 == b_len1); + result = tor_memeq(a_enc, b_enc, a_len1); + tor_free(a_enc); + tor_free(b_enc); + return result; +} + +/** Return true iff the other side of tls has authenticated to us, and + * the key certified in cert is the same as the key they used to do it. + */ +int +tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert) +{ + X509 *peercert = SSL_get_peer_certificate(tls->ssl); + EVP_PKEY *link_key = NULL, *cert_key = NULL; + int result; + + if (!peercert) + return 0; + link_key = X509_get_pubkey(peercert); + cert_key = X509_get_pubkey(cert->cert); + + result = link_key && cert_key && pkey_eq(cert_key, link_key); + + X509_free(peercert); + if (link_key) + EVP_PKEY_free(link_key); + if (cert_key) + EVP_PKEY_free(cert_key); + + return result; +} + +/** Check wither cert is well-formed, currently live, and correctly + * signed by the public key in signing_cert. Return 0 if the cert is + * good, and -1 if it's bad or we couldn't check it. */ +int +tor_tls_cert_is_valid(const tor_cert_t *cert, + const tor_cert_t *signing_cert) +{ + EVP_PKEY *signing_key = X509_get_pubkey(signing_cert->cert); + int r; + if (!signing_key) + return 0; + r = X509_verify(cert->cert, signing_key); + EVP_PKEY_free(signing_key); + if (r <= 0) + return 0; + + /* okay, the signature checked out right. Now let's check the check the + * lifetime. */ + /*XXXX tolerance might be iffy here */ + if (check_cert_lifetime_internal(cert->cert, 60*60) < 0) + return 0; + + /* XXXX compare DNs or anything? */ + + return -1; +} + /** Increase the reference count of ctx. */ static void tor_tls_context_incref(tor_tls_context_t *ctx) @@ -1709,7 +1788,7 @@ tor_tls_peer_has_cert(tor_tls_t *tls) /** Warn that a certificate lifetime extends through a certain range. */ static void -log_cert_lifetime(X509 *cert, const char *problem) +log_cert_lifetime(const X509 *cert, const char *problem) { BIO *bio = NULL; BUF_MEM *buf; @@ -1856,25 +1935,14 @@ tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key) int tor_tls_check_lifetime(tor_tls_t *tls, int tolerance) { - time_t now, t; X509 *cert; int r = -1; - now = time(NULL); - if (!(cert = SSL_get_peer_certificate(tls->ssl))) goto done; - t = now + tolerance; - if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) { - log_cert_lifetime(cert, "not yet valid"); + if (check_cert_lifetime_internal(cert, tolerance) < 0) goto done; - } - t = now - tolerance; - if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) { - log_cert_lifetime(cert, "already expired"); - goto done; - } r = 0; done: @@ -1886,6 +1954,30 @@ tor_tls_check_lifetime(tor_tls_t *tls, int tolerance) return r; } +/** Helper: check whether cert is currently live, give or take + * tolerance seconds. If it is live, return 0. If it is not live, + * log a message and return -1. */ +static int +check_cert_lifetime_internal(const X509 *cert, int tolerance) +{ + time_t now, t; + + now = time(NULL); + + t = now + tolerance; + if (X509_cmp_time(X509_get_notBefore(cert), &t) > 0) { + log_cert_lifetime(cert, "not yet valid"); + return -1; + } + t = now - tolerance; + if (X509_cmp_time(X509_get_notAfter(cert), &t) < 0) { + log_cert_lifetime(cert, "already expired"); + return -1; + } + + return 0; +} + /** Return the number of bytes available for reading from tls. */ int diff --git a/src/common/tortls.h b/src/common/tortls.h index a6aed29852..36309afd4d 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -115,6 +115,9 @@ void tor_cert_get_der(const tor_cert_t *cert, int tor_tls_get_my_certs(int server, const tor_cert_t **link_cert_out, const tor_cert_t **id_cert_out); +int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert); +int tor_tls_cert_is_valid(const tor_cert_t *cert, + const tor_cert_t *signing_cert); #endif