Bug 938805 - Create standalone oscpResponseGenerator for testing. r=keeler a=test-only
authorCamilo Viecco <cviecco@mozilla.com>
Tue, 29 Oct 2013 14:02:35 -0700
changeset 167829 f3e711fceae8a61dd8921c236ac481913acb332a
parent 167828 e236058103180921874613ddf68f0182d0b5ac94
child 167830 1786928ad7b2e6df873721cdc6d8812cfb44ae5a
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, test-only
bugs938805
milestone27.0
Bug 938805 - Create standalone oscpResponseGenerator for testing. r=keeler a=test-only
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
testing/mochitest/Makefile.in
toolkit/mozapps/installer/packager.mk
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -223,16 +223,36 @@ function add_connection_test(aHost, aExp
                               .QueryInterface(Ci.nsITransportSecurityInfo));
         dump("hello #2\n");
       }
       run_next_test();
     });
   });
 }
 
+function _getBinaryUtil(binaryUtilName) {
+  let directoryService = Cc["@mozilla.org/file/directory_service;1"]
+                           .getService(Ci.nsIProperties);
+
+  let utilBin = directoryService.get("CurProcD", Ci.nsILocalFile);
+  utilBin.append(binaryUtilName + (gIsWindows ? ".exe" : ""));
+  // If we're testing locally, the above works. If not, the server executable
+  // is in another location.
+  if (!utilBin.exists()) {
+    utilBin = directoryService.get("CurWorkD", Ci.nsILocalFile);
+    while (utilBin.path.indexOf("xpcshell") != -1) {
+      utilBin = utilBin.parent;
+    }
+    utilBin.append("bin");
+    utilBin.append(binaryUtilName + (gIsWindows ? ".exe" : ""));
+  }
+  do_check_true(utilBin.exists());
+  return utilBin;
+}
+
 // Do not call this directly; use add_tls_server_setup
 function _setupTLSServerTest(serverBinName)
 {
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
   // The trusted CA that is typically used for "good" certificates.
   addCertFromFile(certdb, "tlsserver/test-ca.der", "CTu,u,u");
 
@@ -256,32 +276,53 @@ function _setupTLSServerTest(serverBinNa
         let responseBody = "OK!";
         aResponse.bodyOutputStream.write(responseBody, responseBody.length);
         do_execute_soon(function() {
           httpServer.stop(run_next_test);
         });
       });
   httpServer.start(CALLBACK_PORT);
 
-  let serverBin = directoryService.get("CurProcD", Ci.nsILocalFile);
-  serverBin.append(serverBinName + (gIsWindows ? ".exe" : ""));
-  // If we're testing locally, the above works. If not, the server executable
-  // is in another location.
-  if (!serverBin.exists()) {
-    serverBin = directoryService.get("CurWorkD", Ci.nsILocalFile);
-    while (serverBin.path.indexOf("xpcshell") != -1) {
-      serverBin = serverBin.parent;
-    }
-    serverBin.append("bin");
-    serverBin.append(serverBinName + (gIsWindows ? ".exe" : ""));
-  }
-  do_check_true(serverBin.exists());
+  let serverBin = _getBinaryUtil(serverBinName);
   let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
   process.init(serverBin);
   let certDir = directoryService.get("CurWorkD", Ci.nsILocalFile);
   certDir.append("tlsserver");
   do_check_true(certDir.exists());
   process.run(false, [certDir.path], 1);
 
   do_register_cleanup(function() {
     process.kill();
   });
 }
+
+// Returns an Array of OCSP responses for a given ocspRespArray and a location
+// for a nssDB where the certs and public keys are prepopulated.
+// ocspRespArray is an array of arrays like:
+// [ [typeOfResponse, certnick, extracertnick]...]
+function generateOCSPResponses(ocspRespArray, nssDBlocation)
+{
+  let utilBinName =  "GenerateOCSPResponse";
+  let ocspGenBin = _getBinaryUtil(utilBinName);
+  let retArray = new Array();
+
+  for (let i = 0; i < ocspRespArray.length; i++) {
+    let argArray = new Array();
+    let ocspFilepre = do_get_file(i.toString() + ".ocsp", true);
+    let filename = ocspFilepre.path;
+    argArray.push(nssDBlocation);
+    argArray.push(ocspRespArray[i][0]); // ocsRespType;
+    argArray.push(ocspRespArray[i][1]); // nick;
+    argArray.push(ocspRespArray[i][2]); // extranickname
+    argArray.push(filename);
+    do_print("arg_array ="+argArray);
+
+    let process = Cc["@mozilla.org/process/util;1"]
+                    .createInstance(Ci.nsIProcess);
+    process.init(ocspGenBin);
+    process.run(true, argArray, 5);
+    do_check_eq(0, process.exitValue);
+    let ocspFile = do_get_file(i.toString() + ".ocsp", false);
+    retArray.push(readFile(ocspFile));
+    ocspFile.remove(false);
+  }
+  return retArray;
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/GenerateOCSPResponse.cpp
@@ -0,0 +1,164 @@
+/* -*- 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 "mozilla/Util.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
+  { "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
+};
+
+
+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;
+  }
+
+  ScopedPRFileDesc 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, 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);
+  }
+  const char* dbdir = argv[1];
+
+  SECStatus rv;
+  rv = NSS_Init(dbdir);
+  if (rv != SECSuccess) {
+    PrintPRError("Failed to initialize NSS");
+    exit(EXIT_FAILURE);
+  }
+  PLArenaPool* 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);
+    }
+
+    ScopedCERTCertificate cert;
+    cert = PK11_FindCertFromNickname(certNick, nullptr);
+    if (!cert) {
+      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;
+}
+
+
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 MODULE = 'tlsserver'
 
 FAIL_ON_WARNINGS = True
 
 sources = [
+    'GenerateOCSPResponse',
     'OCSPStaplingServer',
 ]
 
 SOURCES += [
     '%s.cpp' % s for s in sources
 ]
 
 bin_suffix = CONFIG['BIN_SUFFIX']
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -116,16 +116,17 @@ libs:: $(_SERV_FILES)
 # Binaries and scripts that don't get packaged with the build,
 # but that we need for the test harness
 TEST_HARNESS_BINS := \
   xpcshell$(BIN_SUFFIX) \
   ssltunnel$(BIN_SUFFIX) \
   certutil$(BIN_SUFFIX) \
   pk12util$(BIN_SUFFIX) \
   OCSPStaplingServer$(BIN_SUFFIX) \
+  GenerateOCSPResponse$(BIN_SUFFIX) \
   fix_stack_using_bpsyms.py \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 TEST_HARNESS_BINS += \
   crashinject$(BIN_SUFFIX) \
   crashinjectdll$(DLL_SUFFIX) \
   vmwarerecordinghelper$(DLL_SUFFIX) \
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -558,16 +558,17 @@ NO_PKG_FILES += \
 	nsinstall* \
 	res/samples \
 	res/throbber \
 	shlibsign* \
 	ssltunnel* \
 	certutil* \
 	pk12util* \
 	OCSPStaplingServer* \
+	GenerateOCSPResponse* \
 	winEmbed.exe \
 	chrome/chrome.rdf \
 	chrome/app-chrome.manifest \
 	chrome/overlayinfo \
 	components/compreg.dat \
 	components/xpti.dat \
 	content_unit_tests \
 	necko_unit_tests \