security/manager/ssl/src/nsPKCS12Blob.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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

mercurial