Back to index

lightning-sunbird  0.9+nobinonly
nsPKCS12Blob.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Ian McGreer <mcgreer@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 /* $Id: nsPKCS12Blob.cpp,v 1.42.20.3 2006/01/31 12:50:31 kaie%kuix.de Exp $ */
00038 
00039 #include "prmem.h"
00040 #include "prprf.h"
00041 
00042 #include "nsISupportsArray.h"
00043 #include "nsIFile.h"
00044 #include "nsNetUtil.h"
00045 #include "nsILocalFile.h"
00046 #include "nsIDirectoryService.h"
00047 #include "nsIWindowWatcher.h"
00048 #include "nsIPrompt.h"
00049 #include "nsProxiedService.h"
00050 
00051 #include "nsNSSComponent.h"
00052 #include "nsNSSHelper.h"
00053 #include "nsPKCS12Blob.h"
00054 #include "nsString.h"
00055 #include "nsReadableUtils.h"
00056 #include "nsXPIDLString.h"
00057 #include "nsDirectoryServiceDefs.h"
00058 #include "nsNSSHelper.h"
00059 #include "nsNSSCertificate.h"
00060 #include "nsKeygenHandler.h" //For GetSlotWithMechanism
00061 #include "nsPK11TokenDB.h"
00062 #include "nsICertificateDialogs.h"
00063 #include "nsNSSShutDown.h"
00064 #include "nsCRT.h"
00065 #include "pk11func.h"
00066 #include "secerr.h"
00067 
00068 #ifdef PR_LOGGING
00069 extern PRLogModuleInfo* gPIPNSSLog;
00070 #endif
00071 
00072 #include "nsNSSCleaner.h"
00073 NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
00074 
00075 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
00076 
00077 #define PIP_PKCS12_TMPFILENAME   NS_LITERAL_CSTRING(".pip_p12tmp")
00078 #define PIP_PKCS12_BUFFER_SIZE   2048
00079 #define PIP_PKCS12_RESTORE_OK          1
00080 #define PIP_PKCS12_BACKUP_OK           2
00081 #define PIP_PKCS12_USER_CANCELED       3
00082 #define PIP_PKCS12_NOSMARTCARD_EXPORT  4
00083 #define PIP_PKCS12_RESTORE_FAILED      5
00084 #define PIP_PKCS12_BACKUP_FAILED       6
00085 #define PIP_PKCS12_NSS_ERROR           7
00086 
00087 // constructor
00088 nsPKCS12Blob::nsPKCS12Blob():mCertArray(0),
00089                              mTmpFile(nsnull),
00090                              mTmpFilePath(nsnull),
00091                              mDigest(nsnull),
00092                              mDigestIterator(nsnull),
00093                              mTokenSet(PR_FALSE)
00094 {
00095   mUIContext = new PipUIContext();
00096 }
00097 
00098 // destructor
00099 nsPKCS12Blob::~nsPKCS12Blob()
00100 {
00101   delete mDigestIterator;
00102   delete mDigest;
00103 }
00104 
00105 // nsPKCS12Blob::SetToken
00106 //
00107 // Set the token to use for import/export
00108 nsresult
00109 nsPKCS12Blob::SetToken(nsIPK11Token *token)
00110 {
00111  nsNSSShutDownPreventionLock locker;
00112  nsresult rv = NS_OK;
00113  if (token) {
00114    mToken = token;
00115  } else {
00116    PK11SlotInfo *slot;
00117    rv = GetSlotWithMechanism(CKM_RSA_PKCS, mUIContext,&slot);
00118    if (NS_FAILED(rv)) {
00119       mToken = 0;  
00120    } else {
00121      mToken = new nsPK11Token(slot);
00122      PK11_FreeSlot(slot);
00123    }
00124  }
00125  mTokenSet = PR_TRUE;
00126  return rv;
00127 }
00128 
00129 // nsPKCS12Blob::ImportFromFile
00130 //
00131 // Given a file handle, read a PKCS#12 blob from that file, decode it,
00132 // and import the results into the token.
00133 nsresult
00134 nsPKCS12Blob::ImportFromFile(nsILocalFile *file)
00135 {
00136   nsNSSShutDownPreventionLock locker;
00137   nsresult rv = NS_OK;
00138 
00139   if (!mToken) {
00140     if (!mTokenSet) {
00141       rv = SetToken(NULL); // Ask the user to pick a slot
00142       if (NS_FAILED(rv)) {
00143         handleError(PIP_PKCS12_USER_CANCELED);
00144         return rv;
00145       }
00146     }
00147   }
00148 
00149   if (!mToken) {
00150     handleError(PIP_PKCS12_RESTORE_FAILED);
00151     return NS_ERROR_NOT_AVAILABLE;
00152   }
00153 
00154   // init slot
00155   rv = mToken->Login(PR_TRUE);
00156   if (NS_FAILED(rv)) return rv;
00157   
00158   RetryReason wantRetry;
00159   
00160   do {
00161     rv = ImportFromFileHelper(file, im_standard_prompt, wantRetry);
00162     
00163     if (NS_SUCCEEDED(rv) && wantRetry == rr_auto_retry_empty_password_flavors)
00164     {
00165       rv = ImportFromFileHelper(file, im_try_zero_length_secitem, wantRetry);
00166     }
00167   }
00168   while (NS_SUCCEEDED(rv) && (wantRetry != rr_do_not_retry));
00169   
00170   return rv;
00171 }
00172 
00173 nsresult
00174 nsPKCS12Blob::ImportFromFileHelper(nsILocalFile *file, 
00175                                    nsPKCS12Blob::ImportMode aImportMode,
00176                                    nsPKCS12Blob::RetryReason &aWantRetry)
00177 {
00178   nsNSSShutDownPreventionLock locker;
00179   nsresult rv;
00180   SECStatus srv = SECSuccess;
00181   SEC_PKCS12DecoderContext *dcx = NULL;
00182   SECItem unicodePw;
00183 
00184   PK11SlotInfo *slot=nsnull;
00185   nsXPIDLString tokenName;
00186   unicodePw.data = NULL;
00187   
00188   aWantRetry = rr_do_not_retry;
00189 
00190   if (aImportMode == im_try_zero_length_secitem)
00191   {
00192     unicodePw.len = 0;
00193   }
00194   else
00195   {
00196     // get file password (unicode)
00197     rv = getPKCS12FilePassword(&unicodePw);
00198     if (NS_FAILED(rv)) goto finish;
00199     if (unicodePw.data == NULL) {
00200       handleError(PIP_PKCS12_USER_CANCELED);
00201       return NS_OK;
00202     }
00203   }
00204   
00205   mToken->GetTokenName(getter_Copies(tokenName));
00206   {
00207     NS_ConvertUTF16toUTF8 tokenNameCString(tokenName);
00208     slot = PK11_FindSlotByName(tokenNameCString.get());
00209   }
00210   if (!slot) {
00211     srv = SECFailure;
00212     goto finish;
00213   }
00214 
00215   // initialize the decoder
00216   dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, NULL,
00217                                digest_open, digest_close,
00218                                digest_read, digest_write,
00219                                this);
00220   if (!dcx) {
00221     srv = SECFailure;
00222     goto finish;
00223   }
00224   // read input file and feed it to the decoder
00225   rv = inputToDecoder(dcx, file);
00226   if (NS_FAILED(rv)) {
00227     if (NS_ERROR_ABORT == rv) {
00228       // inputToDecoder indicated a NSS error
00229       srv = SECFailure;
00230     }
00231     goto finish;
00232   }
00233   // verify the blob
00234   srv = SEC_PKCS12DecoderVerify(dcx);
00235   if (srv) goto finish;
00236   // validate bags
00237   srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision);
00238   if (srv) goto finish;
00239   // import cert and key
00240   srv = SEC_PKCS12DecoderImportBags(dcx);
00241   if (srv) goto finish;
00242   // Later - check to see if this should become default email cert
00243   handleError(PIP_PKCS12_RESTORE_OK);
00244 finish:
00245   // If srv != SECSuccess, NSS probably set a specific error code.
00246   // We should use that error code instead of inventing a new one
00247   // for every error possible.
00248   if (srv != SECSuccess) {
00249     if (SEC_ERROR_BAD_PASSWORD == PORT_GetError()) {
00250       if (unicodePw.len == sizeof(PRUnichar))
00251       {
00252         // no password chars available, 
00253         // unicodeToItem allocated space for the trailing zero character only.
00254         aWantRetry = rr_auto_retry_empty_password_flavors;
00255       }
00256       else
00257       {
00258         aWantRetry = rr_bad_password;
00259         handleError(PIP_PKCS12_NSS_ERROR);
00260       }
00261     }
00262     else
00263     {
00264       handleError(PIP_PKCS12_NSS_ERROR);
00265     }
00266   } else if (NS_FAILED(rv)) { 
00267     handleError(PIP_PKCS12_RESTORE_FAILED);
00268   }
00269   if (slot)
00270     PK11_FreeSlot(slot);
00271   // finish the decoder
00272   if (dcx)
00273     SEC_PKCS12DecoderFinish(dcx);
00274   return NS_OK;
00275 }
00276 
00277 #if 0
00278 // nsPKCS12Blob::LoadCerts
00279 //
00280 // Given an array of certificate nicknames, load the corresponding
00281 // certificates into a local array.
00282 nsresult
00283 nsPKCS12Blob::LoadCerts(const PRUnichar **certNames, int numCerts)
00284 {
00285   nsresult rv;
00286   char namecpy[256];
00287   /* Create the local array if needed */
00288   if (!mCertArray) {
00289     rv = NS_NewISupportsArray(getter_AddRefs(mCertArray));
00290     if (NS_FAILED(rv)) {
00291       if (!handleError())
00292         return NS_ERROR_OUT_OF_MEMORY;
00293     }
00294   }
00295   /* Add the certs */
00296   for (int i=0; i<numCerts; i++) {
00297     strcpy(namecpy, NS_ConvertUCS2toUTF8(certNames[i]));
00298     CERTCertificate *nssCert = PK11_FindCertFromNickname(namecpy, NULL);
00299     if (!nssCert) {
00300       if (!handleError())
00301         return NS_ERROR_FAILURE;
00302       else continue; /* user may request to keep going */
00303     }
00304     nsCOMPtr<nsIX509Cert> cert = new nsNSSCertificate(nssCert);
00305     CERT_DestroyCertificate(nssCert);
00306     if (!cert) {
00307       if (!handleError())
00308         return NS_ERROR_OUT_OF_MEMORY;
00309     } else {
00310       mCertArray->AppendElement(cert);
00311     }
00312   }
00313   return NS_OK;
00314 }
00315 #endif
00316 
00317 static PRBool
00318 isExtractable(SECKEYPrivateKey *privKey)
00319 {
00320   SECItem value;
00321   PRBool  isExtractable = PR_FALSE;
00322   SECStatus rv;
00323 
00324   rv=PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value);
00325   if (rv != SECSuccess) {
00326     return PR_FALSE;
00327   }
00328   if ((value.len == 1) && (value.data != NULL)) {
00329     isExtractable = *(CK_BBOOL*)value.data;
00330   }
00331   SECITEM_FreeItem(&value, PR_FALSE);
00332   return isExtractable;
00333 }
00334   
00335 // nsPKCS12Blob::ExportToFile
00336 //
00337 // Having already loaded the certs, form them into a blob (loading the keys
00338 // also), encode the blob, and stuff it into the file.
00339 //
00340 // TODO: handle slots correctly
00341 //       mirror "slotToUse" behavior from PSM 1.x
00342 //       verify the cert array to start off with?
00343 //       open output file as nsIFileStream object?
00344 //       set appropriate error codes
00345 nsresult
00346 nsPKCS12Blob::ExportToFile(nsILocalFile *file, 
00347                            nsIX509Cert **certs, int numCerts)
00348 {
00349   nsNSSShutDownPreventionLock locker;
00350   nsresult rv;
00351   SECStatus srv = SECSuccess;
00352   SEC_PKCS12ExportContext *ecx = NULL;
00353   SEC_PKCS12SafeInfo *certSafe = NULL, *keySafe = NULL;
00354   SECItem unicodePw;
00355   nsAutoString filePath;
00356   int i;
00357   nsCOMPtr<nsILocalFile> localFileRef;
00358   NS_ASSERTION(mToken, "Need to set the token before exporting");
00359   // init slot
00360 
00361   PRBool InformedUserNoSmartcardBackup = PR_FALSE;
00362   int numCertsExported = 0;
00363 
00364   rv = mToken->Login(PR_TRUE);
00365   if (NS_FAILED(rv)) goto finish;
00366   // get file password (unicode)
00367   unicodePw.data = NULL;
00368   rv = newPKCS12FilePassword(&unicodePw);
00369   if (NS_FAILED(rv)) goto finish;
00370   if (unicodePw.data == NULL) {
00371     handleError(PIP_PKCS12_USER_CANCELED);
00372     return NS_OK;
00373   }
00374   // what about slotToUse in psm 1.x ???
00375   // create export context
00376   ecx = SEC_PKCS12CreateExportContext(NULL, NULL, NULL /*slot*/, NULL);
00377   if (!ecx) {
00378     srv = SECFailure;
00379     goto finish;
00380   }
00381   // add password integrity
00382   srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1);
00383   if (srv) goto finish;
00384 #if 0
00385   // count the number of certs to export
00386   nrv = mCertArray->Count(&numCerts);
00387   if (NS_FAILED(nrv)) goto finish;
00388   // loop over the certs
00389   for (i=0; i<numCerts; i++) {
00390     nsCOMPtr<nsIX509Cert> cert;
00391     nrv = mCertArray->GetElementAt(i, getter_AddRefs(cert));
00392     if (NS_FAILED(nrv)) goto finish;
00393 #endif
00394   for (i=0; i<numCerts; i++) {
00395 //    nsNSSCertificate *cert = NS_REINTREPRET_POINTER_CAST(nsNSSCertificate *,
00396 //                                                         certs[i]);
00397     nsNSSCertificate *cert = (nsNSSCertificate *)certs[i];
00398     // get it as a CERTCertificate XXX
00399     CERTCertificate *nssCert = NULL;
00400     CERTCertificateCleaner nssCertCleaner(nssCert);
00401     nssCert = cert->GetCert();
00402     if (!nssCert) {
00403       rv = NS_ERROR_FAILURE;
00404       goto finish;
00405     }
00406     // We can only successfully export certs that are on 
00407     // internal token.  Most, if not all, smart card vendors
00408     // won't let you extract the private key (in any way
00409     // shape or form) from the card.  So let's punt if 
00410     // the cert is not in the internal db.
00411     if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) {
00412       // we aren't the internal token, see if the key is extractable.
00413       SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot,
00414                                                       nssCert, this);
00415 
00416       if (privKey) {
00417         PRBool privKeyIsExtractable = isExtractable(privKey);
00418 
00419         SECKEY_DestroyPrivateKey(privKey);
00420 
00421         if (!privKeyIsExtractable) {
00422           if (!InformedUserNoSmartcardBackup) {
00423             InformedUserNoSmartcardBackup = PR_TRUE;
00424             handleError(PIP_PKCS12_NOSMARTCARD_EXPORT);
00425           }
00426           continue;
00427         }
00428       }
00429     }
00430 
00431     // XXX this is why, to verify the slot is the same
00432     // PK11_FindObjectForCert(nssCert, NULL, slot);
00433     // create the cert and key safes
00434     keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx);
00435     if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
00436       certSafe = keySafe;
00437     } else {
00438       certSafe = SEC_PKCS12CreatePasswordPrivSafe(ecx, &unicodePw,
00439                            SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
00440     }
00441     if (!certSafe || !keySafe) {
00442       rv = NS_ERROR_FAILURE;
00443       goto finish;
00444     }
00445     // add the cert and key to the blob
00446     srv = SEC_PKCS12AddCertAndKey(ecx, certSafe, NULL, nssCert,
00447                                   CERT_GetDefaultCertDB(), // XXX
00448                                   keySafe, NULL, PR_TRUE, &unicodePw,
00449                       SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC);
00450     if (srv) goto finish;
00451     // cert was dup'ed, so release it
00452     ++numCertsExported;
00453   }
00454   
00455   if (!numCertsExported) goto finish;
00456   
00457   // prepare the instance to write to an export file
00458   this->mTmpFile = NULL;
00459   file->GetPath(filePath);
00460   // Use the nsCOMPtr var localFileRef so that
00461   // the reference to the nsILocalFile we create gets released as soon as
00462   // we're out of scope, ie when this function exits.
00463   if (filePath.RFind(".p12", PR_TRUE, -1, 4) < 0) {
00464     // We're going to add the .p12 extension to the file name just like
00465     // Communicator used to.  We create a new nsILocalFile and initialize
00466     // it with the new patch.
00467     filePath.AppendLiteral(".p12");
00468     localFileRef = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
00469     if (NS_FAILED(rv)) goto finish;
00470     localFileRef->InitWithPath(filePath);
00471     file = localFileRef;
00472   }
00473   rv = file->OpenNSPRFileDesc(PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0664, 
00474                               &mTmpFile);
00475   if (NS_FAILED(rv) || !this->mTmpFile) goto finish;
00476   // encode and write
00477   srv = SEC_PKCS12Encode(ecx, write_export_file, this);
00478   if (srv) goto finish;
00479   handleError(PIP_PKCS12_BACKUP_OK);
00480 finish:
00481   if (NS_FAILED(rv) || srv != SECSuccess) {
00482     handleError(PIP_PKCS12_BACKUP_FAILED);
00483   }
00484   if (ecx)
00485     SEC_PKCS12DestroyExportContext(ecx);
00486   if (this->mTmpFile) {
00487     PR_Close(this->mTmpFile);
00488     this->mTmpFile = NULL;
00489   }
00490   return rv;
00491 }
00492 
00494 //
00495 //  private members
00496 //
00498 
00499 // unicodeToItem
00500 //
00501 // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to
00502 // a buffer of octets.  Must handle byte order correctly.
00503 // TODO: Is there a mozilla way to do this?  In the string lib?
00504 void
00505 nsPKCS12Blob::unicodeToItem(const PRUnichar *uni, SECItem *item)
00506 {
00507   int len = 0;
00508   while (uni[len++] != 0);
00509   SECITEM_AllocItem(NULL, item, sizeof(PRUnichar) * len);
00510 #ifdef IS_LITTLE_ENDIAN
00511   int i = 0;
00512   for (i=0; i<len; i++) {
00513     item->data[2*i  ] = (unsigned char )(uni[i] << 8);
00514     item->data[2*i+1] = (unsigned char )(uni[i]);
00515   }
00516 #else
00517   memcpy(item->data, uni, item->len);
00518 #endif
00519 }
00520 
00521 // newPKCS12FilePassword
00522 //
00523 // Launch a dialog requesting the user for a new PKCS#12 file passowrd.
00524 // Handle user canceled by returning null password (caller must catch).
00525 nsresult
00526 nsPKCS12Blob::newPKCS12FilePassword(SECItem *unicodePw)
00527 {
00528   nsresult rv = NS_OK;
00529   nsAutoString password;
00530   nsCOMPtr<nsICertificateDialogs> certDialogs;
00531   rv = ::getNSSDialogs(getter_AddRefs(certDialogs), 
00532                        NS_GET_IID(nsICertificateDialogs),
00533                        NS_CERTIFICATEDIALOGS_CONTRACTID);
00534   if (NS_FAILED(rv)) return rv;
00535   PRBool pressedOK;
00536   {
00537     nsPSMUITracker tracker;
00538     if (tracker.isUIForbidden()) {
00539       rv = NS_ERROR_NOT_AVAILABLE;
00540     }
00541     else {
00542       rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK);
00543     }
00544   }
00545   if (NS_FAILED(rv) || !pressedOK) return rv;
00546   unicodeToItem(password.get(), unicodePw);
00547   return NS_OK;
00548 }
00549 
00550 // getPKCS12FilePassword
00551 //
00552 // Launch a dialog requesting the user for the password to a PKCS#12 file.
00553 // Handle user canceled by returning null password (caller must catch).
00554 nsresult
00555 nsPKCS12Blob::getPKCS12FilePassword(SECItem *unicodePw)
00556 {
00557   nsresult rv = NS_OK;
00558   nsAutoString password;
00559   nsCOMPtr<nsICertificateDialogs> certDialogs;
00560   rv = ::getNSSDialogs(getter_AddRefs(certDialogs), 
00561                        NS_GET_IID(nsICertificateDialogs),
00562                        NS_CERTIFICATEDIALOGS_CONTRACTID);
00563   if (NS_FAILED(rv)) return rv;
00564   PRBool pressedOK;
00565   {
00566     nsPSMUITracker tracker;
00567     if (tracker.isUIForbidden()) {
00568       rv = NS_ERROR_NOT_AVAILABLE;
00569     }
00570     else {
00571       rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK);
00572     }
00573   }
00574   if (NS_FAILED(rv) || !pressedOK) return rv;
00575   unicodeToItem(password.get(), unicodePw);
00576   return NS_OK;
00577 }
00578 
00579 // inputToDecoder
00580 //
00581 // Given a decoder, read bytes from file and input them to the decoder.
00582 nsresult
00583 nsPKCS12Blob::inputToDecoder(SEC_PKCS12DecoderContext *dcx, nsILocalFile *file)
00584 {
00585   nsNSSShutDownPreventionLock locker;
00586   nsresult rv;
00587   SECStatus srv;
00588   PRUint32 amount;
00589   char buf[PIP_PKCS12_BUFFER_SIZE];
00590 
00591   nsCOMPtr<nsIInputStream> fileStream;
00592   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), file);
00593   
00594   if (NS_FAILED(rv)) {
00595     return rv;
00596   }
00597 
00598   while (PR_TRUE) {
00599     rv = fileStream->Read(buf, PIP_PKCS12_BUFFER_SIZE, &amount);
00600     if (NS_FAILED(rv)) {
00601       return rv;
00602     }
00603     // feed the file data into the decoder
00604     srv = SEC_PKCS12DecoderUpdate(dcx, 
00605                               (unsigned char*) buf, 
00606                               amount);
00607     if (srv) {
00608       // don't allow the close call to overwrite our precious error code
00609       int pr_err = PORT_GetError();
00610       PORT_SetError(pr_err);
00611       return NS_ERROR_ABORT;
00612     }
00613     if (amount < PIP_PKCS12_BUFFER_SIZE)
00614       break;
00615   }
00616   return NS_OK;
00617 }
00618 
00619 #ifdef XP_MAC
00620 
00621 OSErr ConvertMacPathToUnixPath(const char *macPath, char **unixPath)
00622 {
00623   PRIntn len;
00624   char *cursor;
00625   
00626   len = PL_strlen(macPath);
00627   cursor = (char*)PR_Malloc(len+2);
00628   if (!cursor)
00629     return memFullErr;
00630     
00631   memcpy(cursor+1, macPath, len+1);
00632   *unixPath = cursor;
00633   *cursor = '/';
00634   while ((cursor = PL_strchr(cursor, ':')) != NULL) {
00635     *cursor = '/';
00636     cursor++;
00637   }
00638   return noErr;
00639 }
00640 #endif
00641 
00642 //
00643 // C callback methods
00644 //
00645 
00646 // digest_open
00647 // prepare a memory buffer for reading/writing digests
00648 SECStatus PR_CALLBACK
00649 nsPKCS12Blob::digest_open(void *arg, PRBool reading)
00650 {
00651   nsPKCS12Blob *cx = NS_REINTERPRET_POINTER_CAST(nsPKCS12Blob *, arg);
00652   NS_ENSURE_TRUE(cx, SECFailure);
00653   
00654   if (reading) {
00655     NS_ENSURE_TRUE(cx->mDigest, SECFailure);
00656 
00657     delete cx->mDigestIterator;
00658     cx->mDigestIterator = new nsCString::const_iterator;
00659 
00660     if (!cx->mDigestIterator) {
00661       PORT_SetError(SEC_ERROR_NO_MEMORY);
00662       return SECFailure;
00663     }
00664 
00665     cx->mDigest->BeginReading(*cx->mDigestIterator);
00666   }
00667   else {
00668     delete cx->mDigest;
00669     cx->mDigest = new nsCString;
00670 
00671     if (!cx->mDigest) {
00672       PORT_SetError(SEC_ERROR_NO_MEMORY);
00673       return SECFailure;
00674     }
00675   }
00676 
00677   return SECSuccess;
00678 }
00679 
00680 // digest_close
00681 // destroy a possibly active iterator
00682 // remove the data buffer if requested
00683 SECStatus PR_CALLBACK
00684 nsPKCS12Blob::digest_close(void *arg, PRBool remove_it)
00685 {
00686   nsPKCS12Blob *cx = NS_REINTERPRET_POINTER_CAST(nsPKCS12Blob *, arg);
00687   NS_ENSURE_TRUE(cx, SECFailure);
00688 
00689   delete cx->mDigestIterator;
00690   cx->mDigestIterator = nsnull;
00691 
00692   if (remove_it) {  
00693     delete cx->mDigest;
00694     cx->mDigest = nsnull;
00695   }
00696   
00697   return SECSuccess;
00698 }
00699 
00700 // digest_read
00701 // read bytes from the memory buffer
00702 int PR_CALLBACK
00703 nsPKCS12Blob::digest_read(void *arg, unsigned char *buf, unsigned long len)
00704 {
00705   nsPKCS12Blob *cx = NS_REINTERPRET_POINTER_CAST(nsPKCS12Blob *, arg);
00706   NS_ENSURE_TRUE(cx, SECFailure);
00707   NS_ENSURE_TRUE(cx->mDigest, SECFailure);
00708 
00709   // iterator object must exist when digest has been opened in read mode
00710   NS_ENSURE_TRUE(cx->mDigestIterator, SECFailure);
00711 
00712   unsigned long available = cx->mDigestIterator->size_forward();
00713   
00714   if (len > available)
00715     len = available;
00716 
00717   memcpy(buf, cx->mDigestIterator->get(), len);
00718   cx->mDigestIterator->advance(len);
00719   
00720   return len;
00721 }
00722 
00723 // digest_write
00724 // append bytes to the memory buffer
00725 int PR_CALLBACK
00726 nsPKCS12Blob::digest_write(void *arg, unsigned char *buf, unsigned long len)
00727 {
00728   nsPKCS12Blob *cx = NS_REINTERPRET_POINTER_CAST(nsPKCS12Blob *, arg);
00729   NS_ENSURE_TRUE(cx, SECFailure);
00730   NS_ENSURE_TRUE(cx->mDigest, SECFailure);
00731 
00732   // make sure we are in write mode, read iterator has not yet been allocated
00733   NS_ENSURE_FALSE(cx->mDigestIterator, SECFailure);
00734   
00735   cx->mDigest->Append(NS_REINTERPRET_CAST(char *, buf),
00736                      NS_STATIC_CAST(PRUint32, len));
00737   
00738   return len;
00739 }
00740 
00741 // nickname_collision
00742 // what to do when the nickname collides with one already in the db.
00743 // TODO: not handled, throw a dialog allowing the nick to be changed?
00744 SECItem * PR_CALLBACK
00745 nsPKCS12Blob::nickname_collision(SECItem *oldNick, PRBool *cancel, void *wincx)
00746 {
00747   nsNSSShutDownPreventionLock locker;
00748   *cancel = PR_FALSE;
00749   nsresult rv;
00750   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
00751   if (NS_FAILED(rv)) return nsnull;
00752   int count = 1;
00753   nsCString nickname;
00754   nsAutoString nickFromProp;
00755   nssComponent->GetPIPNSSBundleString("P12DefaultNickname", nickFromProp);
00756   NS_ConvertUTF16toUTF8 nickFromPropC(nickFromProp);
00757   // The user is trying to import a PKCS#12 file that doesn't have the
00758   // attribute we use to set the nickname.  So in order to reduce the
00759   // number of interactions we require with the user, we'll build a nickname
00760   // for the user.  The nickname isn't prominently displayed in the UI, 
00761   // so it's OK if we generate one on our own here.
00762   //   XXX If the NSS API were smarter and actually passed a pointer to
00763   //       the CERTCertificate* we're importing we could actually just
00764   //       call default_nickname (which is what the issuance code path
00765   //       does) and come up with a reasonable nickname.  Alas, the NSS
00766   //       API limits our ability to produce a useful nickname without
00767   //       bugging the user.  :(
00768   while (1) {
00769     // If we've gotten this far, that means there isn't a certificate
00770     // in the database that has the same subject name as the cert we're
00771     // trying to import.  So we need to come up with a "nickname" to 
00772     // satisfy the NSS requirement or fail in trying to import.  
00773     // Basically we use a default nickname from a properties file and 
00774     // see if a certificate exists with that nickname.  If there isn't, then
00775     // create update the count by one and append the string '#1' Or 
00776     // whatever the count currently is, and look for a cert with 
00777     // that nickname.  Keep updating the count until we find a nickname
00778     // without a corresponding cert.
00779     //  XXX If a user imports *many* certs without the 'friendly name'
00780     //      attribute, then this may take a long time.  :(
00781     if (count > 1) {
00782       nickname.Adopt(PR_smprintf("%s #%d", nickFromPropC.get(), count));
00783     } else {
00784       nickname = nickFromPropC;
00785     }
00786     CERTCertificate *cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(),
00787                                            NS_CONST_CAST(char*,nickname.get()));
00788     if (!cert) {
00789       break;
00790     }
00791     CERT_DestroyCertificate(cert);
00792     count++;
00793   }
00794   SECItem *newNick = new SECItem;
00795   if (!newNick)
00796     return nsnull;
00797 
00798   newNick->type = siAsciiString;
00799   newNick->data = (unsigned char*) nsCRT::strdup(nickname.get());
00800   newNick->len  = strlen((char*)newNick->data);
00801   return newNick;
00802 }
00803 
00804 // write_export_file
00805 // write bytes to the exported PKCS#12 file
00806 void PR_CALLBACK
00807 nsPKCS12Blob::write_export_file(void *arg, const char *buf, unsigned long len)
00808 {
00809   nsPKCS12Blob *cx = (nsPKCS12Blob *)arg;
00810   PR_Write(cx->mTmpFile, buf, len);
00811 }
00812 
00813 // pip_ucs2_ascii_conversion_fn
00814 // required to be set by NSS (to do PKCS#12), but since we've already got
00815 // unicode make this a no-op.
00816 PRBool
00817 pip_ucs2_ascii_conversion_fn(PRBool toUnicode,
00818                              unsigned char *inBuf,
00819                              unsigned int inBufLen,
00820                              unsigned char *outBuf,
00821                              unsigned int maxOutBufLen,
00822                              unsigned int *outBufLen,
00823                              PRBool swapBytes)
00824 {
00825   // do a no-op, since I've already got unicode.  Hah!
00826   *outBufLen = inBufLen;
00827   memcpy(outBuf, inBuf, inBufLen);
00828   return PR_TRUE;
00829 }
00830 
00831 PRBool
00832 nsPKCS12Blob::handleError(int myerr)
00833 {
00834   nsPSMUITracker tracker;
00835   if (tracker.isUIForbidden()) {
00836     return PR_FALSE;
00837   }
00838 
00839   nsresult rv;
00840   PRBool keepGoing = PR_FALSE;
00841   int prerr = PORT_GetError();
00842   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("PKCS12: NSS/NSPR error(%d)", prerr));
00843   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("PKCS12: I called(%d)", myerr));
00844   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
00845   if (NS_FAILED(rv)) return PR_FALSE;
00846   nsCOMPtr<nsIProxyObjectManager> proxyman(
00847                                       do_GetService(NS_XPCOMPROXY_CONTRACTID));
00848   if (!proxyman) return PR_FALSE;
00849   nsCOMPtr<nsIPrompt> errPrompt;
00850   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
00851   if (wwatch) {
00852     wwatch->GetNewPrompter(0, getter_AddRefs(errPrompt));
00853     if (errPrompt) {
00854       nsCOMPtr<nsIPrompt> proxyPrompt;
00855       proxyman->GetProxyForObject(NS_UI_THREAD_EVENTQ, NS_GET_IID(nsIPrompt),
00856                                   errPrompt, PROXY_SYNC, 
00857                                   getter_AddRefs(proxyPrompt));
00858       if (!proxyPrompt) return PR_FALSE;
00859     } else {
00860       return PR_FALSE;
00861     }
00862   } else {
00863     return PR_FALSE;
00864   }
00865   nsAutoString errorMsg;
00866   switch (myerr) {
00867   case PIP_PKCS12_RESTORE_OK:
00868     rv = nssComponent->GetPIPNSSBundleString("SuccessfulP12Restore", errorMsg);
00869     if (NS_FAILED(rv)) return rv;
00870     errPrompt->Alert(nsnull, errorMsg.get());
00871     return PR_TRUE;
00872   case PIP_PKCS12_BACKUP_OK:
00873     rv = nssComponent->GetPIPNSSBundleString("SuccessfulP12Backup", errorMsg);
00874     if (NS_FAILED(rv)) return rv;
00875     errPrompt->Alert(nsnull, errorMsg.get());
00876     return PR_TRUE;
00877   case PIP_PKCS12_USER_CANCELED:
00878     return PR_TRUE;  /* Just ignore it for now */
00879   case PIP_PKCS12_NOSMARTCARD_EXPORT:
00880     rv = nssComponent->GetPIPNSSBundleString("PKCS12InfoNoSmartcardBackup", errorMsg);
00881     if (NS_FAILED(rv)) return rv;
00882     errPrompt->Alert(nsnull, errorMsg.get());
00883     return PR_TRUE;
00884   case PIP_PKCS12_RESTORE_FAILED:
00885     rv = nssComponent->GetPIPNSSBundleString("PKCS12UnknownErrRestore", errorMsg);
00886     if (NS_FAILED(rv)) return rv;
00887     errPrompt->Alert(nsnull, errorMsg.get());
00888     return PR_TRUE;
00889   case PIP_PKCS12_BACKUP_FAILED:
00890     rv = nssComponent->GetPIPNSSBundleString("PKCS12UnknownErrBackup", errorMsg);
00891     if (NS_FAILED(rv)) return rv;
00892     errPrompt->Alert(nsnull, errorMsg.get());
00893     return PR_TRUE;
00894   case PIP_PKCS12_NSS_ERROR:
00895     switch (prerr) {
00896     // The following errors have the potential to be "handled", by asking
00897     // the user (via a dialog) whether s/he wishes to continue
00898     case 0: break;
00899     case SEC_ERROR_PKCS12_CERT_COLLISION:
00900       /* pop a dialog saying the cert is already in the database */
00901       /* ask to keep going?  what happens if one collision but others ok? */
00902       // The following errors cannot be "handled", notify the user (via an alert)
00903       // that the operation failed.
00904 #if 0
00905       // XXX a boy can dream...
00906       //     but the PKCS12 lib never throws this error
00907       //     but then again, how would it?  anyway, convey the info below
00908     case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT:
00909       rv = nssComponent->GetPIPNSSBundleString("PKCS12PasswordInvalid", errorMsg);
00910       if (NS_FAILED(rv)) return rv;
00911       errPrompt->Alert(nsnull, errorMsg.get());
00912     break;
00913 #endif
00914     case SEC_ERROR_BAD_PASSWORD:
00915       rv = nssComponent->GetPIPNSSBundleString("PK11BadPassword", errorMsg);
00916       if (NS_FAILED(rv)) return rv;
00917       errPrompt->Alert(nsnull, errorMsg.get());
00918       break;
00919     case SEC_ERROR_BAD_DER:
00920     case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
00921     case SEC_ERROR_PKCS12_INVALID_MAC:
00922       rv = nssComponent->GetPIPNSSBundleString("PKCS12DecodeErr", errorMsg);
00923       if (NS_FAILED(rv)) return rv;
00924       errPrompt->Alert(nsnull, errorMsg.get());
00925       break;
00926     case SEC_ERROR_PKCS12_DUPLICATE_DATA:
00927       rv = nssComponent->GetPIPNSSBundleString("PKCS12DupData", errorMsg);
00928       if (NS_FAILED(rv)) return rv;
00929       errPrompt->Alert(nsnull, errorMsg.get());
00930       break;
00931     default:
00932       rv = nssComponent->GetPIPNSSBundleString("PKCS12UnknownErr", errorMsg);
00933       if (NS_FAILED(rv)) return rv;
00934       errPrompt->Alert(nsnull, errorMsg.get());
00935     }
00936     break;
00937   case 0: 
00938   default:
00939     rv = nssComponent->GetPIPNSSBundleString("PKCS12UnknownErr", errorMsg);
00940     if (NS_FAILED(rv)) return rv;
00941     errPrompt->Alert(nsnull, errorMsg.get());
00942     break;
00943   }
00944   if (NS_FAILED(rv)) return rv;
00945   return keepGoing;
00946 }
00947