Bug 1385146 - Experimental API, r=ekr
authorMartin Thomson <martin.thomson@gmail.com>
Thu, 29 Jun 2017 17:20:10 -0700
changeset 13478 27ad4d2065ac2f0bd35731e703389c2f98403e56
parent 13472 dd0b022aa84322d172ba7203e0e4c7244473e831
child 13479 df096339f92531ae3e79b7931702334371a0ecc9
push id2284
push usermartin.thomson@gmail.com
push dateFri, 28 Jul 2017 01:32:51 +0000
reviewersekr
bugs1385146
Bug 1385146 - Experimental API, r=ekr
gtests/ssl_gtest/manifest.mn
gtests/ssl_gtest/ssl_0rtt_unittest.cc
gtests/ssl_gtest/ssl_gtest.gyp
gtests/ssl_gtest/ssl_misc_unittest.cc
gtests/ssl_gtest/tls_connect.cc
lib/ssl/SSLerrs.h
lib/ssl/exports.gyp
lib/ssl/manifest.mn
lib/ssl/ssl.def
lib/ssl/ssl.h
lib/ssl/sslerr.h
lib/ssl/sslexp.h
lib/ssl/sslsock.c
--- a/gtests/ssl_gtest/manifest.mn
+++ b/gtests/ssl_gtest/manifest.mn
@@ -25,16 +25,17 @@ CPPSRCS = \
       ssl_exporter_unittest.cc \
       ssl_extension_unittest.cc \
       ssl_fragment_unittest.cc \
       ssl_fuzz_unittest.cc \
       ssl_gather_unittest.cc \
       ssl_gtest.cc \
       ssl_hrr_unittest.cc \
       ssl_loopback_unittest.cc \
+      ssl_misc_unittest.cc \
       ssl_record_unittest.cc \
       ssl_resumption_unittest.cc \
       ssl_skip_unittest.cc \
       ssl_staticrsa_unittest.cc \
       ssl_v2_client_hello_unittest.cc \
       ssl_version_unittest.cc \
       ssl_versionpolicy_unittest.cc \
       selfencrypt_unittest.cc \
--- a/gtests/ssl_gtest/ssl_0rtt_unittest.cc
+++ b/gtests/ssl_gtest/ssl_0rtt_unittest.cc
@@ -2,16 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "secerr.h"
 #include "ssl.h"
 #include "sslerr.h"
+#include "sslexp.h"
 #include "sslproto.h"
 
 extern "C" {
 // This is not something that should make you happy.
 #include "libssl_internals.h"
 }
 
 #include "gtest_utils.h"
--- a/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/gtests/ssl_gtest/ssl_gtest.gyp
@@ -26,16 +26,17 @@
         'ssl_exporter_unittest.cc',
         'ssl_extension_unittest.cc',
         'ssl_fuzz_unittest.cc',
         'ssl_fragment_unittest.cc',
         'ssl_gather_unittest.cc',
         'ssl_gtest.cc',
         'ssl_hrr_unittest.cc',
         'ssl_loopback_unittest.cc',
+        'ssl_misc_unittest.cc',
         'ssl_record_unittest.cc',
         'ssl_resumption_unittest.cc',
         'ssl_skip_unittest.cc',
         'ssl_staticrsa_unittest.cc',
         'ssl_v2_client_hello_unittest.cc',
         'ssl_version_unittest.cc',
         'ssl_versionpolicy_unittest.cc',
         'test_io.cc',
new file mode 100644
--- /dev/null
+++ b/gtests/ssl_gtest/ssl_misc_unittest.cc
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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 "sslexp.h"
+
+#include "gtest_utils.h"
+
+namespace nss_test {
+
+class MiscTest : public ::testing::Test {};
+
+TEST_F(MiscTest, NonExistentExperimentalAPI) {
+  EXPECT_EQ(nullptr, SSL_GetExperimentalAPI("blah"));
+  EXPECT_EQ(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API, PORT_GetError());
+}
+
+}  // namespace nss_test
--- a/gtests/ssl_gtest/tls_connect.cc
+++ b/gtests/ssl_gtest/tls_connect.cc
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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 "tls_connect.h"
+#include "sslexp.h"
 extern "C" {
 #include "libssl_internals.h"
 }
 
 #include <iostream>
 
 #include "databuffer.h"
 #include "gtest_utils.h"
