diff options
Diffstat (limited to 'security/nss/lib/ssl/sslgrp.c')
-rw-r--r-- | security/nss/lib/ssl/sslgrp.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/sslgrp.c b/security/nss/lib/ssl/sslgrp.c new file mode 100644 index 000000000..eb53ad381 --- /dev/null +++ b/security/nss/lib/ssl/sslgrp.c @@ -0,0 +1,164 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file contains prototypes for the public SSL functions. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nss.h" +#include "pk11func.h" +#include "ssl.h" +#include "sslimpl.h" + +struct { + sslEphemeralKeyPair *keyPair; + PRCallOnceType once; +} gECDHEKeyPairs[SSL_NAMED_GROUP_COUNT]; + +typedef struct sslSocketAndGroupArgStr { + const sslNamedGroupDef *group; + const sslSocket *ss; +} sslSocketAndGroupArg; + +/* Function to clear out the ECDHE keys. */ +static SECStatus +ssl_CleanupECDHEKeys(void *appData, void *nssData) +{ + unsigned int i; + + for (i = 0; i < SSL_NAMED_GROUP_COUNT; i++) { + if (gECDHEKeyPairs[i].keyPair) { + ssl_FreeEphemeralKeyPair(gECDHEKeyPairs[i].keyPair); + } + } + memset(gECDHEKeyPairs, 0, sizeof(gECDHEKeyPairs)); + return SECSuccess; +} + +/* Only run the cleanup once. */ +static PRCallOnceType cleanupECDHEKeysOnce; +static PRStatus +ssl_SetupCleanupECDHEKeysOnce(void) +{ + SECStatus rv = NSS_RegisterShutdown(ssl_CleanupECDHEKeys, NULL); + return (rv != SECSuccess) ? PR_FAILURE : PR_SUCCESS; +} + +/* This creates a key pair for each of the supported EC groups. If that works, + * we assume that the token supports that group. Since this is relatively + * expensive, this is only done for the first socket that is used. That means + * that if tokens are added or removed, then this will not pick up any changes. + */ +static PRStatus +ssl_CreateStaticECDHEKeyPair(void *arg) +{ + const sslSocketAndGroupArg *typed_arg = (sslSocketAndGroupArg *)arg; + const sslNamedGroupDef *group = typed_arg->group; + const sslSocket *ss = typed_arg->ss; + unsigned int i = group - ssl_named_groups; + SECStatus rv; + + PORT_Assert(group->keaType == ssl_kea_ecdh); + PORT_Assert(i < SSL_NAMED_GROUP_COUNT); + rv = ssl_CreateECDHEphemeralKeyPair(ss, group, + &gECDHEKeyPairs[i].keyPair); + if (rv != SECSuccess) { + gECDHEKeyPairs[i].keyPair = NULL; + SSL_TRC(5, ("%d: SSL[-]: disabling group %d", + SSL_GETPID(), group->name)); + } + + return PR_SUCCESS; +} + +void +ssl_FilterSupportedGroups(sslSocket *ss) +{ + unsigned int i; + PRStatus prv; + sslSocketAndGroupArg arg = { NULL, ss }; + + prv = PR_CallOnce(&cleanupECDHEKeysOnce, ssl_SetupCleanupECDHEKeysOnce); + PORT_Assert(prv == PR_SUCCESS); + if (prv != PR_SUCCESS) { + return; + } + + for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { + PRUint32 policy; + SECStatus srv; + unsigned int index; + const sslNamedGroupDef *group = ss->namedGroupPreferences[i]; + if (!group) { + continue; + } + + srv = NSS_GetAlgorithmPolicy(group->oidTag, &policy); + if (srv == SECSuccess && !(policy & NSS_USE_ALG_IN_SSL_KX)) { + ss->namedGroupPreferences[i] = NULL; + continue; + } + + if (group->assumeSupported) { + continue; + } + + /* For EC groups, we have to test that a key pair can be created. This + * is gross, and expensive, so only do it once. */ + index = group - ssl_named_groups; + PORT_Assert(index < SSL_NAMED_GROUP_COUNT); + + arg.group = group; + prv = PR_CallOnceWithArg(&gECDHEKeyPairs[index].once, + ssl_CreateStaticECDHEKeyPair, + (void *)&arg); + PORT_Assert(prv == PR_SUCCESS); + if (prv != PR_SUCCESS) { + continue; + } + + if (!gECDHEKeyPairs[index].keyPair) { + ss->namedGroupPreferences[i] = NULL; + } + } +} + +/* + * Creates the static "ephemeral" public and private ECDH keys used by server in + * ECDHE_RSA and ECDHE_ECDSA handshakes when we reuse the same key. + */ +SECStatus +ssl_CreateStaticECDHEKey(sslSocket *ss, const sslNamedGroupDef *ecGroup) +{ + sslEphemeralKeyPair *keyPair; + /* We index gECDHEKeyPairs by the named group. Pointer arithmetic! */ + unsigned int index = ecGroup - ssl_named_groups; + PRStatus prv; + sslSocketAndGroupArg arg = { ecGroup, ss }; + + prv = PR_CallOnceWithArg(&gECDHEKeyPairs[index].once, + ssl_CreateStaticECDHEKeyPair, + (void *)&arg); + PORT_Assert(prv == PR_SUCCESS); + if (prv != PR_SUCCESS) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + keyPair = gECDHEKeyPairs[index].keyPair; + if (!keyPair) { + /* Attempting to use a key pair for an unsupported group. */ + PORT_Assert(keyPair); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + keyPair = ssl_CopyEphemeralKeyPair(keyPair); + if (!keyPair) + return SECFailure; + + PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)); + PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs); + return SECSuccess; +} |