bug 1369911 - gather telemetry on the prevalence of 3rd party PKCS#11 modules r=bsmedberg,Cykesiopka data-review=bsmedberg
authorDavid Keeler <dkeeler@mozilla.com>
Fri, 02 Jun 2017 16:44:06 -0700
changeset 363091 76a620a287bfa2bf79971613d2a28093c687c65b
parent 363090 62e44a82730804257046e8fe9aafd8d53652d6ea
child 363092 83ff39c14cedc6a5a56fc0b0c2aa774880ca7c00
push id31994
push usercbook@mozilla.com
push dateFri, 09 Jun 2017 10:56:24 +0000
treeherdermozilla-central@7c9d96bbc400 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, Cykesiopka
bugs1369911
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1369911 - gather telemetry on the prevalence of 3rd party PKCS#11 modules r=bsmedberg,Cykesiopka data-review=bsmedberg MozReview-Commit-ID: Dw99Jm64QNU
security/manager/ssl/PKCS11.cpp
security/manager/ssl/PKCS11.h
security/manager/ssl/nsNSSComponent.cpp
security/manager/ssl/tests/unit/test_pkcs11_module.js
toolkit/components/telemetry/Scalars.yaml
--- a/security/manager/ssl/PKCS11.cpp
+++ b/security/manager/ssl/PKCS11.cpp
@@ -2,16 +2,18 @@
 /* vim: set ts=8 sts=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 "PKCS11.h"
 
 #include "ScopedNSSTypes.h"
+#include "mozilla/Telemetry.h"
+#include "nsCRTGlue.h"
 #include "nsNSSComponent.h"
 #include "nsNativeCharsetUtils.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla { namespace psm {
 
 NS_INTERFACE_MAP_BEGIN(PKCS11)
   NS_INTERFACE_MAP_ENTRY(nsIPKCS11)
@@ -67,16 +69,44 @@ PKCS11::DeleteModule(const nsAString& aM
   SECStatus srv = SECMOD_DeleteModule(moduleName.get(), &modType);
   if (srv != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
+// Given a PKCS#11 module, determines an appropriate name to identify it for the
+// purposes of gathering telemetry. For 3rd party PKCS#11 modules, this should
+// be the name of the dynamic library that implements the module. For built-in
+// NSS modules, it will be the common name of the module.
+// Because the result will be used as a telemetry scalar key (which must be less
+// than 70 characters), this function will also truncate the result if it
+// exceeds this limit. (Note that unfortunately telemetry doesn't expose a way
+// to programmatically query the scalar key length limit, so we have to
+// hard-code the value here.)
+void
+GetModuleNameForTelemetry(/*in*/ const SECMODModule* module,
+                          /*out*/nsString& result)
+{
+  result.Truncate();
+  if (module->dllName) {
+    result.AssignWithConversion(module->dllName);
+    int32_t separatorIndex = result.RFind(FILE_PATH_SEPARATOR);
+    if (separatorIndex != kNotFound) {
+      result = Substring(result, separatorIndex + 1);
+    }
+  } else {
+    result.AssignWithConversion(module->commonName);
+  }
+  if (result.Length() >= 70) {
+    result.Truncate(69);
+  }
+}
+
 // Add a new PKCS11 module to the user's profile.
 NS_IMETHODIMP
 PKCS11::AddModule(const nsAString& aModuleName,
                   const nsAString& aLibraryFullPath,
                   int32_t aCryptoMechanismFlags,
                   int32_t aCipherFlags)
 {
   nsNSSShutDownPreventionLock locker;
@@ -95,22 +125,34 @@ PKCS11::AddModule(const nsAString& aModu
   uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags);
   uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
   SECStatus srv = SECMOD_AddNewModule(moduleName.get(), fullPath.get(),
                                       mechFlags, cipherFlags);
   if (srv != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
-#ifndef MOZ_NO_SMART_CARDS
   UniqueSECMODModule module(SECMOD_FindModule(moduleName.get()));
   if (!module) {
     return NS_ERROR_FAILURE;
   }
+
+#ifndef MOZ_NO_SMART_CARDS
   nsCOMPtr<nsINSSComponent> nssComponent(
     do_GetService(PSM_COMPONENT_CONTRACTID));
   nssComponent->LaunchSmartCardThread(module.get());
 #endif
 
+  nsAutoString scalarKey;
+  GetModuleNameForTelemetry(module.get(), scalarKey);
+  // Scalar keys must be between 0 and 70 characters (exclusive).
+  // GetModuleNameForTelemetry takes care of keys that are too long.
+  // If for some reason it couldn't come up with an appropriate name and
+  // returned an empty result, however, we need to not attempt to record this
+  // (it wouldn't give us anything useful anyway).
+  if (scalarKey.Length() > 0) {
+    Telemetry::ScalarSet(Telemetry::ScalarID::SECURITY_PKCS11_MODULES_LOADED,
+                         scalarKey, true);
+  }
   return NS_OK;
 }
 
 } } // namespace mozilla::psm
--- a/security/manager/ssl/PKCS11.h
+++ b/security/manager/ssl/PKCS11.h
@@ -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/. */
 #ifndef PKCS11_h
 #define PKCS11_h
 
 #include "nsIPKCS11.h"
 
 #include "nsNSSShutDown.h"
