summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/unit/tlsserver/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/unit/tlsserver/cmd')
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp141
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp170
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp129
-rw-r--r--security/manager/ssl/tests/unit/tlsserver/cmd/moz.build25
4 files changed, 465 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
new file mode 100644
index 000000000..348dcfc5c
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
@@ -0,0 +1,141 @@
+/* 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/. */
+
+// This is a standalone server that uses various bad certificates.
+// The client is expected to connect, initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to), and verify the certificate.
+// If all is good, the client then sends one encrypted byte and receives that
+// same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <stdio.h>
+
+#include "TLSServer.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+struct BadCertHost
+{
+ const char *mHostName;
+ const char *mCertName;
+};
+
+// Hostname, cert nickname pairs.
+const BadCertHost sBadCertHosts[] =
+{
+ { "expired.example.com", "expired-ee" },
+ { "notyetvalid.example.com", "notYetValid" },
+ { "before-epoch.example.com", "beforeEpoch" },
+ { "selfsigned.example.com", "selfsigned" },
+ { "unknownissuer.example.com", "unknownissuer" },
+ { "mismatch.example.com", "mismatch" },
+ { "mismatch-CN.example.com", "mismatchCN" },
+ { "expiredissuer.example.com", "expiredissuer" },
+ { "notyetvalidissuer.example.com", "notYetValidIssuer" },
+ { "before-epoch-issuer.example.com", "beforeEpochIssuer" },
+ { "md5signature.example.com", "md5signature" },
+ { "untrusted.example.com", "default-ee" },
+ { "untrustedissuer.example.com", "untrustedissuer" },
+ { "mismatch-expired.example.com", "mismatch-expired" },
+ { "mismatch-notYetValid.example.com", "mismatch-notYetValid" },
+ { "mismatch-untrusted.example.com", "mismatch-untrusted" },
+ { "untrusted-expired.example.com", "untrusted-expired" },
+ { "md5signature-expired.example.com", "md5signature-expired" },
+ { "mismatch-untrusted-expired.example.com", "mismatch-untrusted-expired" },
+ { "inadequatekeyusage.example.com", "inadequatekeyusage-ee" },
+ { "selfsigned-inadequateEKU.example.com", "selfsigned-inadequateEKU" },
+ { "self-signed-end-entity-with-cA-true.example.com", "self-signed-EE-with-cA-true" },
+ { "ca-used-as-end-entity.example.com", "ca-used-as-end-entity" },
+ { "ca-used-as-end-entity-name-mismatch.example.com", "ca-used-as-end-entity" },
+ // All of include-subdomains.pinning.example.com is pinned to End Entity
+ // Test Cert with nick default-ee. Any other nick will only
+ // pass pinning when security.cert_pinning.enforcement.level != strict and
+ // otherCA is added as a user-specified trust anchor. See StaticHPKPins.h.
+ { "include-subdomains.pinning.example.com", "default-ee" },
+ { "good.include-subdomains.pinning.example.com", "default-ee" },
+ { "bad.include-subdomains.pinning.example.com", "other-issuer-ee" },
+ { "bad.include-subdomains.pinning.example.com.", "other-issuer-ee" },
+ { "bad.include-subdomains.pinning.example.com..", "other-issuer-ee" },
+ { "exclude-subdomains.pinning.example.com", "default-ee" },
+ { "sub.exclude-subdomains.pinning.example.com", "other-issuer-ee" },
+ { "test-mode.pinning.example.com", "other-issuer-ee" },
+ { "unknownissuer.include-subdomains.pinning.example.com", "unknownissuer" },
+ { "unknownissuer.test-mode.pinning.example.com", "unknownissuer" },
+ { "nsCertTypeNotCritical.example.com", "nsCertTypeNotCritical" },
+ { "nsCertTypeCriticalWithExtKeyUsage.example.com", "nsCertTypeCriticalWithExtKeyUsage" },
+ { "nsCertTypeCritical.example.com", "nsCertTypeCritical" },
+ { "end-entity-issued-by-v1-cert.example.com", "eeIssuedByV1Cert" },
+ { "end-entity-issued-by-non-CA.example.com", "eeIssuedByNonCA" },
+ { "inadequate-key-size-ee.example.com", "inadequateKeySizeEE" },
+ { "badSubjectAltNames.example.com", "badSubjectAltNames" },
+ { "ipAddressAsDNSNameInSAN.example.com", "ipAddressAsDNSNameInSAN" },
+ { "noValidNames.example.com", "noValidNames" },
+ { "bug413909.xn--hxajbheg2az3al.xn--jxalpdlp", "idn-certificate" },
+ { "emptyissuername.example.com", "emptyIssuerName" },
+ { "ev-test.example.com", "ev-test" },
+ { nullptr, nullptr }
+};
+
+int32_t
+DoSNISocketConfigBySubjectCN(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize)
+{
+ for (uint32_t i = 0; i < aSrvNameArrSize; i++) {
+ UniquePORTString name(
+ static_cast<char*>(PORT_ZAlloc(aSrvNameArr[i].len + 1)));
+ if (name) {
+ PORT_Memcpy(name.get(), aSrvNameArr[i].data, aSrvNameArr[i].len);
+ if (ConfigSecureServerWithNamedCert(aFd, name.get(), nullptr, nullptr)
+ == SECSuccess) {
+ return 0;
+ }
+ }
+ }
+
+ return SSL_SNI_SEND_ALERT;
+}
+
+int32_t
+DoSNISocketConfig(PRFileDesc* aFd, const SECItem* aSrvNameArr,
+ uint32_t aSrvNameArrSize, void* aArg)
+{
+ const BadCertHost* host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
+ sBadCertHosts);
+ if (!host) {
+ // No static cert <-> hostname mapping found. This happens when we use a
+ // collection of certificates in a given directory and build a cert DB at
+ // runtime, rather than using an NSS cert DB populated at build time.
+ // (This will be the default in the future.)
+ // For all given server names, check if the runtime-built cert DB contains
+ // a certificate with a matching subject CN.
+ return DoSNISocketConfigBySubjectCN(aFd, aSrvNameArr, aSrvNameArrSize);
+ }
+
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+ }
+
+ UniqueCERTCertificate cert;
+ SSLKEAType certKEA;
+ if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, host->mCertName,
+ &cert, &certKEA)) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <NSS DB directory>\n", argv[0]);
+ return 1;
+ }
+
+ return StartServer(argv[1], DoSNISocketConfig, nullptr);
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
new file mode 100644
index 000000000..775f1f06e
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 tw=80 et: */
+/* 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/. */
+
+/* This simple program takes a database directory, and one or more tuples like
+ * <typeOfResponse> <CertNick> <ExtraCertNick> <outPutFilename>
+ * to generate (one or more) ocsp responses.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+#include "mozilla/ArrayUtils.h"
+
+#include "base64.h"
+#include "cert.h"
+#include "nspr.h"
+#include "nss.h"
+#include "plarenas.h"
+#include "prerror.h"
+#include "ssl.h"
+#include "secerr.h"
+
+#include "OCSPCommon.h"
+#include "ScopedNSSTypes.h"
+#include "TLSServer.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+struct OCSPResponseName
+{
+ const char *mTypeString;
+ const OCSPResponseType mORT;
+};
+
+const static OCSPResponseName kOCSPResponseNameList[] = {
+ { "good", ORTGood }, // the certificate is good
+ { "good-delegated", ORTDelegatedIncluded}, // the certificate is good, using
+ // a delegated signer
+ { "revoked", ORTRevoked}, // the certificate has been revoked
+ { "unknown", ORTUnknown}, // the responder doesn't know if the
+ // cert is good
+ { "goodotherca", ORTGoodOtherCA}, // the wrong CA has signed the
+ // response
+ { "expiredresponse", ORTExpired}, // the signature on the response has
+ // expired
+ { "oldvalidperiod", ORTExpiredFreshCA}, // fresh signature, but old validity
+ // period
+ { "empty", ORTEmpty}, // an empty stapled response
+
+ { "malformed", ORTMalformed}, // the response from the responder
+ // was malformed
+ { "serverr", ORTSrverr}, // the response indicates there was a
+ // server error
+ { "trylater", ORTTryLater}, // the responder replied with
+ // "try again later"
+ { "resp-unsigned", ORTNeedsSig}, // the response needs a signature
+ { "unauthorized", ORTUnauthorized}, // the responder does not know about
+ // the cert
+ { "bad-signature", ORTBadSignature}, // the response has a bad signature
+ { "longvalidityalmostold", ORTLongValidityAlmostExpired}, // the response is
+ // still valid, but the generation
+ // is almost a year old
+ { "ancientstillvalid", ORTAncientAlmostExpired}, // The response is still
+ // valid but the generation is almost
+ // two years old
+};
+
+bool
+StringToOCSPResponseType(const char* respText,
+ /*out*/ OCSPResponseType* OCSPType)
+{
+ if (!OCSPType) {
+ return false;
+ }
+ for (uint32_t i = 0; i < mozilla::ArrayLength(kOCSPResponseNameList); i++) {
+ if (strcmp(respText, kOCSPResponseNameList[i].mTypeString) == 0) {
+ *OCSPType = kOCSPResponseNameList[i].mORT;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+WriteResponse(const char* filename, const SECItem* item)
+{
+ if (!filename || !item || !item->data) {
+ PR_fprintf(PR_STDERR, "invalid parameters to WriteResponse");
+ return false;
+ }
+
+ UniquePRFileDesc outFile(PR_Open(filename,
+ PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
+ 0644));
+ if (!outFile) {
+ PrintPRError("cannot open file for writing");
+ return false;
+ }
+ int32_t rv = PR_Write(outFile.get(), item->data, item->len);
+ if (rv < 0 || (uint32_t) rv != item->len) {
+ PrintPRError("File write failure");
+ return false;
+ }
+
+ return true;
+}
+
+int
+main(int argc, char* argv[])
+{
+
+ if (argc < 6 || (argc - 6) % 4 != 0) {
+ PR_fprintf(PR_STDERR, "usage: %s <NSS DB directory> <responsetype> "
+ "<cert_nick> <extranick> <outfilename> [<resptype> "
+ "<cert_nick> <extranick> <outfilename>]* \n",
+ argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ SECStatus rv = InitializeNSS(argv[1]);
+ if (rv != SECSuccess) {
+ PR_fprintf(PR_STDERR, "Failed to initialize NSS\n");
+ exit(EXIT_FAILURE);
+ }
+ UniquePLArenaPool arena(PORT_NewArena(256 * argc));
+ if (!arena) {
+ PrintPRError("PORT_NewArena failed");
+ exit(EXIT_FAILURE);
+ }
+
+ for (int i = 2; i + 3 < argc; i += 4) {
+ const char* ocspTypeText = argv[i];
+ const char* certNick = argv[i + 1];
+ const char* extraCertname = argv[i + 2];
+ const char* filename = argv[i + 3];
+
+ OCSPResponseType ORT;
+ if (!StringToOCSPResponseType(ocspTypeText, &ORT)) {
+ PR_fprintf(PR_STDERR, "Cannot generate OCSP response of type %s\n",
+ ocspTypeText);
+ exit(EXIT_FAILURE);
+ }
+
+ UniqueCERTCertificate cert(PK11_FindCertFromNickname(certNick, nullptr));
+ if (!cert) {
+ PrintPRError("PK11_FindCertFromNickname failed");
+ PR_fprintf(PR_STDERR, "Failed to find certificate with nick '%s'\n",
+ certNick);
+ exit(EXIT_FAILURE);
+ }
+
+ SECItemArray* response = GetOCSPResponseForType(ORT, cert, arena,
+ extraCertname);
+ if (!response) {
+ PR_fprintf(PR_STDERR, "Failed to generate OCSP response of type %s "
+ "for %s\n", ocspTypeText, certNick);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!WriteResponse(filename, &response->items[0])) {
+ PR_fprintf(PR_STDERR, "Failed to write file %s\n", filename);
+ exit(EXIT_FAILURE);
+ }
+ }
+ return 0;
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
new file mode 100644
index 000000000..6bf33a143
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/OCSPStaplingServer.cpp
@@ -0,0 +1,129 @@
+/* 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/. */
+
+// This is a standalone server that delivers various stapled OCSP responses.
+// The client is expected to connect, initiate an SSL handshake (with SNI
+// to indicate which "server" to connect to), and verify the OCSP response.
+// If all is good, the client then sends one encrypted byte and receives that
+// same byte back.
+// This server also has the ability to "call back" another process waiting on
+// it. That is, when the server is all set up and ready to receive connections,
+// it will connect to a specified port and issue a simple HTTP request.
+
+#include <stdio.h>
+
+#include "OCSPCommon.h"
+#include "TLSServer.h"
+
+using namespace mozilla;
+using namespace mozilla::test;
+
+const OCSPHost sOCSPHosts[] =
+{
+ { "ocsp-stapling-good.example.com", ORTGood, nullptr, nullptr },
+ { "ocsp-stapling-revoked.example.com", ORTRevoked, nullptr, nullptr },
+ { "ocsp-stapling-revoked-old.example.com", ORTRevokedOld, nullptr, nullptr },
+ { "ocsp-stapling-unknown.example.com", ORTUnknown, nullptr, nullptr },
+ { "ocsp-stapling-unknown-old.example.com", ORTUnknownOld, nullptr, nullptr },
+ { "ocsp-stapling-good-other.example.com", ORTGoodOtherCert, "ocspOtherEndEntity", nullptr },
+ { "ocsp-stapling-good-other-ca.example.com", ORTGoodOtherCA, "other-test-ca", nullptr },
+ { "ocsp-stapling-expired.example.com", ORTExpired, nullptr, nullptr },
+ { "ocsp-stapling-expired-fresh-ca.example.com", ORTExpiredFreshCA, nullptr, nullptr },
+ { "ocsp-stapling-none.example.com", ORTNone, nullptr, nullptr },
+ { "ocsp-stapling-empty.example.com", ORTEmpty, nullptr, nullptr },
+ { "ocsp-stapling-malformed.example.com", ORTMalformed, nullptr, nullptr },
+ { "ocsp-stapling-srverr.example.com", ORTSrverr, nullptr, nullptr },
+ { "ocsp-stapling-trylater.example.com", ORTTryLater, nullptr, nullptr },
+ { "ocsp-stapling-needssig.example.com", ORTNeedsSig, nullptr, nullptr },
+ { "ocsp-stapling-unauthorized.example.com", ORTUnauthorized, nullptr, nullptr },
+ { "ocsp-stapling-with-intermediate.example.com", ORTGood, nullptr, "ocspEEWithIntermediate" },
+ { "ocsp-stapling-bad-signature.example.com", ORTBadSignature, nullptr, nullptr },
+ { "ocsp-stapling-skip-responseBytes.example.com", ORTSkipResponseBytes, nullptr, nullptr },
+ { "ocsp-stapling-critical-extension.example.com", ORTCriticalExtension, nullptr, nullptr },
+ { "ocsp-stapling-noncritical-extension.example.com", ORTNoncriticalExtension, nullptr, nullptr },
+ { "ocsp-stapling-empty-extensions.example.com", ORTEmptyExtensions, nullptr, nullptr },
+ { "ocsp-stapling-delegated-included.example.com", ORTDelegatedIncluded, "delegatedSigner", nullptr },
+ { "ocsp-stapling-delegated-included-last.example.com", ORTDelegatedIncludedLast, "delegatedSigner", nullptr },
+ { "ocsp-stapling-delegated-missing.example.com", ORTDelegatedMissing, "delegatedSigner", nullptr },
+ { "ocsp-stapling-delegated-missing-multiple.example.com", ORTDelegatedMissingMultiple, "delegatedSigner", nullptr },
+ { "ocsp-stapling-delegated-no-extKeyUsage.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerNoExtKeyUsage", nullptr },
+ { "ocsp-stapling-delegated-from-intermediate.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerFromIntermediate", nullptr },
+ { "ocsp-stapling-delegated-keyUsage-crlSigning.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerKeyUsageCrlSigning", nullptr },
+ { "ocsp-stapling-delegated-wrong-extKeyUsage.example.com", ORTDelegatedIncluded, "invalidDelegatedSignerWrongExtKeyUsage", nullptr },
+ { "ocsp-stapling-ancient-valid.example.com", ORTAncientAlmostExpired, nullptr, nullptr },
+ { "keysize-ocsp-delegated.example.com", ORTDelegatedIncluded, "rsa-1016-keysizeDelegatedSigner", nullptr },
+ { "revoked-ca-cert-used-as-end-entity.example.com", ORTRevoked, "ca-used-as-end-entity", nullptr },
+ { "ocsp-stapling-must-staple.example.com", ORTGood, nullptr, "must-staple-ee" },
+ { "ocsp-stapling-must-staple-revoked.example.com", ORTRevoked, nullptr, "must-staple-ee" },
+ { "ocsp-stapling-must-staple-missing.example.com", ORTNone, nullptr, "must-staple-ee" },
+ { "ocsp-stapling-must-staple-empty.example.com", ORTEmpty, nullptr, "must-staple-ee" },
+ { "ocsp-stapling-must-staple-ee-with-must-staple-int.example.com", ORTGood, nullptr, "must-staple-ee-with-must-staple-int" },
+ { "ocsp-stapling-plain-ee-with-must-staple-int.example.com", ORTGood, nullptr, "must-staple-missing-ee" },
+ { "multi-tls-feature-good.example.com", ORTNone, nullptr, "multi-tls-feature-good-ee" },
+ { "multi-tls-feature-bad.example.com", ORTNone, nullptr, "multi-tls-feature-bad-ee" },
+ { nullptr, ORTNull, nullptr, nullptr }
+};
+
+int32_t
+DoSNISocketConfig(PRFileDesc *aFd, const SECItem *aSrvNameArr,
+ uint32_t aSrvNameArrSize, void *aArg)
+{
+ const OCSPHost *host = GetHostForSNI(aSrvNameArr, aSrvNameArrSize,
+ sOCSPHosts);
+ if (!host) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ if (gDebugLevel >= DEBUG_VERBOSE) {
+ fprintf(stderr, "found pre-defined host '%s'\n", host->mHostName);
+ }
+
+ const char *certNickname = host->mServerCertName ? host->mServerCertName
+ : DEFAULT_CERT_NICKNAME;
+
+ UniqueCERTCertificate cert;
+ SSLKEAType certKEA;
+ if (SECSuccess != ConfigSecureServerWithNamedCert(aFd, certNickname,
+ &cert, &certKEA)) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ // If the OCSP response type is "none", don't staple a response.
+ if (host->mORT == ORTNone) {
+ return 0;
+ }
+
+ UniquePLArenaPool arena(PORT_NewArena(1024));
+ if (!arena) {
+ PrintPRError("PORT_NewArena failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ // response is contained by the arena - freeing the arena will free it
+ SECItemArray *response = GetOCSPResponseForType(host->mORT, cert, arena,
+ host->mAdditionalCertName);
+ if (!response) {
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ // SSL_SetStapledOCSPResponses makes a deep copy of response
+ SECStatus st = SSL_SetStapledOCSPResponses(aFd, response, certKEA);
+ if (st != SECSuccess) {
+ PrintPRError("SSL_SetStapledOCSPResponses failed");
+ return SSL_SNI_SEND_ALERT;
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <NSS DB directory>\n", argv[0]);
+ return 1;
+ }
+
+ return StartServer(argv[1], DoSNISocketConfig, nullptr);
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build b/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
new file mode 100644
index 000000000..6fdd0c35b
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
@@ -0,0 +1,25 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+GeckoSimplePrograms([
+ 'BadCertServer',
+ 'GenerateOCSPResponse',
+ 'OCSPStaplingServer',
+], linkage=None)
+
+LOCAL_INCLUDES += [
+ '../lib',
+]
+
+USE_LIBS += [
+ 'mozillapkix',
+ 'nspr',
+ 'nss',
+ 'pkixtestutil',
+ 'tlsserver',
+]
+
+CXXFLAGS += CONFIG['TK_CFLAGS']