1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/ssl/src/nsPKCS12Blob.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,774 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +/* $Id: nsPKCS12Blob.cpp,v 1.49 2007/09/05 07:13:46 jwalden%mit.edu Exp $ */ 1.8 + 1.9 +#include "nsPKCS12Blob.h" 1.10 + 1.11 +#include "pkix/pkixtypes.h" 1.12 + 1.13 +#include "prmem.h" 1.14 +#include "prprf.h" 1.15 + 1.16 +#include "nsIFile.h" 1.17 +#include "nsNetUtil.h" 1.18 +#include "nsIDirectoryService.h" 1.19 +#include "nsThreadUtils.h" 1.20 + 1.21 +#include "nsNSSComponent.h" 1.22 +#include "nsNSSHelper.h" 1.23 +#include "nsString.h" 1.24 +#include "nsReadableUtils.h" 1.25 +#include "nsXPIDLString.h" 1.26 +#include "nsDirectoryServiceDefs.h" 1.27 +#include "nsNSSHelper.h" 1.28 +#include "nsNSSCertificate.h" 1.29 +#include "nsKeygenHandler.h" //For GetSlotWithMechanism 1.30 +#include "nsPK11TokenDB.h" 1.31 +#include "nsICertificateDialogs.h" 1.32 +#include "nsNSSShutDown.h" 1.33 +#include "nsCRT.h" 1.34 + 1.35 +#include "secerr.h" 1.36 + 1.37 +#ifdef PR_LOGGING 1.38 +extern PRLogModuleInfo* gPIPNSSLog; 1.39 +#endif 1.40 + 1.41 +using namespace mozilla; 1.42 + 1.43 +#define PIP_PKCS12_TMPFILENAME NS_LITERAL_CSTRING(".pip_p12tmp") 1.44 +#define PIP_PKCS12_BUFFER_SIZE 2048 1.45 +#define PIP_PKCS12_RESTORE_OK 1 1.46 +#define PIP_PKCS12_BACKUP_OK 2 1.47 +#define PIP_PKCS12_USER_CANCELED 3 1.48 +#define PIP_PKCS12_NOSMARTCARD_EXPORT 4 1.49 +#define PIP_PKCS12_RESTORE_FAILED 5 1.50 +#define PIP_PKCS12_BACKUP_FAILED 6 1.51 +#define PIP_PKCS12_NSS_ERROR 7 1.52 + 1.53 +// constructor 1.54 +nsPKCS12Blob::nsPKCS12Blob():mCertArray(0), 1.55 + mTmpFile(nullptr), 1.56 + mDigest(nullptr), 1.57 + mDigestIterator(nullptr), 1.58 + mTokenSet(false) 1.59 +{ 1.60 + mUIContext = new PipUIContext(); 1.61 +} 1.62 + 1.63 +// destructor 1.64 +nsPKCS12Blob::~nsPKCS12Blob() 1.65 +{ 1.66 + delete mDigestIterator; 1.67 + delete mDigest; 1.68 +} 1.69 + 1.70 +// nsPKCS12Blob::SetToken 1.71 +// 1.72 +// Set the token to use for import/export 1.73 +nsresult 1.74 +nsPKCS12Blob::SetToken(nsIPK11Token *token) 1.75 +{ 1.76 + nsNSSShutDownPreventionLock locker; 1.77 + nsresult rv = NS_OK; 1.78 + if (token) { 1.79 + mToken = token; 1.80 + } else { 1.81 + PK11SlotInfo *slot; 1.82 + rv = GetSlotWithMechanism(CKM_RSA_PKCS, mUIContext,&slot); 1.83 + if (NS_FAILED(rv)) { 1.84 + mToken = 0; 1.85 + } else { 1.86 + mToken = new nsPK11Token(slot); 1.87 + PK11_FreeSlot(slot); 1.88 + } 1.89 + } 1.90 + mTokenSet = true; 1.91 + return rv; 1.92 +} 1.93 + 1.94 +// nsPKCS12Blob::ImportFromFile 1.95 +// 1.96 +// Given a file handle, read a PKCS#12 blob from that file, decode it, 1.97 +// and import the results into the token. 1.98 +nsresult 1.99 +nsPKCS12Blob::ImportFromFile(nsIFile *file) 1.100 +{ 1.101 + nsNSSShutDownPreventionLock locker; 1.102 + nsresult rv = NS_OK; 1.103 + 1.104 + if (!mToken) { 1.105 + if (!mTokenSet) { 1.106 + rv = SetToken(nullptr); // Ask the user to pick a slot 1.107 + if (NS_FAILED(rv)) { 1.108 + handleError(PIP_PKCS12_USER_CANCELED); 1.109 + return rv; 1.110 + } 1.111 + } 1.112 + } 1.113 + 1.114 + if (!mToken) { 1.115 + handleError(PIP_PKCS12_RESTORE_FAILED); 1.116 + return NS_ERROR_NOT_AVAILABLE; 1.117 + } 1.118 + 1.119 + // init slot 1.120 + rv = mToken->Login(true); 1.121 + if (NS_FAILED(rv)) return rv; 1.122 + 1.123 + RetryReason wantRetry; 1.124 + 1.125 + do { 1.126 + rv = ImportFromFileHelper(file, im_standard_prompt, wantRetry); 1.127 + 1.128 + if (NS_SUCCEEDED(rv) && wantRetry == rr_auto_retry_empty_password_flavors) 1.129 + { 1.130 + rv = ImportFromFileHelper(file, im_try_zero_length_secitem, wantRetry); 1.131 + } 1.132 + } 1.133 + while (NS_SUCCEEDED(rv) && (wantRetry != rr_do_not_retry)); 1.134 + 1.135 + return rv; 1.136 +} 1.137 + 1.138 +nsresult 1.139 +nsPKCS12Blob::ImportFromFileHelper(nsIFile *file, 1.140 + nsPKCS12Blob::ImportMode aImportMode, 1.141 + nsPKCS12Blob::RetryReason &aWantRetry) 1.142 +{ 1.143 + nsNSSShutDownPreventionLock locker; 1.144 + nsresult rv = NS_OK; 1.145 + SECStatus srv = SECSuccess; 1.146 + SEC_PKCS12DecoderContext *dcx = nullptr; 1.147 + SECItem unicodePw; 1.148 + 1.149 + PK11SlotInfo *slot=nullptr; 1.150 + nsXPIDLString tokenName; 1.151 + unicodePw.data = nullptr; 1.152 + 1.153 + aWantRetry = rr_do_not_retry; 1.154 + 1.155 + if (aImportMode == im_try_zero_length_secitem) 1.156 + { 1.157 + unicodePw.len = 0; 1.158 + } 1.159 + else 1.160 + { 1.161 + // get file password (unicode) 1.162 + rv = getPKCS12FilePassword(&unicodePw); 1.163 + if (NS_FAILED(rv)) goto finish; 1.164 + if (!unicodePw.data) { 1.165 + handleError(PIP_PKCS12_USER_CANCELED); 1.166 + return NS_OK; 1.167 + } 1.168 + } 1.169 + 1.170 + mToken->GetTokenName(getter_Copies(tokenName)); 1.171 + { 1.172 + NS_ConvertUTF16toUTF8 tokenNameCString(tokenName); 1.173 + slot = PK11_FindSlotByName(tokenNameCString.get()); 1.174 + } 1.175 + if (!slot) { 1.176 + srv = SECFailure; 1.177 + goto finish; 1.178 + } 1.179 + 1.180 + // initialize the decoder 1.181 + dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, nullptr, 1.182 + digest_open, digest_close, 1.183 + digest_read, digest_write, 1.184 + this); 1.185 + if (!dcx) { 1.186 + srv = SECFailure; 1.187 + goto finish; 1.188 + } 1.189 + // read input file and feed it to the decoder 1.190 + rv = inputToDecoder(dcx, file); 1.191 + if (NS_FAILED(rv)) { 1.192 + if (NS_ERROR_ABORT == rv) { 1.193 + // inputToDecoder indicated a NSS error 1.194 + srv = SECFailure; 1.195 + } 1.196 + goto finish; 1.197 + } 1.198 + // verify the blob 1.199 + srv = SEC_PKCS12DecoderVerify(dcx); 1.200 + if (srv) goto finish; 1.201 + // validate bags 1.202 + srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision); 1.203 + if (srv) goto finish; 1.204 + // import cert and key 1.205 + srv = SEC_PKCS12DecoderImportBags(dcx); 1.206 + if (srv) goto finish; 1.207 + // Later - check to see if this should become default email cert 1.208 + handleError(PIP_PKCS12_RESTORE_OK); 1.209 +finish: 1.210 + // If srv != SECSuccess, NSS probably set a specific error code. 1.211 + // We should use that error code instead of inventing a new one 1.212 + // for every error possible. 1.213 + if (srv != SECSuccess) { 1.214 + if (SEC_ERROR_BAD_PASSWORD == PORT_GetError()) { 1.215 + if (unicodePw.len == sizeof(char16_t)) 1.216 + { 1.217 + // no password chars available, 1.218 + // unicodeToItem allocated space for the trailing zero character only. 1.219 + aWantRetry = rr_auto_retry_empty_password_flavors; 1.220 + } 1.221 + else 1.222 + { 1.223 + aWantRetry = rr_bad_password; 1.224 + handleError(PIP_PKCS12_NSS_ERROR); 1.225 + } 1.226 + } 1.227 + else 1.228 + { 1.229 + handleError(PIP_PKCS12_NSS_ERROR); 1.230 + } 1.231 + } else if (NS_FAILED(rv)) { 1.232 + handleError(PIP_PKCS12_RESTORE_FAILED); 1.233 + } 1.234 + if (slot) 1.235 + PK11_FreeSlot(slot); 1.236 + // finish the decoder 1.237 + if (dcx) 1.238 + SEC_PKCS12DecoderFinish(dcx); 1.239 + SECITEM_ZfreeItem(&unicodePw, false); 1.240 + return NS_OK; 1.241 +} 1.242 + 1.243 +static bool 1.244 +isExtractable(SECKEYPrivateKey *privKey) 1.245 +{ 1.246 + SECItem value; 1.247 + bool isExtractable = false; 1.248 + SECStatus rv; 1.249 + 1.250 + rv=PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value); 1.251 + if (rv != SECSuccess) { 1.252 + return false; 1.253 + } 1.254 + if ((value.len == 1) && value.data) { 1.255 + isExtractable = !!(*(CK_BBOOL*)value.data); 1.256 + } 1.257 + SECITEM_FreeItem(&value, false); 1.258 + return isExtractable; 1.259 +} 1.260 + 1.261 +// nsPKCS12Blob::ExportToFile 1.262 +// 1.263 +// Having already loaded the certs, form them into a blob (loading the keys 1.264 +// also), encode the blob, and stuff it into the file. 1.265 +// 1.266 +// TODO: handle slots correctly 1.267 +// mirror "slotToUse" behavior from PSM 1.x 1.268 +// verify the cert array to start off with? 1.269 +// open output file as nsIFileStream object? 1.270 +// set appropriate error codes 1.271 +nsresult 1.272 +nsPKCS12Blob::ExportToFile(nsIFile *file, 1.273 + nsIX509Cert **certs, int numCerts) 1.274 +{ 1.275 + nsNSSShutDownPreventionLock locker; 1.276 + nsresult rv; 1.277 + SECStatus srv = SECSuccess; 1.278 + SEC_PKCS12ExportContext *ecx = nullptr; 1.279 + SEC_PKCS12SafeInfo *certSafe = nullptr, *keySafe = nullptr; 1.280 + SECItem unicodePw; 1.281 + nsAutoString filePath; 1.282 + int i; 1.283 + nsCOMPtr<nsIFile> localFileRef; 1.284 + NS_ASSERTION(mToken, "Need to set the token before exporting"); 1.285 + // init slot 1.286 + 1.287 + bool InformedUserNoSmartcardBackup = false; 1.288 + int numCertsExported = 0; 1.289 + 1.290 + rv = mToken->Login(true); 1.291 + if (NS_FAILED(rv)) goto finish; 1.292 + // get file password (unicode) 1.293 + unicodePw.data = nullptr; 1.294 + rv = newPKCS12FilePassword(&unicodePw); 1.295 + if (NS_FAILED(rv)) goto finish; 1.296 + if (!unicodePw.data) { 1.297 + handleError(PIP_PKCS12_USER_CANCELED); 1.298 + return NS_OK; 1.299 + } 1.300 + // what about slotToUse in psm 1.x ??? 1.301 + // create export context 1.302 + ecx = SEC_PKCS12CreateExportContext(nullptr, nullptr, nullptr /*slot*/, nullptr); 1.303 + if (!ecx) { 1.304 + srv = SECFailure; 1.305 + goto finish; 1.306 + } 1.307 + // add password integrity 1.308 + srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1); 1.309 + if (srv) goto finish; 1.310 + for (i=0; i<numCerts; i++) { 1.311 + nsNSSCertificate *cert = (nsNSSCertificate *)certs[i]; 1.312 + // get it as a CERTCertificate XXX 1.313 + mozilla::pkix::ScopedCERTCertificate nssCert(cert->GetCert()); 1.314 + if (!nssCert) { 1.315 + rv = NS_ERROR_FAILURE; 1.316 + goto finish; 1.317 + } 1.318 + // We can only successfully export certs that are on 1.319 + // internal token. Most, if not all, smart card vendors 1.320 + // won't let you extract the private key (in any way 1.321 + // shape or form) from the card. So let's punt if 1.322 + // the cert is not in the internal db. 1.323 + if (nssCert->slot && !PK11_IsInternal(nssCert->slot)) { 1.324 + // we aren't the internal token, see if the key is extractable. 1.325 + SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot, 1.326 + nssCert.get(), this); 1.327 + 1.328 + if (privKey) { 1.329 + bool privKeyIsExtractable = isExtractable(privKey); 1.330 + 1.331 + SECKEY_DestroyPrivateKey(privKey); 1.332 + 1.333 + if (!privKeyIsExtractable) { 1.334 + if (!InformedUserNoSmartcardBackup) { 1.335 + InformedUserNoSmartcardBackup = true; 1.336 + handleError(PIP_PKCS12_NOSMARTCARD_EXPORT); 1.337 + } 1.338 + continue; 1.339 + } 1.340 + } 1.341 + } 1.342 + 1.343 + // XXX this is why, to verify the slot is the same 1.344 + // PK11_FindObjectForCert(nssCert, nullptr, slot); 1.345 + // create the cert and key safes 1.346 + keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx); 1.347 + if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) { 1.348 + certSafe = keySafe; 1.349 + } else { 1.350 + certSafe = SEC_PKCS12CreatePasswordPrivSafe(ecx, &unicodePw, 1.351 + SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC); 1.352 + } 1.353 + if (!certSafe || !keySafe) { 1.354 + rv = NS_ERROR_FAILURE; 1.355 + goto finish; 1.356 + } 1.357 + // add the cert and key to the blob 1.358 + srv = SEC_PKCS12AddCertAndKey(ecx, certSafe, nullptr, nssCert.get(), 1.359 + CERT_GetDefaultCertDB(), // XXX 1.360 + keySafe, nullptr, true, &unicodePw, 1.361 + SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC); 1.362 + if (srv) goto finish; 1.363 + // cert was dup'ed, so release it 1.364 + ++numCertsExported; 1.365 + } 1.366 + 1.367 + if (!numCertsExported) goto finish; 1.368 + 1.369 + // prepare the instance to write to an export file 1.370 + this->mTmpFile = nullptr; 1.371 + file->GetPath(filePath); 1.372 + // Use the nsCOMPtr var localFileRef so that 1.373 + // the reference to the nsIFile we create gets released as soon as 1.374 + // we're out of scope, ie when this function exits. 1.375 + if (filePath.RFind(".p12", true, -1, 4) < 0) { 1.376 + // We're going to add the .p12 extension to the file name just like 1.377 + // Communicator used to. We create a new nsIFile and initialize 1.378 + // it with the new patch. 1.379 + filePath.AppendLiteral(".p12"); 1.380 + localFileRef = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); 1.381 + if (NS_FAILED(rv)) goto finish; 1.382 + localFileRef->InitWithPath(filePath); 1.383 + file = localFileRef; 1.384 + } 1.385 + rv = file->OpenNSPRFileDesc(PR_RDWR|PR_CREATE_FILE|PR_TRUNCATE, 0664, 1.386 + &mTmpFile); 1.387 + if (NS_FAILED(rv) || !this->mTmpFile) goto finish; 1.388 + // encode and write 1.389 + srv = SEC_PKCS12Encode(ecx, write_export_file, this); 1.390 + if (srv) goto finish; 1.391 + handleError(PIP_PKCS12_BACKUP_OK); 1.392 +finish: 1.393 + if (NS_FAILED(rv) || srv != SECSuccess) { 1.394 + handleError(PIP_PKCS12_BACKUP_FAILED); 1.395 + } 1.396 + if (ecx) 1.397 + SEC_PKCS12DestroyExportContext(ecx); 1.398 + if (this->mTmpFile) { 1.399 + PR_Close(this->mTmpFile); 1.400 + this->mTmpFile = nullptr; 1.401 + } 1.402 + SECITEM_ZfreeItem(&unicodePw, false); 1.403 + return rv; 1.404 +} 1.405 + 1.406 +/////////////////////////////////////////////////////////////////////// 1.407 +// 1.408 +// private members 1.409 +// 1.410 +/////////////////////////////////////////////////////////////////////// 1.411 + 1.412 +// unicodeToItem 1.413 +// 1.414 +// For the NSS PKCS#12 library, must convert PRUnichars (shorts) to 1.415 +// a buffer of octets. Must handle byte order correctly. 1.416 +// TODO: Is there a mozilla way to do this? In the string lib? 1.417 +void 1.418 +nsPKCS12Blob::unicodeToItem(const char16_t *uni, SECItem *item) 1.419 +{ 1.420 + int len = 0; 1.421 + while (uni[len++] != 0); 1.422 + SECITEM_AllocItem(nullptr, item, sizeof(char16_t) * len); 1.423 +#ifdef IS_LITTLE_ENDIAN 1.424 + int i = 0; 1.425 + for (i=0; i<len; i++) { 1.426 + item->data[2*i ] = (unsigned char )(uni[i] << 8); 1.427 + item->data[2*i+1] = (unsigned char )(uni[i]); 1.428 + } 1.429 +#else 1.430 + memcpy(item->data, uni, item->len); 1.431 +#endif 1.432 +} 1.433 + 1.434 +// newPKCS12FilePassword 1.435 +// 1.436 +// Launch a dialog requesting the user for a new PKCS#12 file passowrd. 1.437 +// Handle user canceled by returning null password (caller must catch). 1.438 +nsresult 1.439 +nsPKCS12Blob::newPKCS12FilePassword(SECItem *unicodePw) 1.440 +{ 1.441 + nsresult rv = NS_OK; 1.442 + nsAutoString password; 1.443 + nsCOMPtr<nsICertificateDialogs> certDialogs; 1.444 + rv = ::getNSSDialogs(getter_AddRefs(certDialogs), 1.445 + NS_GET_IID(nsICertificateDialogs), 1.446 + NS_CERTIFICATEDIALOGS_CONTRACTID); 1.447 + if (NS_FAILED(rv)) return rv; 1.448 + bool pressedOK; 1.449 + { 1.450 + nsPSMUITracker tracker; 1.451 + if (tracker.isUIForbidden()) { 1.452 + rv = NS_ERROR_NOT_AVAILABLE; 1.453 + } 1.454 + else { 1.455 + rv = certDialogs->SetPKCS12FilePassword(mUIContext, password, &pressedOK); 1.456 + } 1.457 + } 1.458 + if (NS_FAILED(rv) || !pressedOK) return rv; 1.459 + unicodeToItem(password.get(), unicodePw); 1.460 + return NS_OK; 1.461 +} 1.462 + 1.463 +// getPKCS12FilePassword 1.464 +// 1.465 +// Launch a dialog requesting the user for the password to a PKCS#12 file. 1.466 +// Handle user canceled by returning null password (caller must catch). 1.467 +nsresult 1.468 +nsPKCS12Blob::getPKCS12FilePassword(SECItem *unicodePw) 1.469 +{ 1.470 + nsresult rv = NS_OK; 1.471 + nsAutoString password; 1.472 + nsCOMPtr<nsICertificateDialogs> certDialogs; 1.473 + rv = ::getNSSDialogs(getter_AddRefs(certDialogs), 1.474 + NS_GET_IID(nsICertificateDialogs), 1.475 + NS_CERTIFICATEDIALOGS_CONTRACTID); 1.476 + if (NS_FAILED(rv)) return rv; 1.477 + bool pressedOK; 1.478 + { 1.479 + nsPSMUITracker tracker; 1.480 + if (tracker.isUIForbidden()) { 1.481 + rv = NS_ERROR_NOT_AVAILABLE; 1.482 + } 1.483 + else { 1.484 + rv = certDialogs->GetPKCS12FilePassword(mUIContext, password, &pressedOK); 1.485 + } 1.486 + } 1.487 + if (NS_FAILED(rv) || !pressedOK) return rv; 1.488 + unicodeToItem(password.get(), unicodePw); 1.489 + return NS_OK; 1.490 +} 1.491 + 1.492 +// inputToDecoder 1.493 +// 1.494 +// Given a decoder, read bytes from file and input them to the decoder. 1.495 +nsresult 1.496 +nsPKCS12Blob::inputToDecoder(SEC_PKCS12DecoderContext *dcx, nsIFile *file) 1.497 +{ 1.498 + nsNSSShutDownPreventionLock locker; 1.499 + nsresult rv; 1.500 + SECStatus srv; 1.501 + uint32_t amount; 1.502 + char buf[PIP_PKCS12_BUFFER_SIZE]; 1.503 + 1.504 + nsCOMPtr<nsIInputStream> fileStream; 1.505 + rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), file); 1.506 + 1.507 + if (NS_FAILED(rv)) { 1.508 + return rv; 1.509 + } 1.510 + 1.511 + while (true) { 1.512 + rv = fileStream->Read(buf, PIP_PKCS12_BUFFER_SIZE, &amount); 1.513 + if (NS_FAILED(rv)) { 1.514 + return rv; 1.515 + } 1.516 + // feed the file data into the decoder 1.517 + srv = SEC_PKCS12DecoderUpdate(dcx, 1.518 + (unsigned char*) buf, 1.519 + amount); 1.520 + if (srv) { 1.521 + // don't allow the close call to overwrite our precious error code 1.522 + int pr_err = PORT_GetError(); 1.523 + PORT_SetError(pr_err); 1.524 + return NS_ERROR_ABORT; 1.525 + } 1.526 + if (amount < PIP_PKCS12_BUFFER_SIZE) 1.527 + break; 1.528 + } 1.529 + return NS_OK; 1.530 +} 1.531 + 1.532 +// 1.533 +// C callback methods 1.534 +// 1.535 + 1.536 +// digest_open 1.537 +// prepare a memory buffer for reading/writing digests 1.538 +SECStatus 1.539 +nsPKCS12Blob::digest_open(void *arg, PRBool reading) 1.540 +{ 1.541 + nsPKCS12Blob *cx = reinterpret_cast<nsPKCS12Blob *>(arg); 1.542 + NS_ENSURE_TRUE(cx, SECFailure); 1.543 + 1.544 + if (reading) { 1.545 + NS_ENSURE_TRUE(cx->mDigest, SECFailure); 1.546 + 1.547 + delete cx->mDigestIterator; 1.548 + cx->mDigestIterator = new nsCString::const_iterator; 1.549 + 1.550 + if (!cx->mDigestIterator) { 1.551 + PORT_SetError(SEC_ERROR_NO_MEMORY); 1.552 + return SECFailure; 1.553 + } 1.554 + 1.555 + cx->mDigest->BeginReading(*cx->mDigestIterator); 1.556 + } 1.557 + else { 1.558 + delete cx->mDigest; 1.559 + cx->mDigest = new nsCString; 1.560 + 1.561 + if (!cx->mDigest) { 1.562 + PORT_SetError(SEC_ERROR_NO_MEMORY); 1.563 + return SECFailure; 1.564 + } 1.565 + } 1.566 + 1.567 + return SECSuccess; 1.568 +} 1.569 + 1.570 +// digest_close 1.571 +// destroy a possibly active iterator 1.572 +// remove the data buffer if requested 1.573 +SECStatus 1.574 +nsPKCS12Blob::digest_close(void *arg, PRBool remove_it) 1.575 +{ 1.576 + nsPKCS12Blob *cx = reinterpret_cast<nsPKCS12Blob *>(arg); 1.577 + NS_ENSURE_TRUE(cx, SECFailure); 1.578 + 1.579 + delete cx->mDigestIterator; 1.580 + cx->mDigestIterator = nullptr; 1.581 + 1.582 + if (remove_it) { 1.583 + delete cx->mDigest; 1.584 + cx->mDigest = nullptr; 1.585 + } 1.586 + 1.587 + return SECSuccess; 1.588 +} 1.589 + 1.590 +// digest_read 1.591 +// read bytes from the memory buffer 1.592 +int 1.593 +nsPKCS12Blob::digest_read(void *arg, unsigned char *buf, unsigned long len) 1.594 +{ 1.595 + nsPKCS12Blob *cx = reinterpret_cast<nsPKCS12Blob *>(arg); 1.596 + NS_ENSURE_TRUE(cx, SECFailure); 1.597 + NS_ENSURE_TRUE(cx->mDigest, SECFailure); 1.598 + 1.599 + // iterator object must exist when digest has been opened in read mode 1.600 + NS_ENSURE_TRUE(cx->mDigestIterator, SECFailure); 1.601 + 1.602 + unsigned long available = cx->mDigestIterator->size_forward(); 1.603 + 1.604 + if (len > available) 1.605 + len = available; 1.606 + 1.607 + memcpy(buf, cx->mDigestIterator->get(), len); 1.608 + cx->mDigestIterator->advance(len); 1.609 + 1.610 + return len; 1.611 +} 1.612 + 1.613 +// digest_write 1.614 +// append bytes to the memory buffer 1.615 +int 1.616 +nsPKCS12Blob::digest_write(void *arg, unsigned char *buf, unsigned long len) 1.617 +{ 1.618 + nsPKCS12Blob *cx = reinterpret_cast<nsPKCS12Blob *>(arg); 1.619 + NS_ENSURE_TRUE(cx, SECFailure); 1.620 + NS_ENSURE_TRUE(cx->mDigest, SECFailure); 1.621 + 1.622 + // make sure we are in write mode, read iterator has not yet been allocated 1.623 + NS_ENSURE_FALSE(cx->mDigestIterator, SECFailure); 1.624 + 1.625 + cx->mDigest->Append(reinterpret_cast<char *>(buf), 1.626 + static_cast<uint32_t>(len)); 1.627 + 1.628 + return len; 1.629 +} 1.630 + 1.631 +// nickname_collision 1.632 +// what to do when the nickname collides with one already in the db. 1.633 +// TODO: not handled, throw a dialog allowing the nick to be changed? 1.634 +SECItem * 1.635 +nsPKCS12Blob::nickname_collision(SECItem *oldNick, PRBool *cancel, void *wincx) 1.636 +{ 1.637 + static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); 1.638 + 1.639 + nsNSSShutDownPreventionLock locker; 1.640 + *cancel = false; 1.641 + nsresult rv; 1.642 + nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv)); 1.643 + if (NS_FAILED(rv)) return nullptr; 1.644 + int count = 1; 1.645 + nsCString nickname; 1.646 + nsAutoString nickFromProp; 1.647 + nssComponent->GetPIPNSSBundleString("P12DefaultNickname", nickFromProp); 1.648 + NS_ConvertUTF16toUTF8 nickFromPropC(nickFromProp); 1.649 + // The user is trying to import a PKCS#12 file that doesn't have the 1.650 + // attribute we use to set the nickname. So in order to reduce the 1.651 + // number of interactions we require with the user, we'll build a nickname 1.652 + // for the user. The nickname isn't prominently displayed in the UI, 1.653 + // so it's OK if we generate one on our own here. 1.654 + // XXX If the NSS API were smarter and actually passed a pointer to 1.655 + // the CERTCertificate* we're importing we could actually just 1.656 + // call default_nickname (which is what the issuance code path 1.657 + // does) and come up with a reasonable nickname. Alas, the NSS 1.658 + // API limits our ability to produce a useful nickname without 1.659 + // bugging the user. :( 1.660 + while (1) { 1.661 + // If we've gotten this far, that means there isn't a certificate 1.662 + // in the database that has the same subject name as the cert we're 1.663 + // trying to import. So we need to come up with a "nickname" to 1.664 + // satisfy the NSS requirement or fail in trying to import. 1.665 + // Basically we use a default nickname from a properties file and 1.666 + // see if a certificate exists with that nickname. If there isn't, then 1.667 + // create update the count by one and append the string '#1' Or 1.668 + // whatever the count currently is, and look for a cert with 1.669 + // that nickname. Keep updating the count until we find a nickname 1.670 + // without a corresponding cert. 1.671 + // XXX If a user imports *many* certs without the 'friendly name' 1.672 + // attribute, then this may take a long time. :( 1.673 + if (count > 1) { 1.674 + nickname.Adopt(PR_smprintf("%s #%d", nickFromPropC.get(), count)); 1.675 + } else { 1.676 + nickname = nickFromPropC; 1.677 + } 1.678 + CERTCertificate *cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), 1.679 + const_cast<char*>(nickname.get())); 1.680 + if (!cert) { 1.681 + break; 1.682 + } 1.683 + CERT_DestroyCertificate(cert); 1.684 + count++; 1.685 + } 1.686 + SECItem *newNick = new SECItem; 1.687 + if (!newNick) 1.688 + return nullptr; 1.689 + 1.690 + newNick->type = siAsciiString; 1.691 + newNick->data = (unsigned char*) strdup(nickname.get()); 1.692 + newNick->len = strlen((char*)newNick->data); 1.693 + return newNick; 1.694 +} 1.695 + 1.696 +// write_export_file 1.697 +// write bytes to the exported PKCS#12 file 1.698 +void 1.699 +nsPKCS12Blob::write_export_file(void *arg, const char *buf, unsigned long len) 1.700 +{ 1.701 + nsPKCS12Blob *cx = (nsPKCS12Blob *)arg; 1.702 + PR_Write(cx->mTmpFile, buf, len); 1.703 +} 1.704 + 1.705 +// pip_ucs2_ascii_conversion_fn 1.706 +// required to be set by NSS (to do PKCS#12), but since we've already got 1.707 +// unicode make this a no-op. 1.708 +PRBool 1.709 +pip_ucs2_ascii_conversion_fn(PRBool toUnicode, 1.710 + unsigned char *inBuf, 1.711 + unsigned int inBufLen, 1.712 + unsigned char *outBuf, 1.713 + unsigned int maxOutBufLen, 1.714 + unsigned int *outBufLen, 1.715 + PRBool swapBytes) 1.716 +{ 1.717 + // do a no-op, since I've already got unicode. Hah! 1.718 + *outBufLen = inBufLen; 1.719 + memcpy(outBuf, inBuf, inBufLen); 1.720 + return true; 1.721 +} 1.722 + 1.723 +void 1.724 +nsPKCS12Blob::handleError(int myerr) 1.725 +{ 1.726 + static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); 1.727 + 1.728 + if (!NS_IsMainThread()) { 1.729 + NS_ERROR("nsPKCS12Blob::handleError called off the mai nthread."); 1.730 + return; 1.731 + } 1.732 + 1.733 + int prerr = PORT_GetError(); 1.734 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("PKCS12: NSS/NSPR error(%d)", prerr)); 1.735 + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("PKCS12: I called(%d)", myerr)); 1.736 + 1.737 + const char * msgID = nullptr; 1.738 + 1.739 + switch (myerr) { 1.740 + case PIP_PKCS12_RESTORE_OK: msgID = "SuccessfulP12Restore"; break; 1.741 + case PIP_PKCS12_BACKUP_OK: msgID = "SuccessfulP12Backup"; break; 1.742 + case PIP_PKCS12_USER_CANCELED: 1.743 + return; /* Just ignore it for now */ 1.744 + case PIP_PKCS12_NOSMARTCARD_EXPORT: msgID = "PKCS12InfoNoSmartcardBackup"; break; 1.745 + case PIP_PKCS12_RESTORE_FAILED: msgID = "PKCS12UnknownErrRestore"; break; 1.746 + case PIP_PKCS12_BACKUP_FAILED: msgID = "PKCS12UnknownErrBackup"; break; 1.747 + case PIP_PKCS12_NSS_ERROR: 1.748 + switch (prerr) { 1.749 + // The following errors have the potential to be "handled", by asking 1.750 + // the user (via a dialog) whether s/he wishes to continue 1.751 + case 0: break; 1.752 + case SEC_ERROR_PKCS12_CERT_COLLISION: 1.753 + /* pop a dialog saying the cert is already in the database */ 1.754 + /* ask to keep going? what happens if one collision but others ok? */ 1.755 + // The following errors cannot be "handled", notify the user (via an alert) 1.756 + // that the operation failed. 1.757 + case SEC_ERROR_BAD_PASSWORD: msgID = "PK11BadPassword"; break; 1.758 + 1.759 + case SEC_ERROR_BAD_DER: 1.760 + case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE: 1.761 + case SEC_ERROR_PKCS12_INVALID_MAC: 1.762 + msgID = "PKCS12DecodeErr"; 1.763 + break; 1.764 + 1.765 + case SEC_ERROR_PKCS12_DUPLICATE_DATA: msgID = "PKCS12DupData"; break; 1.766 + } 1.767 + break; 1.768 + } 1.769 + 1.770 + if (!msgID) 1.771 + msgID = "PKCS12UnknownErr"; 1.772 + 1.773 + nsresult rv; 1.774 + nsCOMPtr<nsINSSComponent> nssComponent = do_GetService(kNSSComponentCID, &rv); 1.775 + if (NS_SUCCEEDED(rv)) 1.776 + (void) nssComponent->ShowAlertFromStringBundle(msgID); 1.777 +}