Home | History | Annotate | Download | only in mozilla_security_manager
      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