+#include "nsString.h"
 
 namespace mozilla { namespace psm {
 
 #define NS_PKCS11_CID \
   {0x74b7a390, 0x3b41, 0x11d4, { 0x8a, 0x80, 0x00, 0x60, 0x08, 0xc8, 0x44, 0xc3} }
 
 class PKCS11 : public nsIPKCS11
              , public nsNSSShutDownObject
@@ -26,11 +27,14 @@ public:
 
 protected:
   virtual ~PKCS11();
 
 private:
   virtual void virtualDestroyNSSReference() override {}
 };
 
+void GetModuleNameForTelemetry(/*in*/ const SECMODModule* module,
+                               /*out*/nsString& result);
+
 } } // namespace mozilla::psm
 
 #endif // PKCS11_h
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -3,16 +3,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 "nsNSSComponent.h"
 
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
+#include "PKCS11.h"
 #include "ScopedNSSTypes.h"
 #include "SharedSSLState.h"
 #include "cert.h"
 #include "certdb.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Casting.h"
 #include "mozilla/Preferences.h"
@@ -1944,16 +1945,35 @@ nsNSSComponent::InitializeNSS()
   LaunchSmartCardThreads();
 #endif
 
   mozilla::pkix::RegisterErrorTable();
 
   if (PK11_IsFIPS()) {
     Telemetry::Accumulate(Telemetry::FIPS_ENABLED, true);
   }
+
+  { // Introduce scope for the AutoSECMODListReadLock.
+    AutoSECMODListReadLock lock;
+    for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
+         list = list->next) {
+      nsAutoString scalarKey;
+      GetModuleNameForTelemetry(list->module, scalarKey);
+      // Scalar keys must be between 0 and 70 characters (exclusive).
+      // GetModuleNameForTelemetry takes care of keys that are too long. If for
+      // some reason it couldn't come up with an appropriate name and returned
+      // an empty result, however, we need to not attempt to record this (it
+      // wouldn't give us anything useful anyway).
+      if (scalarKey.Length() > 0) {
+        Telemetry::ScalarSet(
+          Telemetry::ScalarID::SECURITY_PKCS11_MODULES_LOADED, scalarKey, true);
+      }
+    }
+  }
+
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("NSS Initialization done\n"));
   mNSSInitialized = true;
 
   return NS_OK;
 }
 
 void
 nsNSSComponent::ShutdownNSS()
--- a/security/manager/ssl/tests/unit/test_pkcs11_module.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_module.js
@@ -53,23 +53,54 @@ function checkTestModuleExists() {
      "Test module lib name should include lib name of 'pkcs11testmodule'");
 
   notEqual(gModuleDB.findModuleByName("PKCS11 Test Module"), null,
            "Test module should be findable by name");
 
   return testModule;
 }
 
+function checkModuleTelemetry(additionalExpectedModule = undefined) {
+  let expectedModules = [
+    "NSS Internal PKCS #11 Module",
+    `${AppConstants.DLL_PREFIX}nssckbi${AppConstants.DLL_SUFFIX}`,
+  ];
+  if (additionalExpectedModule) {
+    expectedModules.push(additionalExpectedModule);
+  }
+  expectedModules.sort();
+  let telemetryService = Cc["@mozilla.org/base/telemetry;1"]
+                           .getService(Ci.nsITelemetry);
+  let telemetry = telemetryService.snapshotKeyedScalars(
+    Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT).parent;
+  let moduleTelemetry = telemetry["security.pkcs11_modules_loaded"];
+  let actualModules = [];
+  Object.keys(moduleTelemetry).forEach((key) => {
+    ok(moduleTelemetry[key], "each keyed scalar should be true");
+    actualModules.push(key);
+  });
+  actualModules.sort();
+  equal(actualModules.length, expectedModules.length,
+        "the number of actual and expected loaded modules should be the same");
+  for (let i in actualModules) {
+    equal(actualModules[i], expectedModules[i],
+          "actual and expected module names should match");
+  }
+}
+
 function run_test() {
   // Check that if we have never added the test module, that we don't find it
   // in the module list.
   checkTestModuleNotPresent();
+  checkModuleTelemetry();
 
   // Check that adding the test module makes it appear in the module list.
   loadPKCS11TestModule(true);
+  checkModuleTelemetry(
+    `${AppConstants.DLL_PREFIX}pkcs11testmodule${AppConstants.DLL_SUFFIX}`);
   let testModule = checkTestModuleExists();
 
   // Check that listing the slots for the test module works.
   let slots = testModule.listSlots();
   let testModuleSlotNames = [];
   while (slots.hasMoreElements()) {
     let slot = slots.getNext().QueryInterface(Ci.nsIPKCS11Slot);
     testModuleSlotNames.push(slot.name);
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -278,16 +278,32 @@ services.sync:
     kind: string
     keyed: false
     notification_emails:
       - sync-staff@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - main
 
+security:
+  pkcs11_modules_loaded:
+    bug_numbers:
+      - 1369911
+    description: >
+      A keyed boolean indicating the library names of the PKCS#11 modules that
+      have been loaded by the browser.
+    expires: "62"
+    kind: boolean
+    keyed: true
+    notification_emails:
+      - seceng-telemetry@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - main
+
 # The following section contains WebRTC nICEr scalars
 # For more info on ICE, see https://tools.ietf.org/html/rfc5245
 # For more info on STUN, see https://tools.ietf.org/html/rfc5389
 # For more info on TURN, see https://tools.ietf.org/html/rfc5766
 webrtc.nicer:
   stun_retransmits:
     bug_numbers:
       - 1325536