/src/mozilla-central/security/manager/ssl/LocalCertService.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
4 | | |
5 | | #include "LocalCertService.h" |
6 | | |
7 | | #include "CryptoTask.h" |
8 | | #include "ScopedNSSTypes.h" |
9 | | #include "cert.h" |
10 | | #include "mozilla/Casting.h" |
11 | | #include "mozilla/ModuleUtils.h" |
12 | | #include "mozilla/RefPtr.h" |
13 | | #include "nsIPK11Token.h" |
14 | | #include "nsIPK11TokenDB.h" |
15 | | #include "nsIX509Cert.h" |
16 | | #include "nsIX509CertValidity.h" |
17 | | #include "nsLiteralString.h" |
18 | | #include "nsProxyRelease.h" |
19 | | #include "nsServiceManagerUtils.h" |
20 | | #include "nsString.h" |
21 | | #include "pk11pub.h" |
22 | | |
23 | | namespace mozilla { |
24 | | |
25 | | // Given a name, searches the internal certificate/key database for a |
26 | | // self-signed certificate with subject and issuer distinguished name equal to |
27 | | // "CN={name}". This assumes that the user has already authenticated to the |
28 | | // internal DB if necessary. |
29 | | static nsresult |
30 | | FindLocalCertByName(const nsACString& aName, |
31 | | /*out*/ UniqueCERTCertificate& aResult) |
32 | 0 | { |
33 | 0 | aResult.reset(nullptr); |
34 | 0 | NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN="); |
35 | 0 | nsAutoCString expectedDistinguishedName(commonNamePrefix + aName); |
36 | 0 | UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); |
37 | 0 | if (!slot) { |
38 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
39 | 0 | } |
40 | 0 | UniqueCERTCertList certList(PK11_ListCertsInSlot(slot.get())); |
41 | 0 | if (!certList) { |
42 | 0 | return NS_ERROR_UNEXPECTED; |
43 | 0 | } |
44 | 0 | for (const CERTCertListNode* node = CERT_LIST_HEAD(certList); |
45 | 0 | !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) { |
46 | 0 | // If this isn't a self-signed cert, it's not what we're interested in. |
47 | 0 | if (!node->cert->isRoot) { |
48 | 0 | continue; |
49 | 0 | } |
50 | 0 | if (!expectedDistinguishedName.Equals(node->cert->subjectName)) { |
51 | 0 | continue; // Subject should match nickname |
52 | 0 | } |
53 | 0 | if (!expectedDistinguishedName.Equals(node->cert->issuerName)) { |
54 | 0 | continue; // Issuer should match nickname |
55 | 0 | } |
56 | 0 | // We found a match. |
57 | 0 | aResult.reset(CERT_DupCertificate(node->cert)); |
58 | 0 | return NS_OK; |
59 | 0 | } |
60 | 0 | return NS_OK; |
61 | 0 | } |
62 | | |
63 | | class LocalCertTask : public CryptoTask |
64 | | { |
65 | | protected: |
66 | | explicit LocalCertTask(const nsACString& aNickname) |
67 | | : mNickname(aNickname) |
68 | 0 | { |
69 | 0 | } |
70 | | |
71 | | nsresult RemoveExisting() |
72 | 0 | { |
73 | 0 | // Search for any existing self-signed certs with this name and remove them |
74 | 0 | for (;;) { |
75 | 0 | UniqueCERTCertificate cert; |
76 | 0 | nsresult rv = FindLocalCertByName(mNickname, cert); |
77 | 0 | if (NS_FAILED(rv)) { |
78 | 0 | return rv; |
79 | 0 | } |
80 | 0 | // If we didn't find a match, we're done. |
81 | 0 | if (!cert) { |
82 | 0 | return NS_OK; |
83 | 0 | } |
84 | 0 | rv = MapSECStatus(PK11_DeleteTokenCertAndKey(cert.get(), nullptr)); |
85 | 0 | if (NS_FAILED(rv)) { |
86 | 0 | return rv; |
87 | 0 | } |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | | nsCString mNickname; |
92 | | }; |
93 | | |
94 | | class LocalCertGetTask final : public LocalCertTask |
95 | | { |
96 | | public: |
97 | | LocalCertGetTask(const nsACString& aNickname, |
98 | | nsILocalCertGetCallback* aCallback) |
99 | | : LocalCertTask(aNickname) |
100 | | , mCallback(new nsMainThreadPtrHolder<nsILocalCertGetCallback>( |
101 | | "LocalCertGetTask::mCallback", aCallback)) |
102 | | , mCert(nullptr) |
103 | 0 | { |
104 | 0 | } |
105 | | |
106 | | private: |
107 | | virtual nsresult CalculateResult() override |
108 | 0 | { |
109 | 0 | // Try to lookup an existing cert in the DB |
110 | 0 | nsresult rv = GetFromDB(); |
111 | 0 | // Make a new one if getting fails |
112 | 0 | if (NS_FAILED(rv)) { |
113 | 0 | rv = Generate(); |
114 | 0 | } |
115 | 0 | // If generation fails, we're out of luck |
116 | 0 | if (NS_FAILED(rv)) { |
117 | 0 | return rv; |
118 | 0 | } |
119 | 0 | |
120 | 0 | // Validate cert, make a new one if it fails |
121 | 0 | rv = Validate(); |
122 | 0 | if (NS_FAILED(rv)) { |
123 | 0 | rv = Generate(); |
124 | 0 | } |
125 | 0 | // If generation fails, we're out of luck |
126 | 0 | if (NS_FAILED(rv)) { |
127 | 0 | return rv; |
128 | 0 | } |
129 | 0 | |
130 | 0 | return NS_OK; |
131 | 0 | } |
132 | | |
133 | | nsresult Generate() |
134 | 0 | { |
135 | 0 | nsresult rv; |
136 | 0 |
|
137 | 0 | // Get the key slot for generation later |
138 | 0 | UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); |
139 | 0 | if (!slot) { |
140 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
141 | 0 | } |
142 | 0 | |
143 | 0 | // Remove existing certs with this name (if any) |
144 | 0 | rv = RemoveExisting(); |
145 | 0 | if (NS_FAILED(rv)) { |
146 | 0 | return rv; |
147 | 0 | } |
148 | 0 | |
149 | 0 | // Generate a new cert |
150 | 0 | NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN="); |
151 | 0 | nsAutoCString subjectNameStr(commonNamePrefix + mNickname); |
152 | 0 | UniqueCERTName subjectName(CERT_AsciiToName(subjectNameStr.get())); |
153 | 0 | if (!subjectName) { |
154 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
155 | 0 | } |
156 | 0 | |
157 | 0 | // Use the well-known NIST P-256 curve |
158 | 0 | SECOidData* curveOidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); |
159 | 0 | if (!curveOidData) { |
160 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
161 | 0 | } |
162 | 0 | |
163 | 0 | // Get key params from the curve |
164 | 0 | ScopedAutoSECItem keyParams(2 + curveOidData->oid.len); |
165 | 0 | keyParams.data[0] = SEC_ASN1_OBJECT_ID; |
166 | 0 | keyParams.data[1] = curveOidData->oid.len; |
167 | 0 | memcpy(keyParams.data + 2, curveOidData->oid.data, curveOidData->oid.len); |
168 | 0 |
|
169 | 0 | // Generate cert key pair |
170 | 0 | SECKEYPublicKey* tempPublicKey; |
171 | 0 | UniqueSECKEYPrivateKey privateKey( |
172 | 0 | PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &keyParams, |
173 | 0 | &tempPublicKey, true /* token */, |
174 | 0 | true /* sensitive */, nullptr)); |
175 | 0 | UniqueSECKEYPublicKey publicKey(tempPublicKey); |
176 | 0 | tempPublicKey = nullptr; |
177 | 0 | if (!privateKey || !publicKey) { |
178 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
179 | 0 | } |
180 | 0 | |
181 | 0 | // Create subject public key info and cert request |
182 | 0 | UniqueCERTSubjectPublicKeyInfo spki( |
183 | 0 | SECKEY_CreateSubjectPublicKeyInfo(publicKey.get())); |
184 | 0 | if (!spki) { |
185 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
186 | 0 | } |
187 | 0 | UniqueCERTCertificateRequest certRequest( |
188 | 0 | CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr)); |
189 | 0 | if (!certRequest) { |
190 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
191 | 0 | } |
192 | 0 | |
193 | 0 | // Valid from one day before to 1 year after |
194 | 0 | static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) |
195 | 0 | * PRTime(60) // sec |
196 | 0 | * PRTime(60) // min |
197 | 0 | * PRTime(24); // hours |
198 | 0 |
|
199 | 0 | PRTime now = PR_Now(); |
200 | 0 | PRTime notBefore = now - oneDay; |
201 | 0 | PRTime notAfter = now + (PRTime(365) * oneDay); |
202 | 0 | UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter)); |
203 | 0 | if (!validity) { |
204 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
205 | 0 | } |
206 | 0 | |
207 | 0 | // Generate random serial |
208 | 0 | unsigned long serial; |
209 | 0 | // This serial in principle could collide, but it's unlikely |
210 | 0 | rv = MapSECStatus(PK11_GenerateRandomOnSlot( |
211 | 0 | slot.get(), BitwiseCast<unsigned char*, unsigned long*>(&serial), |
212 | 0 | sizeof(serial))); |
213 | 0 | if (NS_FAILED(rv)) { |
214 | 0 | return rv; |
215 | 0 | } |
216 | 0 | |
217 | 0 | // Create the cert from these pieces |
218 | 0 | UniqueCERTCertificate cert( |
219 | 0 | CERT_CreateCertificate(serial, subjectName.get(), validity.get(), |
220 | 0 | certRequest.get())); |
221 | 0 | if (!cert) { |
222 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
223 | 0 | } |
224 | 0 | |
225 | 0 | // Update the cert version to X509v3 |
226 | 0 | if (!cert->version.data) { |
227 | 0 | return NS_ERROR_INVALID_POINTER; |
228 | 0 | } |
229 | 0 | *(cert->version.data) = SEC_CERTIFICATE_VERSION_3; |
230 | 0 | cert->version.len = 1; |
231 | 0 |
|
232 | 0 | // Set cert signature algorithm |
233 | 0 | PLArenaPool* arena = cert->arena; |
234 | 0 | if (!arena) { |
235 | 0 | return NS_ERROR_INVALID_POINTER; |
236 | 0 | } |
237 | 0 | rv = MapSECStatus( |
238 | 0 | SECOID_SetAlgorithmID(arena, &cert->signature, |
239 | 0 | SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0)); |
240 | 0 | if (NS_FAILED(rv)) { |
241 | 0 | return rv; |
242 | 0 | } |
243 | 0 | |
244 | 0 | // Encode and self-sign the cert |
245 | 0 | UniqueSECItem certDER( |
246 | 0 | SEC_ASN1EncodeItem(nullptr, nullptr, cert.get(), |
247 | 0 | SEC_ASN1_GET(CERT_CertificateTemplate))); |
248 | 0 | if (!certDER) { |
249 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
250 | 0 | } |
251 | 0 | rv = MapSECStatus( |
252 | 0 | SEC_DerSignData(arena, &cert->derCert, certDER->data, certDER->len, |
253 | 0 | privateKey.get(), |
254 | 0 | SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE)); |
255 | 0 | if (NS_FAILED(rv)) { |
256 | 0 | return rv; |
257 | 0 | } |
258 | 0 | |
259 | 0 | // Create a CERTCertificate from the signed data |
260 | 0 | UniqueCERTCertificate certFromDER( |
261 | 0 | CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &cert->derCert, nullptr, |
262 | 0 | true /* perm */, true /* copyDER */)); |
263 | 0 | if (!certFromDER) { |
264 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
265 | 0 | } |
266 | 0 | |
267 | 0 | // Save the cert in the DB |
268 | 0 | rv = MapSECStatus(PK11_ImportCert(slot.get(), certFromDER.get(), |
269 | 0 | CK_INVALID_HANDLE, mNickname.get(), |
270 | 0 | false /* unused */)); |
271 | 0 | if (NS_FAILED(rv)) { |
272 | 0 | return rv; |
273 | 0 | } |
274 | 0 | |
275 | 0 | // We should now have cert in the DB, read it back in nsIX509Cert form |
276 | 0 | return GetFromDB(); |
277 | 0 | } |
278 | | |
279 | | nsresult GetFromDB() |
280 | 0 | { |
281 | 0 | UniqueCERTCertificate cert; |
282 | 0 | nsresult rv = FindLocalCertByName(mNickname, cert); |
283 | 0 | if (NS_FAILED(rv)) { |
284 | 0 | return rv; |
285 | 0 | } |
286 | 0 | if (!cert) { |
287 | 0 | return NS_ERROR_FAILURE; |
288 | 0 | } |
289 | 0 | mCert = nsNSSCertificate::Create(cert.get()); |
290 | 0 | return NS_OK; |
291 | 0 | } |
292 | | |
293 | | nsresult Validate() |
294 | 0 | { |
295 | 0 | // Verify cert is self-signed |
296 | 0 | bool selfSigned; |
297 | 0 | nsresult rv = mCert->GetIsSelfSigned(&selfSigned); |
298 | 0 | if (NS_FAILED(rv)) { |
299 | 0 | return rv; |
300 | 0 | } |
301 | 0 | if (!selfSigned) { |
302 | 0 | return NS_ERROR_FAILURE; |
303 | 0 | } |
304 | 0 | |
305 | 0 | // Check that subject and issuer match nickname |
306 | 0 | nsAutoString subjectName; |
307 | 0 | nsAutoString issuerName; |
308 | 0 | mCert->GetSubjectName(subjectName); |
309 | 0 | mCert->GetIssuerName(issuerName); |
310 | 0 | if (!subjectName.Equals(issuerName)) { |
311 | 0 | return NS_ERROR_FAILURE; |
312 | 0 | } |
313 | 0 | NS_NAMED_LITERAL_STRING(commonNamePrefix, "CN="); |
314 | 0 | nsAutoString subjectNameFromNickname( |
315 | 0 | commonNamePrefix + NS_ConvertASCIItoUTF16(mNickname)); |
316 | 0 | if (!subjectName.Equals(subjectNameFromNickname)) { |
317 | 0 | return NS_ERROR_FAILURE; |
318 | 0 | } |
319 | 0 | |
320 | 0 | nsCOMPtr<nsIX509CertValidity> validity; |
321 | 0 | mCert->GetValidity(getter_AddRefs(validity)); |
322 | 0 |
|
323 | 0 | PRTime notBefore, notAfter; |
324 | 0 | validity->GetNotBefore(¬Before); |
325 | 0 | validity->GetNotAfter(¬After); |
326 | 0 |
|
327 | 0 | // Ensure cert will last at least one more day |
328 | 0 | static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) |
329 | 0 | * PRTime(60) // sec |
330 | 0 | * PRTime(60) // min |
331 | 0 | * PRTime(24); // hours |
332 | 0 | PRTime now = PR_Now(); |
333 | 0 | if (notBefore > now || |
334 | 0 | notAfter < (now - oneDay)) { |
335 | 0 | return NS_ERROR_FAILURE; |
336 | 0 | } |
337 | 0 | |
338 | 0 | return NS_OK; |
339 | 0 | } |
340 | | |
341 | | virtual void CallCallback(nsresult rv) override |
342 | 0 | { |
343 | 0 | (void) mCallback->HandleCert(mCert, rv); |
344 | 0 | } |
345 | | |
346 | | nsMainThreadPtrHandle<nsILocalCertGetCallback> mCallback; |
347 | | nsCOMPtr<nsIX509Cert> mCert; // out |
348 | | }; |
349 | | |
350 | | class LocalCertRemoveTask final : public LocalCertTask |
351 | | { |
352 | | public: |
353 | | LocalCertRemoveTask(const nsACString& aNickname, |
354 | | nsILocalCertCallback* aCallback) |
355 | | : LocalCertTask(aNickname) |
356 | | , mCallback(new nsMainThreadPtrHolder<nsILocalCertCallback>( |
357 | | "LocalCertRemoveTask::mCallback", aCallback)) |
358 | 0 | { |
359 | 0 | } |
360 | | |
361 | | private: |
362 | | virtual nsresult CalculateResult() override |
363 | 0 | { |
364 | 0 | return RemoveExisting(); |
365 | 0 | } |
366 | | |
367 | | virtual void CallCallback(nsresult rv) override |
368 | 0 | { |
369 | 0 | (void) mCallback->HandleResult(rv); |
370 | 0 | } |
371 | | |
372 | | nsMainThreadPtrHandle<nsILocalCertCallback> mCallback; |
373 | | }; |
374 | | |
375 | | NS_IMPL_ISUPPORTS(LocalCertService, nsILocalCertService) |
376 | | |
377 | | LocalCertService::LocalCertService() |
378 | 0 | { |
379 | 0 | } |
380 | | |
381 | | LocalCertService::~LocalCertService() |
382 | 0 | { |
383 | 0 | } |
384 | | |
385 | | nsresult |
386 | | LocalCertService::LoginToKeySlot() |
387 | 0 | { |
388 | 0 | nsresult rv; |
389 | 0 |
|
390 | 0 | // Get access to key slot |
391 | 0 | UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); |
392 | 0 | if (!slot) { |
393 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
394 | 0 | } |
395 | 0 | |
396 | 0 | // If no user password yet, set it an empty one |
397 | 0 | if (PK11_NeedUserInit(slot.get())) { |
398 | 0 | rv = MapSECStatus(PK11_InitPin(slot.get(), "", "")); |
399 | 0 | if (NS_FAILED(rv)) { |
400 | 0 | return rv; |
401 | 0 | } |
402 | 0 | } |
403 | 0 | |
404 | 0 | // If user has a password set, prompt to login |
405 | 0 | if (PK11_NeedLogin(slot.get()) && !PK11_IsLoggedIn(slot.get(), nullptr)) { |
406 | 0 | // Switching to XPCOM to get the UI prompt that PSM owns |
407 | 0 | nsCOMPtr<nsIPK11TokenDB> tokenDB = |
408 | 0 | do_GetService(NS_PK11TOKENDB_CONTRACTID); |
409 | 0 | if (!tokenDB) { |
410 | 0 | return NS_ERROR_FAILURE; |
411 | 0 | } |
412 | 0 | nsCOMPtr<nsIPK11Token> keyToken; |
413 | 0 | tokenDB->GetInternalKeyToken(getter_AddRefs(keyToken)); |
414 | 0 | if (!keyToken) { |
415 | 0 | return NS_ERROR_FAILURE; |
416 | 0 | } |
417 | 0 | // Prompt the user to login |
418 | 0 | return keyToken->Login(false /* force */); |
419 | 0 | } |
420 | 0 | |
421 | 0 | return NS_OK; |
422 | 0 | } |
423 | | |
424 | | NS_IMETHODIMP |
425 | | LocalCertService::GetOrCreateCert(const nsACString& aNickname, |
426 | | nsILocalCertGetCallback* aCallback) |
427 | 0 | { |
428 | 0 | if (NS_WARN_IF(aNickname.IsEmpty())) { |
429 | 0 | return NS_ERROR_INVALID_ARG; |
430 | 0 | } |
431 | 0 | if (NS_WARN_IF(!aCallback)) { |
432 | 0 | return NS_ERROR_INVALID_POINTER; |
433 | 0 | } |
434 | 0 | |
435 | 0 | // Before sending off the task, login to key slot if needed |
436 | 0 | nsresult rv = LoginToKeySlot(); |
437 | 0 | if (NS_FAILED(rv)) { |
438 | 0 | aCallback->HandleCert(nullptr, rv); |
439 | 0 | return NS_OK; |
440 | 0 | } |
441 | 0 | |
442 | 0 | RefPtr<LocalCertGetTask> task(new LocalCertGetTask(aNickname, aCallback)); |
443 | 0 | return task->Dispatch("LocalCertGet"); |
444 | 0 | } |
445 | | |
446 | | NS_IMETHODIMP |
447 | | LocalCertService::RemoveCert(const nsACString& aNickname, |
448 | | nsILocalCertCallback* aCallback) |
449 | 0 | { |
450 | 0 | if (NS_WARN_IF(aNickname.IsEmpty())) { |
451 | 0 | return NS_ERROR_INVALID_ARG; |
452 | 0 | } |
453 | 0 | if (NS_WARN_IF(!aCallback)) { |
454 | 0 | return NS_ERROR_INVALID_POINTER; |
455 | 0 | } |
456 | 0 | |
457 | 0 | // Before sending off the task, login to key slot if needed |
458 | 0 | nsresult rv = LoginToKeySlot(); |
459 | 0 | if (NS_FAILED(rv)) { |
460 | 0 | aCallback->HandleResult(rv); |
461 | 0 | return NS_OK; |
462 | 0 | } |
463 | 0 | |
464 | 0 | RefPtr<LocalCertRemoveTask> task( |
465 | 0 | new LocalCertRemoveTask(aNickname, aCallback)); |
466 | 0 | return task->Dispatch("LocalCertRm"); |
467 | 0 | } |
468 | | |
469 | | NS_IMETHODIMP |
470 | | LocalCertService::GetLoginPromptRequired(bool* aRequired) |
471 | 0 | { |
472 | 0 | nsresult rv; |
473 | 0 |
|
474 | 0 | // Get access to key slot |
475 | 0 | UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); |
476 | 0 | if (!slot) { |
477 | 0 | return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); |
478 | 0 | } |
479 | 0 | |
480 | 0 | // If no user password yet, set it an empty one |
481 | 0 | if (PK11_NeedUserInit(slot.get())) { |
482 | 0 | rv = MapSECStatus(PK11_InitPin(slot.get(), "", "")); |
483 | 0 | if (NS_FAILED(rv)) { |
484 | 0 | return rv; |
485 | 0 | } |
486 | 0 | } |
487 | 0 | |
488 | 0 | *aRequired = PK11_NeedLogin(slot.get()) && |
489 | 0 | !PK11_IsLoggedIn(slot.get(), nullptr); |
490 | 0 | return NS_OK; |
491 | 0 | } |
492 | | |
493 | | #define LOCALCERTSERVICE_CID \ |
494 | | { 0x47402be2, 0xe653, 0x45d0, \ |
495 | | { 0x8d, 0xaa, 0x9f, 0x0d, 0xce, 0x0a, 0xc1, 0x48 } } |
496 | | |
497 | | NS_GENERIC_FACTORY_CONSTRUCTOR(LocalCertService) |
498 | | |
499 | | NS_DEFINE_NAMED_CID(LOCALCERTSERVICE_CID); |
500 | | |
501 | | static const Module::CIDEntry kLocalCertServiceCIDs[] = { |
502 | | { &kLOCALCERTSERVICE_CID, false, nullptr, LocalCertServiceConstructor }, |
503 | | { nullptr } |
504 | | }; |
505 | | |
506 | | static const Module::ContractIDEntry kLocalCertServiceContracts[] = { |
507 | | { LOCALCERTSERVICE_CONTRACTID, &kLOCALCERTSERVICE_CID }, |
508 | | { nullptr } |
509 | | }; |
510 | | |
511 | | static const Module kLocalCertServiceModule = { |
512 | | Module::kVersion, |
513 | | kLocalCertServiceCIDs, |
514 | | kLocalCertServiceContracts |
515 | | }; |
516 | | |
517 | | NSMODULE_DEFN(LocalCertServiceModule) = &kLocalCertServiceModule; |
518 | | |
519 | | } // namespace mozilla |