--- a/lib/ssl/SSLerrs.h
+++ b/lib/ssl/SSLerrs.h
@@ -506,8 +506,17 @@ ER3(SSL_ERROR_MALFORMED_PSK_KEY_EXCHANGE
 ER3(SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES, (SSL_ERROR_BASE + 159),
     "SSL expected a PSK key exchange modes extension.")
 
 ER3(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA, (SSL_ERROR_BASE + 160),
     "SSL got a pre-TLS 1.3 version even though we sent early data.")
 
 ER3(SSL_ERROR_TOO_MUCH_EARLY_DATA, (SSL_ERROR_BASE + 161),
     "SSL received more early data than permitted.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_END_OF_EARLY_DATA, (SSL_ERROR_BASE + 162),
+    "SSL received an unexpected End of Early Data message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_END_OF_EARLY_DATA, (SSL_ERROR_BASE + 163),
+    "SSL received a malformed End of Early Data message.")
+
+ER3(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API, (SSL_ERROR_BASE + 164),
+    "An experimental API was called, but not supported.")
--- a/lib/ssl/exports.gyp
+++ b/lib/ssl/exports.gyp
@@ -10,16 +10,17 @@
       'target_name': 'lib_ssl_exports',
       'type': 'none',
       'copies': [
         {
           'files': [
             'preenc.h',
             'ssl.h',
             'sslerr.h',
+            'sslexp.h',
             'sslproto.h',
             'sslt.h'
           ],
           'destination': '<(nss_public_dist_dir)/<(module)'
         }
       ]
     }
   ],
--- a/lib/ssl/manifest.mn
+++ b/lib/ssl/manifest.mn
@@ -5,16 +5,17 @@
 CORE_DEPTH = ../..
 
 # DEFINES = -DTRACE
 
 EXPORTS = \
         ssl.h \
         sslt.h \
         sslerr.h \
+        sslexp.h \
         sslproto.h \
         preenc.h \
         $(NULL)
 
 MODULE = nss
 MAPFILE = $(OBJDIR)/ssl.def
 
 CSRCS = \
--- a/lib/ssl/ssl.def
+++ b/lib/ssl/ssl.def
@@ -229,8 +229,14 @@ SSL_SetSessionTicketKeyPair;
 ;+};
 ;+NSS_3.30.0.1 { # Additional symbols for NSS 3.30 release
 ;+    global:
 SSL_AlertReceivedCallback;
 SSL_AlertSentCallback;
 ;+    local:
 ;+*;
 ;+};
