/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 |