1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is the Netscape security libraries. 15 * 16 * The Initial Developer of the Original Code is 17 * Netscape Communications Corporation. 18 * Portions created by the Initial Developer are Copyright (C) 2000 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * Ian McGreer <mcgreer (at) netscape.com> 23 * 24 * Alternatively, the contents of this file may be used under the terms of 25 * either the GNU General Public License Version 2 or later (the "GPL"), or 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 * in which case the provisions of the GPL or the LGPL are applicable instead 28 * of those above. If you wish to allow use of your version of this file only 29 * under the terms of either the GPL or the LGPL, and not to allow others to 30 * use your version of this file under the terms of the MPL, indicate your 31 * decision by deleting the provisions above and replace them with the notice 32 * and other provisions required by the GPL or the LGPL. If you do not delete 33 * the provisions above, a recipient may use your version of this file under 34 * the terms of any one of the MPL, the GPL or the LGPL. 35 * 36 * ***** END LICENSE BLOCK ***** */ 37 38 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h" 39 40 #include <pk11pub.h> 41 #include <pkcs12.h> 42 #include <p12plcy.h> 43 #include <secerr.h> 44 45 #include "base/lazy_instance.h" 46 #include "base/logging.h" 47 #include "base/strings/string_util.h" 48 #include "crypto/nss_util_internal.h" 49 #include "net/base/net_errors.h" 50 #include "net/cert/x509_certificate.h" 51 52 namespace mozilla_security_manager { 53 54 namespace { 55 56 // unicodeToItem 57 // 58 // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to 59 // a buffer of octets. Must handle byte order correctly. 60 // TODO: Is there a Mozilla way to do this? In the string lib? 61 void unicodeToItem(const PRUnichar *uni, SECItem *item) 62 { 63 int len = 0; 64 while (uni[len++] != 0); 65 SECITEM_AllocItem(NULL, item, sizeof(PRUnichar) * len); 66 #ifdef IS_LITTLE_ENDIAN 67 int i = 0; 68 for (i=0; i<len; i++) { 69 item->data[2*i ] = (unsigned char )(uni[i] << 8); 70 item->data[2*i+1] = (unsigned char )(uni[i]); 71 } 72 #else 73 memcpy(item->data, uni, item->len); 74 #endif 75 } 76 77 // write_export_data 78 // write bytes to the exported PKCS#12 data buffer 79 void write_export_data(void* arg, const char* buf, unsigned long len) { 80 std::string* dest = reinterpret_cast<std::string*>(arg); 81 dest->append(buf, len); 82 } 83 84 // nickname_collision 85 // what to do when the nickname collides with one already in the db. 86 // Based on P12U_NicknameCollisionCallback from nss/cmd/pk12util/pk12util.c 87 SECItem* PR_CALLBACK 88 nickname_collision(SECItem *old_nick, PRBool *cancel, void *wincx) 89 { 90 char *nick = NULL; 91 SECItem *ret_nick = NULL; 92 CERTCertificate* cert = (CERTCertificate*)wincx; 93 94 if (!cancel || !cert) { 95 // pk12util calls this error user cancelled? 96 return NULL; 97 } 98 99 if (!old_nick) 100 VLOG(1) << "no nickname for cert in PKCS12 file."; 101 102 nick = CERT_MakeCANickname(cert); 103 if (!nick) { 104 return NULL; 105 } 106 107 if(old_nick && old_nick->data && old_nick->len && 108 PORT_Strlen(nick) == old_nick->len && 109 !PORT_Strncmp((char *)old_nick->data, nick, old_nick->len)) { 110 PORT_Free(nick); 111 PORT_SetError(SEC_ERROR_IO); 112 return NULL; 113 } 114 115 VLOG(1) << "using nickname " << nick; 116 ret_nick = PORT_ZNew(SECItem); 117 if(ret_nick == NULL) { 118 PORT_Free(nick); 119 return NULL; 120 } 121 122 ret_nick->data = (unsigned char *)nick; 123 ret_nick->len = PORT_Strlen(nick); 124 125 return ret_nick; 126 } 127 128 // pip_ucs2_ascii_conversion_fn 129 // required to be set by NSS (to do PKCS#12), but since we've already got 130 // unicode make this a no-op. 131 PRBool 132 pip_ucs2_ascii_conversion_fn(PRBool toUnicode, 133 unsigned char *inBuf, 134 unsigned int inBufLen, 135 unsigned char *outBuf, 136 unsigned int maxOutBufLen, 137 unsigned int *outBufLen, 138 PRBool swapBytes) 139 { 140 CHECK_GE(maxOutBufLen, inBufLen); 141 // do a no-op, since I've already got Unicode. Hah! 142 *outBufLen = inBufLen; 143 memcpy(outBuf, inBuf, inBufLen); 144 return PR_TRUE; 145 } 146 147 // Based on nsPKCS12Blob::ImportFromFileHelper. 148 int 149 nsPKCS12Blob_ImportHelper(const char* pkcs12_data, 150 size_t pkcs12_len, 151 const base::string16& password, 152 bool is_extractable, 153 bool try_zero_length_secitem, 154 PK11SlotInfo *slot, 155 net::CertificateList* imported_certs) 156 { 157 DCHECK(pkcs12_data); 158 DCHECK(slot); 159 int import_result = net::ERR_PKCS12_IMPORT_FAILED; 160 SECStatus srv = SECSuccess; 161 SEC_PKCS12DecoderContext *dcx = NULL; 162 SECItem unicodePw; 163 SECItem attribute_value; 164 CK_BBOOL attribute_data = CK_FALSE; 165 const SEC_PKCS12DecoderItem* decoder_item = NULL; 166 167 unicodePw.type = siBuffer; 168 unicodePw.len = 0; 169 unicodePw.data = NULL; 170 if (!try_zero_length_secitem) { 171 unicodeToItem(password.c_str(), &unicodePw); 172 } 173 174 // Initialize the decoder 175 dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, 176 // wincx 177 NULL, 178 // dOpen, dClose, dRead, dWrite, dArg: NULL 179 // specifies default impl using memory buffer. 180 NULL, NULL, NULL, NULL, NULL); 181 if (!dcx) { 182 srv = SECFailure; 183 goto finish; 184 } 185 // feed input to the decoder 186 srv = SEC_PKCS12DecoderUpdate(dcx, 187 (unsigned char*)pkcs12_data, 188 pkcs12_len); 189 if (srv) goto finish; 190 // verify the blob 191 srv = SEC_PKCS12DecoderVerify(dcx); 192 if (srv) goto finish; 193 // validate bags 194 srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision); 195 if (srv) goto finish; 196 // import certificate and key 197 srv = SEC_PKCS12DecoderImportBags(dcx); 198 if (srv) goto finish; 199 200 attribute_value.data = &attribute_data; 201 attribute_value.len = sizeof(attribute_data); 202 203 srv = SEC_PKCS12DecoderIterateInit(dcx); 204 if (srv) goto finish; 205 206 if (imported_certs) 207 imported_certs->clear(); 208 209 // Collect the list of decoded certificates, and mark private keys 210 // non-extractable if needed. 211 while (SEC_PKCS12DecoderIterateNext(dcx, &decoder_item) == SECSuccess) { 212 if (decoder_item->type != SEC_OID_PKCS12_V1_CERT_BAG_ID) 213 continue; 214 215 CERTCertificate* cert = PK11_FindCertFromDERCertItem( 216 slot, decoder_item->der, 217 NULL); // wincx 218 if (!cert) { 219 LOG(ERROR) << "Could not grab a handle to the certificate in the slot " 220 << "from the corresponding PKCS#12 DER certificate."; 221 continue; 222 } 223 224 // Add the cert to the list 225 if (imported_certs) { 226 // Empty list of intermediates. 227 net::X509Certificate::OSCertHandles intermediates; 228 imported_certs->push_back( 229 net::X509Certificate::CreateFromHandle(cert, intermediates)); 230 } 231 232 // Once we have determined that the imported certificate has an 233 // associated private key too, only then can we mark the key as 234 // non-extractable. 235 if (!decoder_item->hasKey) { 236 CERT_DestroyCertificate(cert); 237 continue; 238 } 239 240 // Iterate through all the imported PKCS12 items and mark any accompanying 241 // private keys as non-extractable. 242 if (!is_extractable) { 243 SECKEYPrivateKey* privKey = PK11_FindPrivateKeyFromCert(slot, cert, 244 NULL); // wincx 245 if (privKey) { 246 // Mark the private key as non-extractable. 247 srv = PK11_WriteRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, 248 &attribute_value); 249 SECKEY_DestroyPrivateKey(privKey); 250 if (srv) { 251 LOG(ERROR) << "Could not set CKA_EXTRACTABLE attribute on private " 252 << "key."; 253 CERT_DestroyCertificate(cert); 254 break; 255 } 256 } 257 } 258 CERT_DestroyCertificate(cert); 259 if (srv) goto finish; 260 } 261 import_result = net::OK; 262 finish: 263 // If srv != SECSuccess, NSS probably set a specific error code. 264 // We should use that error code instead of inventing a new one 265 // for every error possible. 266 if (srv != SECSuccess) { 267 int error = PORT_GetError(); 268 LOG(ERROR) << "PKCS#12 import failed with error " << error; 269 switch (error) { 270 case SEC_ERROR_BAD_PASSWORD: 271 case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT: 272 import_result = net::ERR_PKCS12_IMPORT_BAD_PASSWORD; 273 break; 274 case SEC_ERROR_PKCS12_INVALID_MAC: 275 import_result = net::ERR_PKCS12_IMPORT_INVALID_MAC; 276 break; 277 case SEC_ERROR_BAD_DER: 278 case SEC_ERROR_PKCS12_DECODING_PFX: 279 case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE: 280 import_result = net::ERR_PKCS12_IMPORT_INVALID_FILE; 281 break; 282 case SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM: 283 case SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE: 284 case SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM: 285 case SEC_ERROR_PKCS12_UNSUPPORTED_VERSION: 286 import_result = net::ERR_PKCS12_IMPORT_UNSUPPORTED; 287 break; 288 default: 289 import_result = net::ERR_PKCS12_IMPORT_FAILED; 290 break; 291 } 292 } 293 // Finish the decoder 294 if (dcx) 295 SEC_PKCS12DecoderFinish(dcx); 296 SECITEM_ZfreeItem(&unicodePw, PR_FALSE); 297 return import_result; 298 } 299 300 301 // Attempt to read the CKA_EXTRACTABLE attribute on a private key inside 302 // a token. On success, store the attribute in |extractable| and return 303 // SECSuccess. 304 SECStatus 305 isExtractable(SECKEYPrivateKey *privKey, PRBool *extractable) 306 { 307 SECItem value; 308 SECStatus rv; 309 310 rv=PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value); 311 if (rv != SECSuccess) 312 return rv; 313 314 if ((value.len == 1) && (value.data != NULL)) 315 *extractable = !!(*(CK_BBOOL*)value.data); 316 else 317 rv = SECFailure; 318 SECITEM_FreeItem(&value, PR_FALSE); 319 return rv; 320 } 321 322 class PKCS12InitSingleton { 323 public: 324 // From the PKCS#12 section of nsNSSComponent::InitializeNSS in 325 // nsNSSComponent.cpp. 326 PKCS12InitSingleton() { 327 // Enable ciphers for PKCS#12 328 SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1); 329 SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1); 330 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1); 331 SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1); 332 SEC_PKCS12EnableCipher(PKCS12_DES_56, 1); 333 SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1); 334 SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1); 335 336 // Set no-op ascii-ucs2 conversion function to work around weird NSS 337 // interface. Thankfully, PKCS12 appears to be the only thing in NSS that 338 // uses PORT_UCS2_ASCIIConversion, so this doesn't break anything else. 339 PORT_SetUCS2_ASCIIConversionFunction(pip_ucs2_ascii_conversion_fn); 340 } 341 }; 342 343 // Leaky so it can be initialized on worker threads and because there is no 344 // cleanup necessary. 345 static base::LazyInstance<PKCS12InitSingleton>::Leaky g_pkcs12_init_singleton = 346 LAZY_INSTANCE_INITIALIZER; 347 348 } // namespace 349 350 void EnsurePKCS12Init() { 351 g_pkcs12_init_singleton.Get(); 352 } 353 354 // Based on nsPKCS12Blob::ImportFromFile. 355 int nsPKCS12Blob_Import(PK11SlotInfo* slot, 356 const char* pkcs12_data, 357 size_t pkcs12_len, 358 const base::string16& password, 359 bool is_extractable, 360 net::CertificateList* imported_certs) { 361 362 int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, 363 is_extractable, false, slot, 364 imported_certs); 365 366 // When the user entered a zero length password: 367 // An empty password should be represented as an empty 368 // string (a SECItem that contains a single terminating 369 // NULL UTF16 character), but some applications use a 370 // zero length SECItem. 371 // We try both variations, zero length item and empty string, 372 // without giving a user prompt when trying the different empty password 373 // flavors. 374 if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) { 375 rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, 376 is_extractable, true, slot, imported_certs); 377 } 378 return rv; 379 } 380 381 // Based on nsPKCS12Blob::ExportToFile 382 // 383 // Having already loaded the certs, form them into a blob (loading the keys 384 // also), encode the blob, and stuff it into the file. 385 // 386 // TODO: handle slots correctly 387 // mirror "slotToUse" behavior from PSM 1.x 388 // verify the cert array to start off with? 389 // set appropriate error codes 390 int 391 nsPKCS12Blob_Export(std::string* output, 392 const net::CertificateList& certs, 393 const base::string16& password) 394 { 395 int return_count = 0; 396 SECStatus srv = SECSuccess; 397 SEC_PKCS12ExportContext *ecx = NULL; 398 SEC_PKCS12SafeInfo *certSafe = NULL, *keySafe = NULL; 399 SECItem unicodePw; 400 unicodePw.type = siBuffer; 401 unicodePw.len = 0; 402 unicodePw.data = NULL; 403 404 int numCertsExported = 0; 405 406 // get file password (unicode) 407 unicodeToItem(password.c_str(), &unicodePw); 408 409 // what about slotToUse in psm 1.x ??? 410 // create export context 411 ecx = SEC_PKCS12CreateExportContext(NULL, NULL, NULL /*slot*/, NULL); 412 if (!ecx) { 413 srv = SECFailure; 414 goto finish; 415 } 416 // add password integrity 417 srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1); 418 if (srv) goto finish; 419 420 for (size_t i=0; i<certs.size(); i++) { 421 DCHECK(certs[i].get()); 422 CERTCertificate* nssCert = certs[i]->os_cert_handle(); 423 DCHECK(nssCert); 424 425 // We only allow certificate and private key extraction if the corresponding 426 // CKA_EXTRACTABLE private key attribute is set to CK_TRUE. Most hardware 427 // tokens including smartcards enforce this behavior. An internal (soft) 428 // token may ignore this attribute (and hence still be able to export) but 429 // we still refuse to attempt an export. 430 // In addition, some tokens may not support this attribute, in which case 431 // we still attempt the export and let the token implementation dictate 432 // the export behavior. 433 if (nssCert->slot) { 434 SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot, 435 nssCert, 436 NULL); // wincx 437 if (privKey) { 438 PRBool privKeyIsExtractable = PR_FALSE; 439 SECStatus rv = isExtractable(privKey, &privKeyIsExtractable); 440 SECKEY_DestroyPrivateKey(privKey); 441 442 if (rv == SECSuccess && !privKeyIsExtractable) { 443 LOG(ERROR) << "Private key is not extractable"; 444 continue; 445 } 446 } 447 } 448 449 // XXX this is why, to verify the slot is the same 450 // PK11_FindObjectForCert(nssCert, NULL, slot); 451 // create the cert and key safes 452 keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx); 453 if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) { 454 certSafe = keySafe; 455 } else { 456 certSafe = SEC_PKCS12CreatePasswordPrivSafe(ecx, &unicodePw, 457 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC); 458 } 459 if (!certSafe || !keySafe) { 460 LOG(ERROR) << "!certSafe || !keySafe " << certSafe << " " << keySafe; 461 srv = SECFailure; 462 goto finish; 463 } 464 // add the cert and key to the blob 465 srv = SEC_PKCS12AddCertAndKey(ecx, certSafe, NULL, nssCert, 466 CERT_GetDefaultCertDB(), 467 keySafe, NULL, PR_TRUE, &unicodePw, 468 SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC); 469 if (srv) goto finish; 470 ++numCertsExported; 471 } 472 473 if (!numCertsExported) goto finish; 474 475 // encode and write 476 srv = SEC_PKCS12Encode(ecx, write_export_data, output); 477 if (srv) goto finish; 478 return_count = numCertsExported; 479 finish: 480 if (srv) 481 LOG(ERROR) << "PKCS#12 export failed with error " << PORT_GetError(); 482 if (ecx) 483 SEC_PKCS12DestroyExportContext(ecx); 484 SECITEM_ZfreeItem(&unicodePw, PR_FALSE); 485 return return_count; 486 } 487 488 } // namespace mozilla_security_manager 489