Line data Source code
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 "nsPKCS12Blob.h"
6 :
7 : #include "ScopedNSSTypes.h"
8 : #include "mozilla/Assertions.h"
9 : #include "mozilla/Casting.h"
10 : #include "mozilla/Unused.h"
11 : #include "nsICertificateDialogs.h"
12 : #include "nsIFile.h"
13 : #include "nsIInputStream.h"
14 : #include "nsNSSCertHelper.h"
15 : #include "nsNSSCertificate.h"
16 : #include "nsNSSHelper.h"
17 : #include "nsNetUtil.h"
18 : #include "nsReadableUtils.h"
19 : #include "nsThreadUtils.h"
20 : #include "pkix/pkixtypes.h"
21 : #include "secerr.h"
22 :
23 : using namespace mozilla;
24 : extern LazyLogModule gPIPNSSLog;
25 :
26 : #define PIP_PKCS12_TMPFILENAME NS_LITERAL_CSTRING(".pip_p12tmp")
27 : #define PIP_PKCS12_BUFFER_SIZE 2048
28 : #define PIP_PKCS12_USER_CANCELED 3
29 : #define PIP_PKCS12_NOSMARTCARD_EXPORT 4
30 : #define PIP_PKCS12_RESTORE_FAILED 5
31 : #define PIP_PKCS12_BACKUP_FAILED 6
32 : #define PIP_PKCS12_NSS_ERROR 7
33 :
34 : // constructor
35 0 : nsPKCS12Blob::nsPKCS12Blob()
36 : : mCertArray(nullptr)
37 0 : , mTmpFile(nullptr)
38 : {
39 0 : mUIContext = new PipUIContext();
40 0 : }
41 :
42 : // destructor
43 0 : nsPKCS12Blob::~nsPKCS12Blob()
44 : {
45 0 : nsNSSShutDownPreventionLock locker;
46 0 : if (isAlreadyShutDown()) {
47 0 : return;
48 : }
49 :
50 0 : shutdown(ShutdownCalledFrom::Object);
51 0 : }
52 :
53 : // nsPKCS12Blob::ImportFromFile
54 : //
55 : // Given a file handle, read a PKCS#12 blob from that file, decode it, and
56 : // import the results into the internal database.
57 : nsresult
58 0 : nsPKCS12Blob::ImportFromFile(nsIFile *file)
59 : {
60 0 : nsNSSShutDownPreventionLock locker;
61 0 : nsresult rv = NS_OK;
62 :
63 : RetryReason wantRetry;
64 :
65 0 : do {
66 0 : rv = ImportFromFileHelper(file, im_standard_prompt, wantRetry);
67 :
68 0 : if (NS_SUCCEEDED(rv) && wantRetry == rr_auto_retry_empty_password_flavors) {
69 0 : rv = ImportFromFileHelper(file, im_try_zero_length_secitem, wantRetry);
70 : }
71 : }
72 0 : while (NS_SUCCEEDED(rv) && (wantRetry != rr_do_not_retry));
73 :
74 0 : return rv;
75 : }
76 :
77 : nsresult
78 0 : nsPKCS12Blob::ImportFromFileHelper(nsIFile *file,
79 : nsPKCS12Blob::ImportMode aImportMode,
80 : nsPKCS12Blob::RetryReason &aWantRetry)
81 : {
82 0 : nsNSSShutDownPreventionLock locker;
83 0 : nsresult rv = NS_OK;
84 0 : SECStatus srv = SECSuccess;
85 0 : SEC_PKCS12DecoderContext *dcx = nullptr;
86 0 : SECItem unicodePw = { siBuffer, nullptr, 0 };
87 :
88 0 : aWantRetry = rr_do_not_retry;
89 :
90 0 : UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
91 0 : if (!slot) {
92 0 : srv = SECFailure;
93 0 : goto finish;
94 : }
95 :
96 0 : if (aImportMode == im_try_zero_length_secitem) {
97 0 : unicodePw.len = 0;
98 : } else {
99 : // get file password (unicode)
100 0 : rv = getPKCS12FilePassword(&unicodePw);
101 0 : if (NS_FAILED(rv)) goto finish;
102 0 : if (!unicodePw.data) {
103 0 : handleError(PIP_PKCS12_USER_CANCELED);
104 0 : return NS_OK;
105 : }
106 : }
107 :
108 : // initialize the decoder
109 0 : dcx = SEC_PKCS12DecoderStart(&unicodePw, slot.get(), nullptr, nullptr,
110 0 : nullptr, nullptr, nullptr, nullptr);
111 0 : if (!dcx) {
112 0 : srv = SECFailure;
113 0 : goto finish;
114 : }
115 : // read input file and feed it to the decoder
116 0 : rv = inputToDecoder(dcx, file);
117 0 : if (NS_FAILED(rv)) {
118 0 : if (NS_ERROR_ABORT == rv) {
119 : // inputToDecoder indicated a NSS error
120 0 : srv = SECFailure;
121 : }
122 0 : goto finish;
123 : }
124 : // verify the blob
125 0 : srv = SEC_PKCS12DecoderVerify(dcx);
126 0 : if (srv) goto finish;
127 : // validate bags
128 0 : srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision);
129 0 : if (srv) goto finish;
130 : // import cert and key
131 0 : srv = SEC_PKCS12DecoderImportBags(dcx);
132 0 : if (srv) goto finish;
133 : // Later - check to see if this should become default email cert
134 : finish:
135 : // If srv != SECSuccess, NSS probably set a specific error code.
136 : // We should use that error code instead of inventing a new one
137 : // for every error possible.
138 0 : if (srv != SECSuccess) {
139 0 : if (SEC_ERROR_BAD_PASSWORD == PORT_GetError()) {
140 0 : if (unicodePw.len == sizeof(char16_t))
141 : {
142 : // no password chars available,
143 : // unicodeToItem allocated space for the trailing zero character only.
144 0 : aWantRetry = rr_auto_retry_empty_password_flavors;
145 : }
146 : else
147 : {
148 0 : aWantRetry = rr_bad_password;
149 0 : handleError(PIP_PKCS12_NSS_ERROR);
150 : }
151 : }
152 : else
153 : {
154 0 : handleError(PIP_PKCS12_NSS_ERROR);
155 : }
156 0 : } else if (NS_FAILED(rv)) {
157 0 : handleError(PIP_PKCS12_RESTORE_FAILED);
158 : }
159 : // finish the decoder
160 0 : if (dcx)
161 0 : SEC_PKCS12DecoderFinish(dcx);
162 0 : SECITEM_ZfreeItem(&unicodePw, false);
163 0 : return NS_OK;
164 : }
165 :
166 : static bool
167 0 : isExtractable(SECKEYPrivateKey *privKey)
168 : {
169 0 : ScopedAutoSECItem value;
170 : SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey,
171 0 : CKA_EXTRACTABLE, &value);
172 0 : if (rv != SECSuccess) {
173 0 : return false;
174 : }
175 :
176 0 : bool isExtractable = false;
177 0 : if ((value.len == 1) && value.data) {
178 0 : isExtractable = !!(*(CK_BBOOL*)value.data);
179 : }
180 0 : return isExtractable;
181 : }
182 :
183 : // nsPKCS12Blob::ExportToFile
184 : //
185 : // Having already loaded the certs, form them into a blob (loading the keys
186 : // also), encode the blob, and stuff it into the file.
187 : nsresult
188 0 : nsPKCS12Blob::ExportToFile(nsIFile *file,
189 : nsIX509Cert **certs, int numCerts)
190 : {
191 0 : nsNSSShutDownPreventionLock locker;
192 : nsresult rv;
193 0 : SECStatus srv = SECSuccess;
194 0 : SEC_PKCS12ExportContext *ecx = nullptr;
195 0 : SEC_PKCS12SafeInfo *certSafe = nullptr, *keySafe = nullptr;
196 : SECItem unicodePw;
197 0 : nsAutoString filePath;
198 : int i;
199 0 : nsCOMPtr<nsIFile> localFileRef;
200 : // init slot
201 :
202 0 : bool InformedUserNoSmartcardBackup = false;
203 0 : int numCertsExported = 0;
204 :
205 : // get file password (unicode)
206 0 : unicodePw.data = nullptr;
207 0 : rv = newPKCS12FilePassword(&unicodePw);
208 0 : if (NS_FAILED(rv)) goto finish;
209 0 : if (!unicodePw.data) {
210 0 : handleError(PIP_PKCS12_USER_CANCELED);
211 0 : return NS_OK;
212 : }
213 : // what about slotToUse in psm 1.x ???
214 : // create export context
215 0 : ecx = SEC_PKCS12CreateExportContext(nullptr, nullptr, nullptr /*slot*/, nullptr);
216 0 : if (!ecx) {
217 0 : srv = SECFailure;
218 0 : goto finish;
219 : }
220 : // add password integrity
221 0 : srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1);
222 0 : if (srv) goto finish;
223 0 : for (i=0; i<numCerts; i++) {
224 0 : nsNSSCertificate *cert = (nsNSSCertificate *)certs[i];
225 : // get it as a CERTCertificate XXX
226 0 : UniqueCERTCertificate nssCert(cert->GetCert());
227 0 : if (!nssCert) {
228 0 : rv = NS_ERROR_FAILURE;
229 0 : goto finish;
230 : }
231 : // We can only successfully export certs that are on
232 : // internal token. Most, if not all, smart card vendors
233 : // won't let you extract the private key (in any way
234 : // shape or form) from the card. So let's punt if
235 : // the cert is not in the internal db.
236 0 : if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) {
237 : // we aren't the internal token, see if the key is extractable.
238 0 : SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot,
239 0 : nssCert.get(), this);
240 :
241 0 : if (privKey) {
242 0 : bool privKeyIsExtractable = isExtractable(privKey);
243 :
244 0 : SECKEY_DestroyPrivateKey(privKey);
245 :
246 0 : if (!privKeyIsExtractable) {
247 0 : if (!InformedUserNoSmartcardBackup) {
248 0 : InformedUserNoSmartcardBackup = true;
249 0 : handleError(PIP_PKCS12_NOSMARTCARD_EXPORT);
250 : }
251 0 : continue;
252 : }
253 : }
254 : }
255 :
256 : // XXX this is why, to verify the slot is the same
257 : // PK11_FindObjectForCert(nssCert, nullptr, slot);
258 : // create the cert and key safes
259 0 : keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx);
260 0 : if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
261 0 : certSafe = keySafe;
262 : } else {
263 : certSafe = SEC_PKCS12CreatePasswordPrivSafe(ecx, &unicodePw,
264 0 : SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
265 : }
266 0 : if (!certSafe || !keySafe) {
267 0 : rv = NS_ERROR_FAILURE;
268 0 : goto finish;
269 : }
270 : // add the cert and key to the blob
271 0 : srv = SEC_PKCS12AddCertAndKey(ecx, certSafe, nullptr, nssCert.get(),
272 : CERT_GetDefaultCertDB(), // XXX
273 : keySafe, nullptr, true, &unicodePw,
274 0 : SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC);
275 0 : if (srv) goto finish;
276 : // cert was dup'ed, so release it
277 0 : ++numCertsExported;
278 : }
279 :
280 0 : if (!numCertsExported) goto finish;
281 :
282 : // prepare the instance to write to an export file
283 0 : this->mTmpFile = nullptr;
284 0 : file->GetPath(filePath);
285 : // Use the nsCOMPtr var localFileRef so that
286 : // the reference to the nsIFile we create gets released as soon as
287 : // we're out of scope, ie when this function exits.
288 0 : if (filePath.RFind(".p12", true, -1, 4) < 0) {
289 : // We're going to add the .p12 extension to the file name just like
290 : // Communicator used to. We create a new nsIFile and initialize
291 : // it with the new patch.
292 0 : filePath.AppendLiteral(".p12");
293 0 : localFileRef = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
294 0 : if (NS_FAILED(rv)) goto finish;
295 0 : localFileRef->InitWithPath(filePath);
296 0 : file = localFileRef;
297 : }
298 0 : rv = file->OpenNSPRFileDesc(PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0664,
299 0 : &mTmpFile);
300 0 : if (NS_FAILED(rv) || !this->mTmpFile) goto finish;
301 : // encode and write
302 0 : srv = SEC_PKCS12Encode(ecx, write_export_file, this);
303 0 : if (srv) goto finish;
304 : finish:
305 0 : if (NS_FAILED(rv) || srv != SECSuccess) {
306 0 : handleError(PIP_PKCS12_BACKUP_FAILED);
307 : }
308 0 : if (ecx)
309 0 : SEC_PKCS12DestroyExportContext(ecx);
310 0 : if (this->mTmpFile) {
311 0 : PR_Close(this->mTmpFile);
312 0 : this->mTmpFile = nullptr;
313 : }
314 0 : SECITEM_ZfreeItem(&unicodePw, false);
315 0 : return rv;
316 : }
317 :
318 : ///////////////////////////////////////////////////////////////////////
319 : //
320 : // private members
321 : //
322 : ///////////////////////////////////////////////////////////////////////
323 :
324 : // unicodeToItem
325 : //
326 : // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to
327 : // a buffer of octets. Must handle byte order correctly.
328 : nsresult
329 0 : nsPKCS12Blob::unicodeToItem(const nsString& uni, SECItem* item)
330 : {
331 0 : uint32_t len = uni.Length() + 1; // +1 for the null terminator.
332 0 : if (!SECITEM_AllocItem(nullptr, item, sizeof(char16_t) * len)) {
333 0 : return NS_ERROR_OUT_OF_MEMORY;
334 : }
335 :
336 : // We have to use a cast here because on Windows, uni.get() returns
337 : // char16ptr_t instead of char16_t*.
338 0 : mozilla::NativeEndian::copyAndSwapToBigEndian(
339 0 : item->data,
340 0 : static_cast<const char16_t*>(uni.get()),
341 0 : len);
342 :
343 0 : return NS_OK;
344 : }
345 :
346 : // newPKCS12FilePassword
347 : //
348 : // Launch a dialog requesting the user for a new PKCS#12 file passowrd.
349 : // Handle user canceled by returning null password (caller must catch).
350 : nsresult
351 0 : nsPKCS12Blob::newPKCS12FilePassword(SECItem *unicodePw)
352 : {
353 0 : nsresult rv = NS_OK;
354 0 : nsAutoString password;
355 0 : nsCOMPtr<nsICertificateDialogs> certDialogs;
356 0 : rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
357 : NS_GET_IID(nsICertificateDialogs),
358 0 : NS_CERTIFICATEDIALOGS_CONTRACTID);
359 0 : if (NS_FAILED(rv)) return rv;
360 : bool pressedOK;
361 0 : rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK);
362 0 : if (NS_FAILED(rv) || !pressedOK) return rv;
363 0 : return unicodeToItem(password, unicodePw);
364 : }
365 :
366 : // getPKCS12FilePassword
367 : //
368 : // Launch a dialog requesting the user for the password to a PKCS#12 file.
369 : // Handle user canceled by returning null password (caller must catch).
370 : nsresult
371 0 : nsPKCS12Blob::getPKCS12FilePassword(SECItem *unicodePw)
372 : {
373 0 : nsresult rv = NS_OK;
374 0 : nsAutoString password;
375 0 : nsCOMPtr<nsICertificateDialogs> certDialogs;
376 0 : rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
377 : NS_GET_IID(nsICertificateDialogs),
378 0 : NS_CERTIFICATEDIALOGS_CONTRACTID);
379 0 : if (NS_FAILED(rv)) return rv;
380 : bool pressedOK;
381 0 : rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK);
382 0 : if (NS_FAILED(rv) || !pressedOK) return rv;
383 0 : return unicodeToItem(password, unicodePw);
384 : }
385 :
386 : // inputToDecoder
387 : //
388 : // Given a decoder, read bytes from file and input them to the decoder.
389 : nsresult
390 0 : nsPKCS12Blob::inputToDecoder(SEC_PKCS12DecoderContext *dcx, nsIFile *file)
391 : {
392 0 : nsNSSShutDownPreventionLock locker;
393 : nsresult rv;
394 : SECStatus srv;
395 : uint32_t amount;
396 : char buf[PIP_PKCS12_BUFFER_SIZE];
397 :
398 0 : nsCOMPtr<nsIInputStream> fileStream;
399 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), file);
400 :
401 0 : if (NS_FAILED(rv)) {
402 0 : return rv;
403 : }
404 :
405 : while (true) {
406 0 : rv = fileStream->Read(buf, PIP_PKCS12_BUFFER_SIZE, &amount);
407 0 : if (NS_FAILED(rv)) {
408 0 : return rv;
409 : }
410 : // feed the file data into the decoder
411 0 : srv = SEC_PKCS12DecoderUpdate(dcx,
412 : (unsigned char*) buf,
413 0 : amount);
414 0 : if (srv) {
415 : // don't allow the close call to overwrite our precious error code
416 0 : int pr_err = PORT_GetError();
417 0 : PORT_SetError(pr_err);
418 0 : return NS_ERROR_ABORT;
419 : }
420 0 : if (amount < PIP_PKCS12_BUFFER_SIZE)
421 0 : break;
422 0 : }
423 0 : return NS_OK;
424 : }
425 :
426 : // nickname_collision
427 : // what to do when the nickname collides with one already in the db.
428 : // TODO: not handled, throw a dialog allowing the nick to be changed?
429 : SECItem *
430 0 : nsPKCS12Blob::nickname_collision(SECItem *oldNick, PRBool *cancel, void *wincx)
431 : {
432 0 : nsNSSShutDownPreventionLock locker;
433 0 : *cancel = false;
434 0 : int count = 1;
435 0 : nsCString nickname;
436 0 : nsAutoString nickFromProp;
437 0 : nsresult rv = GetPIPNSSBundleString("P12DefaultNickname", nickFromProp);
438 0 : if (NS_FAILED(rv)) {
439 0 : return nullptr;
440 : }
441 0 : NS_ConvertUTF16toUTF8 nickFromPropC(nickFromProp);
442 : // The user is trying to import a PKCS#12 file that doesn't have the
443 : // attribute we use to set the nickname. So in order to reduce the
444 : // number of interactions we require with the user, we'll build a nickname
445 : // for the user. The nickname isn't prominently displayed in the UI,
446 : // so it's OK if we generate one on our own here.
447 : // XXX If the NSS API were smarter and actually passed a pointer to
448 : // the CERTCertificate* we're importing we could actually just
449 : // call default_nickname (which is what the issuance code path
450 : // does) and come up with a reasonable nickname. Alas, the NSS
451 : // API limits our ability to produce a useful nickname without
452 : // bugging the user. :(
453 : while (1) {
454 : // If we've gotten this far, that means there isn't a certificate
455 : // in the database that has the same subject name as the cert we're
456 : // trying to import. So we need to come up with a "nickname" to
457 : // satisfy the NSS requirement or fail in trying to import.
458 : // Basically we use a default nickname from a properties file and
459 : // see if a certificate exists with that nickname. If there isn't, then
460 : // create update the count by one and append the string '#1' Or
461 : // whatever the count currently is, and look for a cert with
462 : // that nickname. Keep updating the count until we find a nickname
463 : // without a corresponding cert.
464 : // XXX If a user imports *many* certs without the 'friendly name'
465 : // attribute, then this may take a long time. :(
466 0 : nickname = nickFromPropC;
467 0 : if (count > 1) {
468 0 : nickname.AppendPrintf(" #%d", count);
469 : }
470 : UniqueCERTCertificate cert(CERT_FindCertByNickname(CERT_GetDefaultCertDB(),
471 0 : nickname.get()));
472 0 : if (!cert) {
473 0 : break;
474 : }
475 0 : count++;
476 0 : }
477 0 : SECItem *newNick = new SECItem;
478 0 : if (!newNick)
479 0 : return nullptr;
480 :
481 0 : newNick->type = siAsciiString;
482 0 : newNick->data = (unsigned char*) strdup(nickname.get());
483 0 : newNick->len = strlen((char*)newNick->data);
484 0 : return newNick;
485 : }
486 :
487 : // write_export_file
488 : // write bytes to the exported PKCS#12 file
489 : void
490 0 : nsPKCS12Blob::write_export_file(void *arg, const char *buf, unsigned long len)
491 : {
492 0 : nsPKCS12Blob *cx = (nsPKCS12Blob *)arg;
493 0 : PR_Write(cx->mTmpFile, buf, len);
494 0 : }
495 :
496 : // pip_ucs2_ascii_conversion_fn
497 : // required to be set by NSS (to do PKCS#12), but since we've already got
498 : // unicode make this a no-op.
499 : PRBool
500 0 : pip_ucs2_ascii_conversion_fn(PRBool toUnicode,
501 : unsigned char *inBuf,
502 : unsigned int inBufLen,
503 : unsigned char *outBuf,
504 : unsigned int maxOutBufLen,
505 : unsigned int *outBufLen,
506 : PRBool swapBytes)
507 : {
508 : // do a no-op, since I've already got unicode. Hah!
509 0 : *outBufLen = inBufLen;
510 0 : memcpy(outBuf, inBuf, inBufLen);
511 0 : return true;
512 : }
513 :
514 : void
515 0 : nsPKCS12Blob::handleError(int myerr)
516 : {
517 0 : MOZ_ASSERT(NS_IsMainThread());
518 0 : if (!NS_IsMainThread()) {
519 0 : return;
520 : }
521 :
522 0 : int prerr = PORT_GetError();
523 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PKCS12: NSS/NSPR error(%d)", prerr));
524 0 : MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PKCS12: I called(%d)", myerr));
525 :
526 0 : const char * msgID = nullptr;
527 :
528 0 : switch (myerr) {
529 : case PIP_PKCS12_USER_CANCELED:
530 0 : return; /* Just ignore it for now */
531 0 : case PIP_PKCS12_NOSMARTCARD_EXPORT: msgID = "PKCS12InfoNoSmartcardBackup"; break;
532 0 : case PIP_PKCS12_RESTORE_FAILED: msgID = "PKCS12UnknownErrRestore"; break;
533 0 : case PIP_PKCS12_BACKUP_FAILED: msgID = "PKCS12UnknownErrBackup"; break;
534 : case PIP_PKCS12_NSS_ERROR:
535 0 : switch (prerr) {
536 : // The following errors have the potential to be "handled", by asking
537 : // the user (via a dialog) whether s/he wishes to continue
538 0 : case 0: break;
539 : case SEC_ERROR_PKCS12_CERT_COLLISION:
540 : /* pop a dialog saying the cert is already in the database */
541 : /* ask to keep going? what happens if one collision but others ok? */
542 : // The following errors cannot be "handled", notify the user (via an alert)
543 : // that the operation failed.
544 0 : case SEC_ERROR_BAD_PASSWORD: msgID = "PK11BadPassword"; break;
545 :
546 : case SEC_ERROR_BAD_DER:
547 : case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
548 : case SEC_ERROR_PKCS12_INVALID_MAC:
549 0 : msgID = "PKCS12DecodeErr";
550 0 : break;
551 :
552 0 : case SEC_ERROR_PKCS12_DUPLICATE_DATA: msgID = "PKCS12DupData"; break;
553 : }
554 0 : break;
555 : }
556 :
557 0 : if (!msgID)
558 0 : msgID = "PKCS12UnknownErr";
559 :
560 0 : nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
561 0 : if (!wwatch) {
562 0 : return;
563 : }
564 0 : nsCOMPtr<nsIPrompt> prompter;
565 0 : if (NS_FAILED(wwatch->GetNewPrompter(nullptr, getter_AddRefs(prompter)))) {
566 0 : return;
567 : }
568 0 : nsAutoString message;
569 0 : if (NS_FAILED(GetPIPNSSBundleString(msgID, message))) {
570 0 : return;
571 : }
572 :
573 0 : Unused << prompter->Alert(nullptr, message.get());
574 : }
|