aboutsummaryrefslogtreecommitdiff
path: root/src/common/tortls.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/tortls.c')
-rw-r--r--src/common/tortls.c154
1 files changed, 137 insertions, 17 deletions
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 1075de977..d6a0d8488 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -815,27 +815,22 @@ log_cert_lifetime(X509 *cert, const char *problem)
tor_free(s2);
}
-/** If the provided tls connection is authenticated and has a
- * certificate that is currently valid and signed, then set
- * *<b>identity_key</b> to the identity certificate's key and return
- * 0. Else, return -1 and log complaints with log-level <b>severity</b>.
- */
-int
-tor_tls_verify_v1(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
+/** DOCDOC helper.
+ * cert_out needs to be freed. id_cert_out doesn't. */
+static void
+try_to_extract_certs_from_tls(int severity, tor_tls_t *tls,
+ X509 **cert_out, X509 **id_cert_out)
{
X509 *cert = NULL, *id_cert = NULL;
STACK_OF(X509) *chain = NULL;
- EVP_PKEY *id_pkey = NULL;
- RSA *rsa;
- int num_in_chain;
- int r = -1, i;
-
- *identity_key = NULL;
+ int num_in_chain, i;
+ *cert_out = *id_cert_out = NULL;
if (!(cert = SSL_get_peer_certificate(tls->ssl)))
- goto done;
+ return;
+ *cert_out = cert;
if (!(chain = SSL_get_peer_cert_chain(tls->ssl)))
- goto done;
+ return;
num_in_chain = sk_X509_num(chain);
/* 1 means we're receiving (server-side), and it's just the id_cert.
* 2 means we're connecting (client-side), and it's both the link
@@ -845,18 +840,38 @@ tor_tls_verify_v1(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
log_fn(severity,LD_PROTOCOL,
"Unexpected number of certificates in chain (%d)",
num_in_chain);
- goto done;
+ return;
}
for (i=0; i<num_in_chain; ++i) {
id_cert = sk_X509_value(chain, i);
if (X509_cmp(id_cert, cert) != 0)
break;
}
+ *id_cert_out = id_cert;
+}
+
+/** If the provided tls connection is authenticated and has a
+ * certificate that is currently valid and signed, then set
+ * *<b>identity_key</b> to the identity certificate's key and return
+ * 0. Else, return -1 and log complaints with log-level <b>severity</b>.
+ */
+int
+tor_tls_verify_v1(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
+{
+ X509 *cert = NULL, *id_cert = NULL;
+ EVP_PKEY *id_pkey = NULL;
+ RSA *rsa;
+ int r = -1;
+
+ *identity_key = NULL;
+
+ try_to_extract_certs_from_tls(severity, tls, &cert, &id_cert);
+ if (!cert)
+ goto done;
if (!id_cert) {
log_fn(severity,LD_PROTOCOL,"No distinct identity certificate found");
goto done;
}
-
if (!(id_pkey = X509_get_pubkey(id_cert)) ||
X509_verify(cert, id_pkey) <= 0) {
log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
@@ -884,6 +899,111 @@ tor_tls_verify_v1(int severity, tor_tls_t *tls, crypto_pk_env_t **identity_key)
return r;
}
+/** DOCDOC
+ *
+ * Returns 1 on "verification is done", 0 on "still need LINK_AUTH."
+ */
+int
+tor_tls_verify_certs_v2(int severity, tor_tls_t *tls,
+ const char *cert_str, size_t cert_len,
+ const char *id_cert_str, size_t id_cert_len,
+ crypto_pk_env_t **cert_key_out,
+ char *conn_cert_digest_out,
+ char *id_digest_out)
+{
+ X509 *cert = NULL, *id_cert = NULL;
+ EVP_PKEY *id_pkey = NULL, *cert_pkey = NULL;
+ int free_id_cert = 0, peer_used_tls_cert = 0;
+ int r = -1;
+
+ tor_assert(cert_key_out);
+ tor_assert(conn_cert_digest_out);
+ tor_assert(id_digest_out);
+
+ *cert_key_out = NULL;
+
+ if (cert_str && cert_len) {
+ /*XXXX020 warn on error. */
+ const unsigned char *cp = (const unsigned char*) cert_str;
+ cert = d2i_X509(NULL, &cp, cert_len);
+ }
+ if (id_cert_str && id_cert_len) {
+ /*XXXX020 warn on error. */
+ const unsigned char *cp = (const unsigned char*) id_cert_str;
+ id_cert = d2i_X509(NULL, &cp, id_cert_len);
+ if (id_cert)
+ free_id_cert = 1;
+ }
+
+ if (cert) {
+ int cmp = 0;
+ X509 *cert_tmp = SSL_get_peer_certificate(tls->ssl);
+ if (cert_tmp) {
+ peer_used_tls_cert = 1;
+ cmp = X509_cmp(cert, cert_tmp);
+ X509_free(cert_tmp);
+ }
+ if (cmp != 0) {
+ log_fn(severity, LD_PROTOCOL,
+ "Certificate in CERT cell didn't match TLS cert.");
+ goto done;
+ }
+ }
+
+ if (!cert || !id_cert) {
+ X509 *c=NULL, *id=NULL;
+ try_to_extract_certs_from_tls(severity, tls, &c, &id);
+ if (c) {
+ if (!cert)
+ cert = c;
+ else
+ X509_free(c);
+ }
+ if (id && !id_cert)
+ id_cert = id;
+ }
+ if (!id_cert || !cert)
+ goto done;
+
+ if (!(id_pkey = X509_get_pubkey(id_cert)) ||
+ X509_verify(cert, id_pkey) <= 0) {
+ log_fn(severity,LD_PROTOCOL,"X509_verify on cert and pkey returned <= 0");
+ tls_log_errors(severity,"verifying certificate");
+ goto done;
+ }
+
+ {
+ crypto_pk_env_t *i = _crypto_new_pk_env_evp_pkey(id_pkey);
+ if (!i)
+ goto done;
+ crypto_pk_get_digest(i, id_digest_out);
+ crypto_free_pk_env(i);
+ }
+ if (!(cert_pkey = X509_get_pubkey(cert)))
+ goto done;
+ if (!(*cert_key_out = _crypto_new_pk_env_evp_pkey(cert_pkey)))
+ goto done;
+
+ {
+ unsigned int len = 0;
+ X509_digest(cert, EVP_sha1(), (unsigned char*)conn_cert_digest_out, &len);
+ tor_assert(len == DIGEST_LEN);
+ }
+
+ r = peer_used_tls_cert ? 1 : 0;
+ done:
+ if (cert)
+ X509_free(cert);
+ if (id_cert && free_id_cert)
+ X509_free(id_cert);
+ if (id_pkey)
+ EVP_PKEY_free(id_pkey);
+ if (cert_pkey)
+ EVP_PKEY_free(cert_pkey);
+
+ return r;
+}
+
/** Check whether the certificate set on the connection <b>tls</b> is
* expired or not-yet-valid, give or take <b>tolerance</b>
* seconds. Return 0 for valid, -1 for failure.