blob: b144e9494facf8bedba039702861cfe213e01e81 [file] [log] [blame]
diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c
index c5cb1eb..299e414 100644
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -8,6 +8,7 @@
/* TODO(ekr): Implement HelloVerifyRequest on server side. OK for now. */
+#define _GNU_SOURCE 1
#include "cert.h"
#include "ssl.h"
#include "cryptohi.h" /* for DSAU_ stuff */
@@ -46,6 +47,9 @@
#ifdef NSS_ENABLE_ZLIB
#include "zlib.h"
#endif
+#ifdef LINUX
+#include <dlfcn.h>
+#endif
#ifndef PK11_SETATTRS
#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \
@@ -1897,6 +1901,63 @@ ssl3_BuildRecordPseudoHeader(unsigned char *out,
return 13;
}
+typedef SECStatus (*PK11CryptFcn)(
+ PK11SymKey *symKey, CK_MECHANISM_TYPE mechanism, SECItem *param,
+ unsigned char *out, unsigned int *outLen, unsigned int maxLen,
+ const unsigned char *in, unsigned int inLen);
+
+static PK11CryptFcn pk11_encrypt = NULL;
+static PK11CryptFcn pk11_decrypt = NULL;
+
+static PRCallOnceType resolvePK11CryptOnce;
+
+static PRStatus
+ssl3_ResolvePK11CryptFunctions(void)
+{
+#ifdef LINUX
+ /* On Linux we use the system NSS libraries. Look up the PK11_Encrypt and
+ * PK11_Decrypt functions at run time. */
+ pk11_encrypt = (PK11CryptFcn)dlsym(RTLD_DEFAULT, "PK11_Encrypt");
+ pk11_decrypt = (PK11CryptFcn)dlsym(RTLD_DEFAULT, "PK11_Decrypt");
+ return PR_SUCCESS;
+#else
+ /* On other platforms we use our own copy of NSS. PK11_Encrypt and
+ * PK11_Decrypt are known to be available. */
+ pk11_encrypt = PK11_Encrypt;
+ pk11_decrypt = PK11_Decrypt;
+ return PR_SUCCESS;
+#endif
+}
+
+/*
+ * In NSS 3.15, PK11_Encrypt and PK11_Decrypt were added to provide access
+ * to the AES GCM implementation in the NSS softoken. So the presence of
+ * these two functions implies the NSS version supports AES GCM.
+ */
+static PRBool
+ssl3_HasGCMSupport(void)
+{
+ (void)PR_CallOnce(&resolvePK11CryptOnce, ssl3_ResolvePK11CryptFunctions);
+ return pk11_encrypt != NULL;
+}
+
+/* On this socket, disable the GCM cipher suites */
+SECStatus
+ssl3_DisableGCMSuites(sslSocket * ss)
+{
+ unsigned int i;
+
+ for (i = 0; i < PR_ARRAY_SIZE(cipher_suite_defs); i++) {
+ const ssl3CipherSuiteDef *cipher_def = &cipher_suite_defs[i];
+ if (cipher_def->bulk_cipher_alg == cipher_aes_128_gcm) {
+ SECStatus rv = ssl3_CipherPrefSet(ss, cipher_def->cipher_suite,
+ PR_FALSE);
+ PORT_Assert(rv == SECSuccess); /* else is coding error */
+ }
+ }
+ return SECSuccess;
+}
+
static SECStatus
ssl3_AESGCM(ssl3KeyMaterial *keys,
PRBool doDecrypt,
@@ -1948,10 +2009,10 @@ ssl3_AESGCM(ssl3KeyMaterial *keys,
gcmParams.ulTagBits = tagSize * 8;
if (doDecrypt) {
- rv = PK11_Decrypt(keys->write_key, CKM_AES_GCM, &param, out, &uOutLen,
+ rv = pk11_decrypt(keys->write_key, CKM_AES_GCM, &param, out, &uOutLen,
maxout, in, inlen);
} else {
- rv = PK11_Encrypt(keys->write_key, CKM_AES_GCM, &param, out, &uOutLen,
+ rv = pk11_encrypt(keys->write_key, CKM_AES_GCM, &param, out, &uOutLen,
maxout, in, inlen);
}
*outlen += (int) uOutLen;
@@ -5337,6 +5398,10 @@ ssl3_SendClientHello(sslSocket *ss, PRBool resending)
ssl3_DisableNonDTLSSuites(ss);
}
+ if (!ssl3_HasGCMSupport()) {
+ ssl3_DisableGCMSuites(ss);
+ }
+
/* how many suites are permitted by policy and user preference? */
num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
if (!num_suites) {
@@ -8400,6 +8465,10 @@ ssl3_HandleClientHello(sslSocket *ss, SSL3Opaque *b, PRUint32 length)
ssl3_DisableNonDTLSSuites(ss);
}
+ if (!ssl3_HasGCMSupport()) {
+ ssl3_DisableGCMSuites(ss);
+ }
+
#ifdef PARANOID
/* Look for a matching cipher suite. */
j = ssl3_config_match_init(ss);