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