+;+NSS_3.33 {    # NSS 3.33 release
+;+    global:
+SSL_GetExperimentalAPI;
+;+    local:
+;+*;
+;+};
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -1369,11 +1369,18 @@ extern const char *NSSSSL_GetVersion(voi
  * return SECSuccess (normally), but that does not mean that the application
  * should continue using the connection. If the application passes a non-zero
  * value for second argument (error), or if SSL_AuthCertificateComplete returns
  * anything other than SECSuccess, then the application should close the
  * connection.
  */
 SSL_IMPORT SECStatus SSL_AuthCertificateComplete(PRFileDesc *fd,
                                                  PRErrorCode error);
+
+/*
+ * This is used to access experimental APIs.  Don't call this directly.  This is
+ * used to enable the experimental APIs that are defined in "sslexp.h".
+ */
+SSL_IMPORT void *SSL_GetExperimentalAPI(const char *name);
+
 SEC_END_PROTOS
 
 #endif /* __ssl_h_ */
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -241,15 +241,20 @@ typedef enum {
     SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST = (SSL_ERROR_BASE + 154),
     SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST = (SSL_ERROR_BASE + 155),
     SSL_ERROR_BAD_2ND_CLIENT_HELLO = (SSL_ERROR_BASE + 156),
     SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION = (SSL_ERROR_BASE + 157),
     SSL_ERROR_MALFORMED_PSK_KEY_EXCHANGE_MODES = (SSL_ERROR_BASE + 158),
     SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES = (SSL_ERROR_BASE + 159),
     SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA = (SSL_ERROR_BASE + 160),
     SSL_ERROR_TOO_MUCH_EARLY_DATA = (SSL_ERROR_BASE + 161),
+    SSL_ERROR_RX_UNEXPECTED_END_OF_EARLY_DATA = (SSL_ERROR_BASE + 162),
+    SSL_ERROR_RX_MALFORMED_END_OF_EARLY_DATA = (SSL_ERROR_BASE + 163),
+
+    SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API = (SSL_ERROR_BASE + 164),
+
     SSL_ERROR_END_OF_LIST   /* let the c compiler determine the value of this. */
 } SSLErrorCodes;
 #endif /* NO_SECURITY_ERROR_ENUM */
 
 /* clang-format on */
 
 #endif /* __SSL_ERR_H_ */
new file mode 100644
--- /dev/null
+++ b/lib/ssl/sslexp.h
@@ -0,0 +1,27 @@
+/*
+ * This file contains prototypes for experimental 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/. */
+
+#ifndef __sslexp_h_
+#define __sslexp_h_
+
+#include "ssl.h"
+#include "sslerr.h"
+
+SEC_BEGIN_PROTOS
+
+/* The functions in this header file are not guaranteed to remain available in
+ * future NSS versions. Code that uses these functions needs to safeguard
+ * against the function not being available. */
+
+#define SSL_EXPERIMENTAL_API(name, arglist, args)                   \
+    (SSL_GetExperimentalAPI(name)                                   \
+         ? ((SECStatus(*) arglist)SSL_GetExperimentalAPI(name))args \
+         : SECFailure)
+
+SEC_END_PROTOS
+
+#endif /* __sslexp_h_ */
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -6,16 +6,17 @@
  *
  * 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 "seccomon.h"
 #include "cert.h"
 #include "keyhi.h"
 #include "ssl.h"
+#include "sslexp.h"
 #include "sslimpl.h"
 #include "sslproto.h"
 #include "nspr.h"
 #include "private/pprio.h"
 #include "nss.h"
 #include "pk11pqg.h"
 
 static const sslSocketOps ssl_default_ops = { /* No SSL. */
@@ -3835,8 +3836,51 @@ SSL_CanBypass(CERTCertificate *cert, SEC
 {
     if (!pcanbypass) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return SECFailure;
     }
     *pcanbypass = PR_FALSE;
     return SECSuccess;
 }
+
+/* Functions that are truly experimental use EXP, functions that are no longer
+ * experimental use PUB.
+ *
+ * When initially defining a new API, add that API here using the EXP() macro
+ * and name the function with a SSLExp_ prefix.  Define the experimental API as
+ * a macro in sslexp.h using the SSL_EXPERIMENTAL_API() macro defined there.
+ *
+ * Once an API is stable and proven, move the macro definition in sslexp.h to a
+ * proper function declaration in ssl.h.  Keeping the function in this list
+ * ensures that code built against the release that contained the experimental
+ * API will continue to work; use PUB() to reference the public function.
+ */
+#define EXP(n)                \
+    {                         \
+        "SSL_" #n, SSLExp_##n \
+    }
+#define PUB(n)             \
+    {                      \
+        "SSL_" #n, SSL_##n \
+    }
+struct {
+    const char *const name;
+    void *function;
+} ssl_experimental_functions[] = {
+#ifndef SSL_DISABLE_EXPERIMENTAL_API
+#endif
+};
+#undef EXP
+#undef PUB
+
+void *
+SSL_GetExperimentalAPI(const char *name)
+{
+    unsigned int i;
+    for (i = 0; i < PR_ARRAY_SIZE(ssl_experimental_functions); ++i) {
+        if (strcmp(name, ssl_experimental_functions[i].name) == 0) {
+            return ssl_experimental_functions[i].function;
+        }
+    }
+    PORT_SetError(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API);
+    return NULL;
+}