Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/manager/ssl/PKCS11ModuleDB.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "PKCS11ModuleDB.h"
8
9
#include "ScopedNSSTypes.h"
10
#include "mozilla/Telemetry.h"
11
#include "nsCRTGlue.h"
12
#include "nsIMutableArray.h"
13
#include "nsNSSCertHelper.h"
14
#include "nsNSSComponent.h"
15
#include "nsNativeCharsetUtils.h"
16
#include "nsPKCS11Slot.h"
17
#include "nsServiceManagerUtils.h"
18
19
namespace mozilla { namespace psm {
20
21
NS_IMPL_ISUPPORTS(PKCS11ModuleDB, nsIPKCS11ModuleDB)
22
23
// Convert the UTF16 name of the module as it appears to the user to the
24
// internal representation. For most modules this just involves converting from
25
// UTF16 to UTF8. For the builtin root module, it also involves mapping from the
26
// localized name to the internal, non-localized name.
27
static nsresult
28
NormalizeModuleNameIn(const nsAString& moduleNameIn, nsCString& moduleNameOut)
29
0
{
30
0
  nsAutoString localizedRootModuleName;
31
0
  nsresult rv = GetPIPNSSBundleString("RootCertModuleName",
32
0
                                      localizedRootModuleName);
33
0
  if (NS_FAILED(rv)) {
34
0
    return rv;
35
0
  }
36
0
  if (moduleNameIn.Equals(localizedRootModuleName)) {
37
0
    moduleNameOut.Assign(kRootModuleName);
38
0
    return NS_OK;
39
0
  }
40
0
  moduleNameOut.Assign(NS_ConvertUTF16toUTF8(moduleNameIn));
41
0
  return NS_OK;
42
0
}
43
44
// Delete a PKCS11 module from the user's profile.
45
NS_IMETHODIMP
46
PKCS11ModuleDB::DeleteModule(const nsAString& aModuleName)
47
0
{
48
0
  if (aModuleName.IsEmpty()) {
49
0
    return NS_ERROR_INVALID_ARG;
50
0
  }
51
0
52
0
  nsAutoCString moduleNameNormalized;
53
0
  nsresult rv = NormalizeModuleNameIn(aModuleName, moduleNameNormalized);
54
0
  if (NS_FAILED(rv)) {
55
0
    return rv;
56
0
  }
57
0
  // modType is an output variable. We ignore it.
58
0
  int32_t modType;
59
0
  SECStatus srv = SECMOD_DeleteModule(moduleNameNormalized.get(), &modType);
60
0
  if (srv != SECSuccess) {
61
0
    return NS_ERROR_FAILURE;
62
0
  }
63
0
64
0
  return NS_OK;
65
0
}
66
67
// Given a PKCS#11 module, determines an appropriate name to identify it for the
68
// purposes of gathering telemetry. For 3rd party PKCS#11 modules, this should
69
// be the name of the dynamic library that implements the module. For built-in
70
// NSS modules, it will be the common name of the module.
71
// Because the result will be used as a telemetry scalar key (which must be less
72
// than 70 characters), this function will also truncate the result if it
73
// exceeds this limit. (Note that unfortunately telemetry doesn't expose a way
74
// to programmatically query the scalar key length limit, so we have to
75
// hard-code the value here.)
76
void
77
GetModuleNameForTelemetry(/*in*/ const SECMODModule* module,
78
                          /*out*/nsString& result)
79
0
{
80
0
  result.Truncate();
81
0
  if (module->dllName) {
82
0
    result.AssignASCII(module->dllName);
83
0
    int32_t separatorIndex = result.RFind(FILE_PATH_SEPARATOR);
84
0
    if (separatorIndex != kNotFound) {
85
0
      result = Substring(result, separatorIndex + 1);
86
0
    }
87
0
  } else {
88
0
    result.AssignASCII(module->commonName);
89
0
  }
90
0
  if (result.Length() >= 70) {
91
0
    result.Truncate(69);
92
0
  }
93
0
}
94
95
// Add a new PKCS11 module to the user's profile.
96
NS_IMETHODIMP
97
PKCS11ModuleDB::AddModule(const nsAString& aModuleName,
98
                          const nsAString& aLibraryFullPath,
99
                          int32_t aCryptoMechanismFlags,
100
                          int32_t aCipherFlags)
101
0
{
102
0
  if (aModuleName.IsEmpty()) {
103
0
    return NS_ERROR_INVALID_ARG;
104
0
  }
105
0
106
0
  // "Root Certs" is the name some NSS command-line utilities will give the
107
0
  // roots module if they decide to load it when there happens to be a
108
0
  // `MOZ_DLL_PREFIX "nssckbi" MOZ_DLL_SUFFIX` file in the directory being
109
0
  // operated on.  This causes failures, so as a workaround, the PSM
110
0
  // initialization code will unconditionally remove any module named "Root
111
0
  // Certs". We should prevent the user from adding an unrelated module named
112
0
  // "Root Certs" in the first place so PSM doesn't delete it. See bug 1406396.
113
0
  if (aModuleName.EqualsLiteral("Root Certs")) {
114
0
    return NS_ERROR_ILLEGAL_VALUE;
115
0
  }
116
0
117
0
  // There appears to be a deadlock if we try to load modules concurrently, so
118
0
  // just wait until the loadable roots module has been loaded.
119
0
  nsresult rv = BlockUntilLoadableRootsLoaded();
120
0
  if (NS_FAILED(rv)) {
121
0
    return rv;
122
0
  }
123
0
124
0
  nsAutoCString moduleNameNormalized;
125
0
  rv = NormalizeModuleNameIn(aModuleName, moduleNameNormalized);
126
0
  if (NS_FAILED(rv)) {
127
0
    return rv;
128
0
  }
129
0
  nsCString fullPath;
130
0
  // NSS doesn't support Unicode path.  Use native charset
131
0
  NS_CopyUnicodeToNative(aLibraryFullPath, fullPath);
132
0
  uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags);
133
0
  uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags);
134
0
  SECStatus srv = SECMOD_AddNewModule(moduleNameNormalized.get(),
135
0
                                      fullPath.get(), mechFlags, cipherFlags);
136
0
  if (srv != SECSuccess) {
137
0
    return NS_ERROR_FAILURE;
138
0
  }
139
0
140
0
  UniqueSECMODModule module(SECMOD_FindModule(moduleNameNormalized.get()));
141
0
  if (!module) {
142
0
    return NS_ERROR_FAILURE;
143
0
  }
144
0
145
0
  nsAutoString scalarKey;
146
0
  GetModuleNameForTelemetry(module.get(), scalarKey);
147
0
  // Scalar keys must be between 0 and 70 characters (exclusive).
148
0
  // GetModuleNameForTelemetry takes care of keys that are too long.
149
0
  // If for some reason it couldn't come up with an appropriate name and
150
0
  // returned an empty result, however, we need to not attempt to record this
151
0
  // (it wouldn't give us anything useful anyway).
152
0
  if (scalarKey.Length() > 0) {
153
0
    Telemetry::ScalarSet(Telemetry::ScalarID::SECURITY_PKCS11_MODULES_LOADED,
154
0
                         scalarKey, true);
155
0
  }
156
0
  return NS_OK;
157
0
}
158
159
NS_IMETHODIMP
160
PKCS11ModuleDB::ListModules(nsISimpleEnumerator** _retval)
161
0
{
162
0
  NS_ENSURE_ARG_POINTER(_retval);
163
0
164
0
  nsresult rv = BlockUntilLoadableRootsLoaded();
165
0
  if (NS_FAILED(rv)) {
166
0
    return rv;
167
0
  }
168
0
169
0
  nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
170
0
  if (!array) {
171
0
    return NS_ERROR_FAILURE;
172
0
  }
173
0
174
0
  /* lock down the list for reading */
175
0
  AutoSECMODListReadLock lock;
176
0
  for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
177
0
       list = list->next) {
178
0
    nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
179
0
    nsresult rv = array->AppendElement(module);
180
0
    if (NS_FAILED(rv)) {
181
0
      return rv;
182
0
    }
183
0
  }
