security/manager/ssl/src/nsPKCS12Blob.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:f71660918738
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 /* $Id: nsPKCS12Blob.cpp,v 1.49 2007/09/05 07:13:46 jwalden%mit.edu Exp $ */
5
6 #include "nsPKCS12Blob.h"
7
8 #include "pkix/pkixtypes.h"
9
10 #include "prmem.h"
11 #include "prprf.h"
12
13 #include "nsIFile.h"
14 #include "nsNetUtil.h"
15 #include "nsIDirectoryService.h"
16 #include "nsThreadUtils.h"
17
18 #include "nsNSSComponent.h"
19 #include "nsNSSHelper.h"
20 #include "nsString.h"
21 #include "nsReadableUtils.h"
22 #include "nsXPIDLString.h"
23 #include "nsDirectoryServiceDefs.h"
24 #include "nsNSSHelper.h"
25 #include "nsNSSCertificate.h"
26 #include "nsKeygenHandler.h" //For GetSlotWithMechanism
27 #include "nsPK11TokenDB.h"
28 #include "nsICertificateDialogs.h"
29 #include "nsNSSShutDown.h"
30 #include "nsCRT.h"
31
32 #include "secerr.h"
33
34 #ifdef PR_LOGGING
35 extern PRLogModuleInfo* gPIPNSSLog;
36 #endif
37
38 using namespace mozilla;
39
40 #define PIP_PKCS12_TMPFILENAME NS_LITERAL_CSTRING(".pip_p12tmp")
41 #define PIP_PKCS12_BUFFER_SIZE 2048
42 #define PIP_PKCS12_RESTORE_OK 1
43 #define PIP_PKCS12_BACKUP_OK 2
44 #define PIP_PKCS12_USER_CANCELED 3
45 #define PIP_PKCS12_NOSMARTCARD_EXPORT 4
46 #define PIP_PKCS12_RESTORE_FAILED 5
47 #define PIP_PKCS12_BACKUP_FAILED 6
48 #define PIP_PKCS12_NSS_ERROR 7
49
50 // constructor
51 nsPKCS12Blob::nsPKCS12Blob():mCertArray(0),
52 mTmpFile(nullptr),
53 mDigest(nullptr),
54 mDigestIterator(nullptr),
55 mTokenSet(false)
56 {
57 mUIContext = new PipUIContext();
58 }
59
60 // destructor
61 nsPKCS12Blob::~nsPKCS12Blob()
62 {
63 delete mDigestIterator;
64 delete mDigest;
65 }
66
67 // nsPKCS12Blob::SetToken
68 //
69 // Set the token to use for import/export
70 nsresult
71 nsPKCS12Blob::SetToken(nsIPK11Token *token)
72 {
73 nsNSSShutDownPreventionLock locker;
74 nsresult rv = NS_OK;
75 if (token) {
76 mToken = token;
77 } else {
78 PK11SlotInfo *slot;
79 rv = GetSlotWithMechanism(CKM_RSA_PKCS, mUIContext,&slot);
80 if (NS_FAILED(rv)) {
81 mToken = 0;
82 } else {
83 mToken = new nsPK11Token(slot);
84 PK11_FreeSlot(slot);
85 }
86 }
87 mTokenSet = true;
88 return rv;
89 }
90
91 // nsPKCS12Blob::ImportFromFile
92 //
93 // Given a file handle, read a PKCS#12 blob from that file, decode it,
94 // and import the results into the token.
95 nsresult
96 nsPKCS12Blob::ImportFromFile(nsIFile *file)
97 {
98 nsNSSShutDownPreventionLock locker;
99 nsresult rv = NS_OK;
100
101 if (!mToken) {
102 if (!mTokenSet) {
103 rv = SetToken(nullptr); // Ask the user to pick a slot
104 if (NS_FAILED(rv)) {
105 handleError(PIP_PKCS12_USER_CANCELED);
106 return rv;
107 }
108 }
109 }
110
111 if (!mToken) {
112 handleError(PIP_PKCS12_RESTORE_FAILED);
113 return NS_ERROR_NOT_AVAILABLE;
114 }
115
116 // init slot
117 rv = mToken->Login(true);
118 if (NS_FAILED(rv)) return rv;
119
120 RetryReason wantRetry;
121
122 do {
123 rv = ImportFromFileHelper(file, im_standard_prompt, wantRetry);
124
125 if (NS_SUCCEEDED(rv) && wantRetry == rr_auto_retry_empty_password_flavors)
126 {
127 rv = ImportFromFileHelper(file, im_try_zero_length_secitem, wantRetry);
128 }
129 }
130 while (NS_SUCCEEDED(rv) && (wantRetry != rr_do_not_retry));
131
132 return rv;
133 }
134
135 nsresult
136 nsPKCS12Blob::ImportFromFileHelper(nsIFile *file,
137 nsPKCS12Blob::ImportMode aImportMode,
138 nsPKCS12Blob::RetryReason &aWantRetry)
139 {
140 nsNSSShutDownPreventionLock locker;
141 nsresult rv = NS_OK;
142 SECStatus srv = SECSuccess;
143 SEC_PKCS12DecoderContext *dcx = nullptr;
144 SECItem unicodePw;
145
146 PK11SlotInfo *slot=nullptr;
147 nsXPIDLString tokenName;
148 unicodePw.data = nullptr;
149
150 aWantRetry = rr_do_not_retry;
151
152 if (aImportMode == im_try_zero_length_secitem)
153 {
154 unicodePw.len = 0;
155 }
156 else
157 {
158 // get file password (unicode)
159 rv = getPKCS12FilePassword(&unicodePw);
160 if (NS_FAILED(rv)) goto finish;
161 if (!unicodePw.data) {
162 handleError(PIP_PKCS12_USER_CANCELED);
163 return NS_OK;
164 }
165 }
166
167 mToken->GetTokenName(getter_Copies(tokenName));
168 {
169 NS_ConvertUTF16toUTF8 tokenNameCString(tokenName);
170 slot = PK11_FindSlotByName(tokenNameCString.get());
171 }
172 if (!slot) {
173 srv = SECFailure;
174 goto finish;
175 }
176
177 // initialize the decoder
178 dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, nullptr,
179 digest_open, digest_close,
180 digest_read, digest_write,
181 this);
182 if (!dcx) {
183 srv = SECFailure;
184 goto finish;
185 }
186 // read input file and feed it to the decoder
187 rv = inputToDecoder(dcx, file);
188 if (NS_FAILED(rv)) {
189 if (NS_ERROR_ABORT == rv) {
190 // inputToDecoder indicated a NSS error
191 srv = SECFailure;
192 }
193 goto finish;
194 }
195 // verify the blob
196 srv = SEC_PKCS12DecoderVerify(dcx);
197 if (srv) goto finish;
198 // validate bags
199 srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision);
200 if (srv) goto finish;
201 // import cert and key
202 srv = SEC_PKCS12DecoderImportBags(dcx);
203 if (srv) goto finish;
204 // Later - check to see if this should become default email cert
205 handleError(PIP_PKCS12_RESTORE_OK);
206 finish:
207 // If srv != SECSuccess, NSS probably set a specific error code.
208 // We should use that error code instead of inventing a new one
209 // for every error possible.
210 if (srv != SECSuccess) {
211 if (SEC_ERROR_BAD_PASSWORD == PORT_GetError()) {
212 if (unicodePw.len == sizeof(char16_t))
213 {
214 // no password chars available,
215 // unicodeToItem allocated space for the trailing zero character only.
216 aWantRetry = rr_auto_retry_empty_password_flavors;
217 }
218 else
219 {
220 aWantRetry = rr_bad_password;
221 handleError(PIP_PKCS12_NSS_ERROR);
222 }
223 }
224 else
225 {
226 handleError(PIP_PKCS12_NSS_ERROR);
227 }
228 } else if (NS_FAILED(rv)) {
229 handleError(PIP_PKCS12_RESTORE_FAILED);
230 }
231 if (slot)
232 PK11_FreeSlot(slot);
233 // finish the decoder
234 if (dcx)
235 SEC_PKCS12DecoderFinish(dcx);
236 SECITEM_ZfreeItem(&unicodePw, false);
237 return NS_OK;
238 }
239
240 static bool
241 isExtractable(SECKEYPrivateKey *privKey)
242 {
243 SECItem value;
244 bool isExtractable = false;
245 SECStatus rv;
246
247 rv=PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value);
248 if (rv != SECSuccess) {
249 return false;
250 }
251 if ((value.len == 1) && value.data) {
252 isExtractable = !!(*(CK_BBOOL*)value.data);
253 }
254 SECITEM_FreeItem(&value, false);
255 return isExtractable;
256 }
257
258 // nsPKCS12Blob::ExportToFile
259 //
260 // Having already loaded the certs, form them into a blob (loading the keys
261 // also), encode the blob, and stuff it into the file.
262 //
263 // TODO: handle slots correctly
264 // mirror "slotToUse" behavior from PSM 1.x
265 // verify the cert array to start off with?
266 // open output file as nsIFileStream object?
267 // set appropriate error codes
268 nsresult
269 nsPKCS12Blob::ExportToFile(nsIFile *file,
270 nsIX509Cert **certs, int numCerts)
271 {
272 nsNSSShutDownPreventionLock locker;
273 nsresult rv;
274 SECStatus srv = SECSuccess;
275 SEC_PKCS12ExportContext *ecx = nullptr;
276 SEC_PKCS12SafeInfo *certSafe = nullptr, *keySafe = nullptr;
277 SECItem unicodePw;
278 nsAutoString filePath;
279 int i;
280 nsCOMPtr<nsIFile> localFileRef;
281 NS_ASSERTION(mToken, "Need to set the token before exporting");
282 // init slot
283
284 bool InformedUserNoSmartcardBackup = false;
285 int numCertsExported = 0;
286
287 rv = mToken->Login(true);
288 if (NS_FAILED(rv)) goto finish;
289 // get file password (unicode)
290 unicodePw.data = nullptr;
291 rv = newPKCS12FilePassword(&unicodePw);
292 if (NS_FAILED(rv)) goto finish;
293 if (!unicodePw.data) {
294 handleError(PIP_PKCS12_USER_CANCELED);
295 return NS_OK;
296 }
297 // what about slotToUse in psm 1.x ???
298 // create export context
299 ecx = SEC_PKCS12CreateExportContext(nullptr, nullptr, nullptr /*slot*/, nullptr);
300 if (!ecx) {
301 srv = SECFailure;
302 goto finish;
303 }
304 // add password integrity
305 srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1);
306 if (srv) goto finish;
307 for (i=0; i<numCerts; i++) {
308 nsNSSCertificate *cert = (nsNSSCertificate *)certs[i];
309 // get it as a CERTCertificate XXX
310 mozilla::pkix::ScopedCERTCertificate nssCert(cert->GetCert());
311 if (!nssCert) {
312 rv = NS_ERROR_FAILURE;
313 goto finish;
314 }
315 // We can only successfully export certs that are on
316 // internal token. Most, if not all, smart card vendors
317 // won't let you extract the private key (in any way
318 // shape or form) from the card. So let's punt if
319 // the cert is not in the internal db.
320 if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) {
321 // we aren't the internal token, see if the key is extractable.
322 SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot,
323 nssCert.get(), this);
324
325 if (privKey) {
326 bool privKeyIsExtractable = isExtractable(privKey);
327
328 SECKEY_DestroyPrivateKey(privKey);
329
330 if (!privKeyIsExtractable) {
331 if (!InformedUserNoSmartcardBackup) {
332 InformedUserNoSmartcardBackup = true;
333 handleError(PIP_PKCS12_NOSMARTCARD_EXPORT);
334 }
335 continue;
336 }
337 }
338 }
339
340 // XXX this is why, to verify the slot is the same
341 // PK11_FindObjectForCert(nssCert, nullptr, slot);
342 // create the cert and key safes
343 keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx);
344 if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
345 certSafe = keySafe;
346 } else {
347 certSafe = SEC_PKCS12CreatePasswordPrivSafe(ecx, &unicodePw,
348 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
349 }
350 if (!certSafe || !keySafe) {
351 rv = NS_ERROR_FAILURE;
352 goto finish;
353 }
354 // add the cert and key to the blob
355 srv = SEC_PKCS12AddCertAndKey(ecx, certSafe, nullptr, nssCert.get(),
356 CERT_GetDefaultCertDB(), // XXX
357 keySafe, nullptr, true, &unicodePw,
358 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC);
359 if (srv) goto finish;
360 // cert was dup'ed, so release it
361 ++numCertsExported;
362 }
363
364 if (!numCertsExported) goto finish;
365
366 // prepare the instance to write to an export file
367 this->mTmpFile = nullptr;
368 file->GetPath(filePath);
369 // Use the nsCOMPtr var localFileRef so that
370 // the reference to the nsIFile we create gets released as soon as
371 // we're out of scope, ie when this function exits.
372 if (filePath.RFind(".p12", true, -1, 4) < 0) {
373 // We're going to add the .p12 extension to the file name just like
374 // Communicator used to. We create a new nsIFile and initialize
375 // it with the new patch.
376 filePath.AppendLiteral(".p12");
377 localFileRef = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
378 if (NS_FAILED(rv)) goto finish;
379 localFileRef->InitWithPath(filePath);
380 file = localFileRef;
381 }
382 rv = file->OpenNSPRFileDesc(PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0664,
383 &mTmpFile);
384 if (NS_FAILED(rv) || !this->mTmpFile) goto finish;
385 // encode and write
386 srv = SEC_PKCS12Encode(ecx, write_export_file, this);
387 if (srv) goto finish;
388 handleError(PIP_PKCS12_BACKUP_OK);
389 finish:
390 if (NS_FAILED(rv) || srv != SECSuccess) {
391 handleError(PIP_PKCS12_BACKUP_FAILED);
392 }
393 if (ecx)
394 SEC_PKCS12DestroyExportContext(ecx);
395 if (this->mTmpFile) {
396 PR_Close(this->mTmpFile);
397 this->mTmpFile = nullptr;
398 }
399 SECITEM_ZfreeItem(&unicodePw, false);
400 return rv;
401 }
402
403 ///////////////////////////////////////////////////////////////////////
404 //
405 // private members
406 //
407 ///////////////////////////////////////////////////////////////////////
408
409 // unicodeToItem
410 //
411 // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to
412 // a buffer of octets. Must handle byte order correctly.
413 // TODO: Is there a mozilla way to do this? In the string lib?
414 void
415 nsPKCS12Blob::unicodeToItem(const char16_t *uni, SECItem *item)
416 {
417 int len = 0;
418 while (uni[len++] != 0);
419 SECITEM_AllocItem(nullptr, item, sizeof(char16_t) * len);
420 #ifdef IS_LITTLE_ENDIAN
421 int i = 0;
422 for (i=0; i<len; i++) {
423 item->data[2*i ] = (unsigned char )(uni[i] << 8);
424 item->data[2*i+1] = (unsigned char )(uni[i]);
425 }
426 #else
427 memcpy(item->data, uni, item->len);
428 #endif
429 }
430
431 // newPKCS12FilePassword
432 //
433 // Launch a dialog requesting the user for a new PKCS#12 file passowrd.
434 // Handle user canceled by returning null password (caller must catch).
435 nsresult
436 nsPKCS12Blob::newPKCS12FilePassword(SECItem *unicodePw)
437 {
438 nsresult rv = NS_OK;
439 nsAutoString password;
440 nsCOMPtr<nsICertificateDialogs> certDialogs;
441 rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
442 NS_GET_IID(nsICertificateDialogs),
443 NS_CERTIFICATEDIALOGS_CONTRACTID);
444 if (NS_FAILED(rv)) return rv;
445 bool pressedOK;
446 {
447 nsPSMUITracker tracker;
448 if (tracker.isUIForbidden()) {
449 rv = NS_ERROR_NOT_AVAILABLE;
450 }
451 else {
452 rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK);
453 }
454 }
455 if (NS_FAILED(rv) || !pressedOK) return rv;
456 unicodeToItem(password.get(), unicodePw);
457 return NS_OK;
458 }
459
460 // getPKCS12FilePassword
461 //
462 // Launch a dialog requesting the user for the password to a PKCS#12 file.
463 // Handle user canceled by returning null password (caller must catch).
464 nsresult
465 nsPKCS12Blob::getPKCS12FilePassword(SECItem *unicodePw)
466 {
467 nsresult rv = NS_OK;
468 nsAutoString password;
469 nsCOMPtr<nsICertificateDialogs> certDialogs;
470 rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
471 NS_GET_IID(nsICertificateDialogs),
472 NS_CERTIFICATEDIALOGS_CONTRACTID);
473 if (NS_FAILED(rv)) return rv;
474 bool pressedOK;
475 {
476 nsPSMUITracker tracker;
477 if (tracker.isUIForbidden()) {
478 rv = NS_ERROR_NOT_AVAILABLE;
479 }
480 else {
481 rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK);
482 }
483 }
484 if (NS_FAILED(rv) || !pressedOK) return rv;
485 unicodeToItem(password.get(), unicodePw);
486 return NS_OK;
487 }
488
489 // inputToDecoder
490 //
491 // Given a decoder, read bytes from file and input them to the decoder.
492 nsresult
493 nsPKCS12Blob::inputToDecoder(SEC_PKCS12DecoderContext *dcx, nsIFile *file)
494 {
495 nsNSSShutDownPreventionLock locker;
496 nsresult rv;
497 SECStatus srv;
498 uint32_t amount;
499 char buf[PIP_PKCS12_BUFFER_SIZE];
500
501 nsCOMPtr<nsIInputStream> fileStream;
502 rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), file);
503
504 if (NS_FAILED(rv)) {
505 return rv;
506 }
507
508 while (true) {
509 rv = fileStream->Read(buf, PIP_PKCS12_BUFFER_SIZE, &amount);
510 if (NS_FAILED(rv)) {
511 return rv;
512 }
513 // feed the file data into the decoder
514 srv = SEC_PKCS12DecoderUpdate(dcx,
515 (unsigned char*) buf,
516 amount);
517 if (srv) {
518 // don't allow the close call to overwrite our precious error code
519 int pr_err = PORT_GetError();
520 PORT_SetError(pr_err);
521 return NS_ERROR_ABORT;
522 }
523 if (amount < PIP_PKCS12_BUFFER_SIZE)
524 break;
525 }
526 return NS_OK;
527 }
528
529 //
530 // C callback methods
531 //
532
533 // digest_open
534 // prepare a memory buffer for reading/writing digests
535 SECStatus
536 nsPKCS12Blob::digest_open(void *arg, PRBool reading)
537 {
538 nsPKCS12Blob *cx = reinterpret_cast<nsPKCS12Blob *>(arg);
539 NS_ENSURE_TRUE(cx, SECFailure);
540
541 if (reading) {
542 NS_ENSURE_TRUE(cx->mDigest, SECFailure);
543
544 delete cx->mDigestIterator;
545 cx->mDigestIterator = new nsCString::const_iterator;
546
547 if (!cx->mDigestIterator) {
548 PORT_SetError(SEC_ERROR_NO_MEMORY);
549 return SECFailure;
550 }
551
552 cx->mDigest->BeginReading(*cx->mDigestIterator);
553 }
554 else {
555 delete cx->mDigest;
556 cx->mDigest = new nsCString;
557
558 if (!cx->mDigest) {
559 PORT_SetError(SEC_ERROR_NO_MEMORY);
560 return SECFailure;
561 }
562 }
563
564 return SECSuccess;
565 }
566
567 // digest_close
568 // destroy a possibly active iterator
569 // remove the data buffer if requested
570 SECStatus
571 nsPKCS12Blob::digest_close(void *arg, PRBool remove_it)
572 {
573 nsPKCS12Blob *cx = reinterpret_cast<nsPKCS12Blob *>(arg);
574 NS_ENSURE_TRUE(cx, SECFailure);
575
576 delete cx->mDigestIterator;
577 cx->mDigestIterator = nullptr;
578
579 if (remove_it) {
580 delete cx->mDigest;
581 cx->mDigest = nullptr;
582 }
583
584 return SECSuccess;
585 }
586
587 // digest_read
588 // read bytes from the memory buffer
589 int
590 nsPKCS12Blob::digest_read(void *arg, unsigned char *buf, unsigned long len)
591 {
592 nsPKCS12Blob *cx = reinterpret_cast<nsPKCS12Blob *>(arg);
593 NS_ENSURE_TRUE(cx, SECFailure);
594 NS_ENSURE_TRUE(cx->mDigest, SECFailure);
595
596 // iterator object must exist when digest has been opened in read mode
597 NS_ENSURE_TRUE(cx->mDigestIterator, SECFailure);
598
599 unsigned long available = cx->mDigestIterator->size_forward();
600
601 if (len > available)
602 len = available;
603
604 memcpy(buf, cx->mDigestIterator->get(), len);
605 cx->mDigestIterator->advance(len);
606
607 return len;
608 }
609
610 // digest_write
611 // append bytes to the memory buffer
612 int
613 nsPKCS12Blob::digest_write(void *arg, unsigned char *buf, unsigned long len)
614 {
615 nsPKCS12Blob *cx = reinterpret_cast<nsPKCS12Blob *>(arg);
616 NS_ENSURE_TRUE(cx, SECFailure);
617 NS_ENSURE_TRUE(cx->mDigest, SECFailure);
618
619 // make sure we are in write mode, read iterator has not yet been allocated
620 NS_ENSURE_FALSE(cx->mDigestIterator, SECFailure);
621
622 cx->mDigest->Append(reinterpret_cast<char *>(buf),
623 static_cast<uint32_t>(len));
624
625 return len;
626 }
627
628 // nickname_collision
629 // what to do when the nickname collides with one already in the db.
630 // TODO: not handled, throw a dialog allowing the nick to be changed?
631 SECItem *
632 nsPKCS12Blob::nickname_collision(SECItem *oldNick, PRBool *cancel, void *wincx)
633 {
634 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
635
636 nsNSSShutDownPreventionLock locker;
637 *cancel = false;
638 nsresult rv;
639 nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
640 if (NS_FAILED(rv)) return nullptr;
641 int count = 1;
642 nsCString nickname;
643 nsAutoString nickFromProp;
644 nssComponent->GetPIPNSSBundleString("P12DefaultNickname", nickFromProp);
645 NS_ConvertUTF16toUTF8 nickFromPropC(nickFromProp);
646 // The user is trying to import a PKCS#12 file that doesn't have the
647 // attribute we use to set the nickname. So in order to reduce the
648 // number of interactions we require with the user, we'll build a nickname
649 // for the user. The nickname isn't prominently displayed in the UI,
650 // so it's OK if we generate one on our own here.
651 // XXX If the NSS API were smarter and actually passed a pointer to
652 // the CERTCertificate* we're importing we could actually just
653 // call default_nickname (which is what the issuance code path
654 // does) and come up with a reasonable nickname. Alas, the NSS
655 // API limits our ability to produce a useful nickname without
656 // bugging the user. :(
657 while (1) {
658 // If we've gotten this far, that means there isn't a certificate
659 // in the database that has the same subject name as the cert we're
660 // trying to import. So we need to come up with a "nickname" to
661 // satisfy the NSS requirement or fail in trying to import.
662 // Basically we use a default nickname from a properties file and
663 // see if a certificate exists with that nickname. If there isn't, then
664 // create update the count by one and append the string '#1' Or
665 // whatever the count currently is, and look for a cert with
666 // that nickname. Keep updating the count until we find a nickname
667 // without a corresponding cert.
668 // XXX If a user imports *many* certs without the 'friendly name'
669 // attribute, then this may take a long time. :(
670 if (count > 1) {
671 nickname.Adopt(PR_smprintf("%s #%d", nickFromPropC.get(), count));
672 } else {
673 nickname = nickFromPropC;
674 }
675 CERTCertificate *cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(),
676 const_cast<char*>(nickname.get()));
677 if (!cert) {
678 break;
679 }
680 CERT_DestroyCertificate(cert);
681 count++;
682 }
683 SECItem *newNick = new SECItem;
684 if (!newNick)
685 return nullptr;
686
687 newNick->type = siAsciiString;
688 newNick->data = (unsigned char*) strdup(nickname.get());
689 newNick->len = strlen((char*)newNick->data);
690 return newNick;
691 }
692
693 // write_export_file
694 // write bytes to the exported PKCS#12 file
695 void
696 nsPKCS12Blob::write_export_file(void *arg, const char *buf, unsigned long len)
697 {
698 nsPKCS12Blob *cx = (nsPKCS12Blob *)arg;
699 PR_Write(cx->mTmpFile, buf, len);
700 }
701
702 // pip_ucs2_ascii_conversion_fn
703 // required to be set by NSS (to do PKCS#12), but since we've already got
704 // unicode make this a no-op.
705 PRBool
706 pip_ucs2_ascii_conversion_fn(PRBool toUnicode,
707 unsigned char *inBuf,
708 unsigned int inBufLen,
709 unsigned char *outBuf,
710 unsigned int maxOutBufLen,
711 unsigned int *outBufLen,
712 PRBool swapBytes)
713 {
714 // do a no-op, since I've already got unicode. Hah!
715 *outBufLen = inBufLen;
716 memcpy(outBuf, inBuf, inBufLen);
717 return true;
718 }
719
720 void
721 nsPKCS12Blob::handleError(int myerr)
722 {
723 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
724
725 if (!NS_IsMainThread()) {
726 NS_ERROR("nsPKCS12Blob::handleError called off the mai nthread.");
727 return;
728 }
729
730 int prerr = PORT_GetError();
731 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("PKCS12: NSS/NSPR error(%d)", prerr));
732 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("PKCS12: I called(%d)", myerr));
733
734 const char * msgID = nullptr;
735
736 switch (myerr) {
737 case PIP_PKCS12_RESTORE_OK: msgID = "SuccessfulP12Restore"; break;
738 case PIP_PKCS12_BACKUP_OK: msgID = "SuccessfulP12Backup"; break;
739 case PIP_PKCS12_USER_CANCELED:
740 return; /* Just ignore it for now */
741 case PIP_PKCS12_NOSMARTCARD_EXPORT: msgID = "PKCS12InfoNoSmartcardBackup"; break;
742 case PIP_PKCS12_RESTORE_FAILED: msgID = "PKCS12UnknownErrRestore"; break;
743 case PIP_PKCS12_BACKUP_FAILED: msgID = "PKCS12UnknownErrBackup"; break;
744 case PIP_PKCS12_NSS_ERROR:
745 switch (prerr) {
746 // The following errors have the potential to be "handled", by asking
747 // the user (via a dialog) whether s/he wishes to continue
748 case 0: break;
749 case SEC_ERROR_PKCS12_CERT_COLLISION:
750 /* pop a dialog saying the cert is already in the database */
751 /* ask to keep going? what happens if one collision but others ok? */
752 // The following errors cannot be "handled", notify the user (via an alert)
753 // that the operation failed.
754 case SEC_ERROR_BAD_PASSWORD: msgID = "PK11BadPassword"; break;
755
756 case SEC_ERROR_BAD_DER:
757 case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
758 case SEC_ERROR_PKCS12_INVALID_MAC:
759 msgID = "PKCS12DecodeErr";
760 break;
761
762 case SEC_ERROR_PKCS12_DUPLICATE_DATA: msgID = "PKCS12DupData"; break;
763 }
764 break;
765 }
766
767 if (!msgID)
768 msgID = "PKCS12UnknownErr";
769
770 nsresult rv;
771 nsCOMPtr<nsINSSComponent> nssComponent = do_GetService(kNSSComponentCID, &rv);
772 if (NS_SUCCEEDED(rv))
773 (void) nssComponent->ShowAlertFromStringBundle(msgID);
774 }

mercurial