security/manager/ssl/src/nsPKCS12Blob.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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 $ */
     6 #include "nsPKCS12Blob.h"
     8 #include "pkix/pkixtypes.h"
    10 #include "prmem.h"
    11 #include "prprf.h"
    13 #include "nsIFile.h"
    14 #include "nsNetUtil.h"
    15 #include "nsIDirectoryService.h"
    16 #include "nsThreadUtils.h"
    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"
    32 #include "secerr.h"
    34 #ifdef PR_LOGGING
    35 extern PRLogModuleInfo* gPIPNSSLog;
    36 #endif
    38 using namespace mozilla;
    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
    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 }
    60 // destructor
    61 nsPKCS12Blob::~nsPKCS12Blob()
    62 {
    63   delete mDigestIterator;
    64   delete mDigest;
    65 }
    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 }
    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;
   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   }
   111   if (!mToken) {
   112     handleError(PIP_PKCS12_RESTORE_FAILED);
   113     return NS_ERROR_NOT_AVAILABLE;
   114   }
   116   // init slot
   117   rv = mToken->Login(true);
   118   if (NS_FAILED(rv)) return rv;
   120   RetryReason wantRetry;
   122   do {
   123     rv = ImportFromFileHelper(file, im_standard_prompt, wantRetry);
   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));
   132   return rv;
   133 }
   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;
   146   PK11SlotInfo *slot=nullptr;
   147   nsXPIDLString tokenName;
   148   unicodePw.data = nullptr;
   150   aWantRetry = rr_do_not_retry;
   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   }
   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   }
   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 }
   240 static bool
   241 isExtractable(SECKEYPrivateKey *privKey)
   242 {
   243   SECItem value;
   244   bool    isExtractable = false;
   245   SECStatus rv;
   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 }
   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
   284   bool InformedUserNoSmartcardBackup = false;
   285   int numCertsExported = 0;
   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);
   325       if (privKey) {
   326         bool privKeyIsExtractable = isExtractable(privKey);
   328         SECKEY_DestroyPrivateKey(privKey);
   330         if (!privKeyIsExtractable) {
   331           if (!InformedUserNoSmartcardBackup) {
   332             InformedUserNoSmartcardBackup = true;
   333             handleError(PIP_PKCS12_NOSMARTCARD_EXPORT);
   334           }
   335           continue;
   336         }
   337       }
   338     }
   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   }
   364   if (!numCertsExported) goto finish;
   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 }
   403 ///////////////////////////////////////////////////////////////////////
   404 //
   405 //  private members
   406 //
   407 ///////////////////////////////////////////////////////////////////////
   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 }
   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 }
   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 }
   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];
   501   nsCOMPtr<nsIInputStream> fileStream;
   502   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), file);
   504   if (NS_FAILED(rv)) {
   505     return rv;
   506   }
   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 }
   529 //
   530 // C callback methods
   531 //
   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);
   541   if (reading) {
   542     NS_ENSURE_TRUE(cx->mDigest, SECFailure);
   544     delete cx->mDigestIterator;
   545     cx->mDigestIterator = new nsCString::const_iterator;
   547     if (!cx->mDigestIterator) {
   548       PORT_SetError(SEC_ERROR_NO_MEMORY);
   549       return SECFailure;
   550     }
   552     cx->mDigest->BeginReading(*cx->mDigestIterator);
   553   }
   554   else {
   555     delete cx->mDigest;
   556     cx->mDigest = new nsCString;
   558     if (!cx->mDigest) {
   559       PORT_SetError(SEC_ERROR_NO_MEMORY);
   560       return SECFailure;
   561     }
   562   }
   564   return SECSuccess;
   565 }
   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);
   576   delete cx->mDigestIterator;
   577   cx->mDigestIterator = nullptr;
   579   if (remove_it) {  
   580     delete cx->mDigest;
   581     cx->mDigest = nullptr;
   582   }
   584   return SECSuccess;
   585 }
   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);
   596   // iterator object must exist when digest has been opened in read mode
   597   NS_ENSURE_TRUE(cx->mDigestIterator, SECFailure);
   599   unsigned long available = cx->mDigestIterator->size_forward();
   601   if (len > available)
   602     len = available;
   604   memcpy(buf, cx->mDigestIterator->get(), len);
   605   cx->mDigestIterator->advance(len);
   607   return len;
   608 }
   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);
   619   // make sure we are in write mode, read iterator has not yet been allocated
   620   NS_ENSURE_FALSE(cx->mDigestIterator, SECFailure);
   622   cx->mDigest->Append(reinterpret_cast<char *>(buf),
   623                      static_cast<uint32_t>(len));
   625   return len;
   626 }
   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);
   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;
   687   newNick->type = siAsciiString;
   688   newNick->data = (unsigned char*) strdup(nickname.get());
   689   newNick->len  = strlen((char*)newNick->data);
   690   return newNick;
   691 }
   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 }
   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 }
   720 void
   721 nsPKCS12Blob::handleError(int myerr)
   722 {
   723   static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
   725   if (!NS_IsMainThread()) {
   726     NS_ERROR("nsPKCS12Blob::handleError called off the mai nthread.");
   727     return;
   728   }
   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));
   734   const char * msgID = nullptr;
   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;
   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;
   762     case SEC_ERROR_PKCS12_DUPLICATE_DATA: msgID = "PKCS12DupData"; break;
   763     }
   764     break;
   765   }
   767   if (!msgID)
   768     msgID = "PKCS12UnknownErr";
   770   nsresult rv;
   771   nsCOMPtr<nsINSSComponent> nssComponent = do_GetService(kNSSComponentCID, &rv);
   772   if (NS_SUCCEEDED(rv))
   773     (void) nssComponent->ShowAlertFromStringBundle(msgID);
   774 }

mercurial