184
0
185
0
  /* Get the modules in the database that didn't load */
186
0
  for (SECMODModuleList* list = SECMOD_GetDeadModuleList(); list;
187
0
       list = list->next) {
188
0
    nsCOMPtr<nsIPKCS11Module> module = new nsPKCS11Module(list->module);
189
0
    nsresult rv = array->AppendElement(module);
190
0
    if (NS_FAILED(rv)) {
191
0
      return rv;
192
0
    }
193
0
  }
194
0
195
0
  return array->Enumerate(_retval, NS_GET_IID(nsIPKCS11Module));
196
0
}
197
198
NS_IMETHODIMP
199
PKCS11ModuleDB::GetCanToggleFIPS(bool* aCanToggleFIPS)
200
0
{
201
0
  NS_ENSURE_ARG_POINTER(aCanToggleFIPS);
202
0
203
0
  *aCanToggleFIPS = SECMOD_CanDeleteInternalModule();
204
0
  return NS_OK;
205
0
}
206
207
208
NS_IMETHODIMP
209
PKCS11ModuleDB::ToggleFIPSMode()
210
0
{
211
0
  // The way to toggle FIPS mode in NSS is extremely obscure. Basically, we
212
0
  // delete the internal module, and it gets replaced with the opposite module
213
0
  // (i.e. if it was FIPS before, then it becomes non-FIPS next).
214
0
  // SECMOD_GetInternalModule() returns a pointer to a local copy of the
215
0
  // internal module stashed in NSS.  We don't want to delete it since it will
216
0
  // cause much pain in NSS.
217
0
  SECMODModule* internal = SECMOD_GetInternalModule();
218
0
  if (!internal) {
219
0
    return NS_ERROR_FAILURE;
220
0
  }
221
0
222
0
  if (SECMOD_DeleteInternalModule(internal->commonName) != SECSuccess) {
223
0
    return NS_ERROR_FAILURE;
224
0
  }
225
0
226
0
  if (PK11_IsFIPS()) {
227
0
    Telemetry::Accumulate(Telemetry::FIPS_ENABLED, true);
228
0
  }
229
0
230
0
  return NS_OK;
231
0
}
232
233
NS_IMETHODIMP
234
PKCS11ModuleDB::GetIsFIPSEnabled(bool* aIsFIPSEnabled)
235
0
{
236
0
  NS_ENSURE_ARG_POINTER(aIsFIPSEnabled);
237
0
238
0
  *aIsFIPSEnabled = PK11_IsFIPS();
239
0
  return NS_OK;
240
0
}
241
242
} } // namespace mozilla::psm