summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Færøy <ahf@torproject.org>2020-03-31 02:33:54 +0000
committerNick Mathewson <nickm@torproject.org>2020-07-06 16:19:16 -0400
commitb46984e97ec4064ac8178ea9b3bf6985a4f2f632 (patch)
tree3da4540f64d22f07bbc350832b54531618e3b3a8
parent33e1c2e6fd614f8cb42a6d5758d411d3f8d5411c (diff)
downloadtor-b46984e97ec4064ac8178ea9b3bf6985a4f2f632.tar.gz
tor-b46984e97ec4064ac8178ea9b3bf6985a4f2f632.zip
Fix out-of-bound memory read in `tor_tls_cert_matches_key()` for NSS.
This patch fixes an out-of-bound memory read in `tor_tls_cert_matches_key()` when Tor is compiled to use Mozilla's NSS instead of OpenSSL. The NSS library stores some length fields in bits instead of bytes, but the comparison function found in `SECITEM_ItemsAreEqual()` needs the length to be encoded in bytes. This means that for a 140-byte, DER-encoded, SubjectPublicKeyInfo struct (with a 1024-bit RSA public key in it), we would ask `SECITEM_ItemsAreEqual()` to compare the first 1120 bytes instead of 140 (140bytes * 8bits = 1120bits). This patch fixes the issue by converting from bits to bytes before calling `SECITEM_ItemsAreEqual()` and convert the `len`-fields back to bits before we leave the function. This patch is part of the fix for TROVE-2020-001. See: https://bugs.torproject.org/33119
-rw-r--r--changes/bug331194
-rw-r--r--src/lib/tls/tortls_nss.c38
2 files changed, 36 insertions, 6 deletions
diff --git a/changes/bug33119 b/changes/bug33119
new file mode 100644
index 0000000000..c976654b26
--- /dev/null
+++ b/changes/bug33119
@@ -0,0 +1,4 @@
+ o Major bugfixes (NSS):
+ - Fix out-of-bound memory access in `tor_tls_cert_matches_key()` when Tor is
+ compiled with NSS support. Fixes bug 33119; bugfix on 0.3.5.1-alpha. This
+ issue is also tracked as TROVE-2020-001.
diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c
index 3c62e98df1..f7792e07a2 100644
--- a/src/lib/tls/tortls_nss.c
+++ b/src/lib/tls/tortls_nss.c
@@ -713,23 +713,49 @@ MOCK_IMPL(int,
tor_tls_cert_matches_key,(const tor_tls_t *tls,
const struct tor_x509_cert_t *cert))
{
- tor_assert(tls);
tor_assert(cert);
+ tor_assert(cert->cert);
+
int rv = 0;
- CERTCertificate *peercert = SSL_PeerCertificate(tls->ssl);
- if (!peercert)
+ tor_x509_cert_t *peercert = tor_tls_get_peer_cert((tor_tls_t *)tls);
+
+ if (!peercert || !peercert->cert)
goto done;
- CERTSubjectPublicKeyInfo *peer_info = &peercert->subjectPublicKeyInfo;
+
+ CERTSubjectPublicKeyInfo *peer_info = &peercert->cert->subjectPublicKeyInfo;
CERTSubjectPublicKeyInfo *cert_info = &cert->cert->subjectPublicKeyInfo;
+
+ /* NSS stores the `len` field in bits, instead of bytes, for the
+ * `subjectPublicKey` field in CERTSubjectPublicKeyInfo, but
+ * `SECITEM_ItemsAreEqual()` compares the two bitstrings using a length field
+ * defined in bytes.
+ *
+ * We convert the `len` field from bits to bytes, do our comparison with
+ * `SECITEM_ItemsAreEqual()`, and reset the length field from bytes to bits
+ * again.
+ *
+ * See also NSS's own implementation of `SECKEY_CopySubjectPublicKeyInfo()`
+ * in seckey.c in the NSS source tree. This function also does the conversion
+ * between bits and bytes.
+ */
+ unsigned int peer_info_orig_len = peer_info->subjectPublicKey.len;
+ unsigned int cert_info_orig_len = cert_info->subjectPublicKey.len;
+
+ peer_info->subjectPublicKey.len = (peer_info_orig_len >> 3);
+ cert_info->subjectPublicKey.len = (cert_info_orig_len >> 3);
+
rv = SECOID_CompareAlgorithmID(&peer_info->algorithm,
&cert_info->algorithm) == 0 &&
SECITEM_ItemsAreEqual(&peer_info->subjectPublicKey,
&cert_info->subjectPublicKey);
+ peer_info->subjectPublicKey.len = peer_info_orig_len;
+ cert_info->subjectPublicKey.len = cert_info_orig_len;
+
done:
- if (peercert)
- CERT_DestroyCertificate(peercert);
+ tor_x509_cert_free(peercert);
+
return rv;
}