15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/* ***** BEGIN LICENSE BLOCK *****
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Version: MPL 1.1/GPL 2.0/LGPL 2.1
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The contents of this file are subject to the Mozilla Public License Version
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * 1.1 (the "License"); you may not use this file except in compliance with
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the License. You may obtain a copy of the License at
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * http://www.mozilla.org/MPL/
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Software distributed under the License is distributed on an "AS IS" basis,
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * for the specific language governing rights and limitations under the
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * License.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The Original Code is the Netscape security libraries.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * The Initial Developer of the Original Code is
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Netscape Communications Corporation.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Portions created by the Initial Developer are Copyright (C) 2000
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the Initial Developer. All Rights Reserved.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Contributor(s):
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *   Ian McGreer <mcgreer@netscape.com>
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Alternatively, the contents of this file may be used under the terms of
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * either the GNU General Public License Version 2 or later (the "GPL"), or
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * in which case the provisions of the GPL or the LGPL are applicable instead
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * of those above. If you wish to allow use of your version of this file only
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * under the terms of either the GPL or the LGPL, and not to allow others to
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * use your version of this file under the terms of the MPL, indicate your
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * decision by deleting the provisions above and replace them with the notice
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * and other provisions required by the GPL or the LGPL. If you do not delete
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the provisions above, a recipient may use your version of this file under
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * the terms of any one of the MPL, the GPL or the LGPL.
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) *
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * ***** END LICENSE BLOCK ***** */
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <pk11pub.h>
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <pkcs12.h>
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <p12plcy.h>
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <secerr.h>
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/lazy_instance.h"
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
47868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "crypto/nss_util_internal.h"
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "net/cert/x509_certificate.h"
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace mozilla_security_manager {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// unicodeToItem
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For the NSS PKCS#12 library, must convert PRUnichars (shorts) to
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a buffer of octets.  Must handle byte order correctly.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO: Is there a Mozilla way to do this?  In the string lib?
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void unicodeToItem(const PRUnichar *uni, SECItem *item)
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int len = 0;
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (uni[len++] != 0);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECITEM_AllocItem(NULL, item, sizeof(PRUnichar) * len);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifdef IS_LITTLE_ENDIAN
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int i = 0;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (i=0; i<len; i++) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    item->data[2*i  ] = (unsigned char )(uni[i] << 8);
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    item->data[2*i+1] = (unsigned char )(uni[i]);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(item->data, uni, item->len);
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// write_export_data
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// write bytes to the exported PKCS#12 data buffer
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void write_export_data(void* arg, const char* buf, unsigned long len) {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string* dest = reinterpret_cast<std::string*>(arg);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dest->append(buf, len);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// nickname_collision
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// what to do when the nickname collides with one already in the db.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Based on P12U_NicknameCollisionCallback from nss/cmd/pk12util/pk12util.c
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SECItem* PR_CALLBACK
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)nickname_collision(SECItem *old_nick, PRBool *cancel, void *wincx)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char           *nick     = NULL;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECItem        *ret_nick = NULL;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CERTCertificate* cert    = (CERTCertificate*)wincx;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!cancel || !cert) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // pk12util calls this error user cancelled?
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!old_nick)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VLOG(1) << "no nickname for cert in PKCS12 file.";
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  nick = CERT_MakeCANickname(cert);
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!nick) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if(old_nick && old_nick->data && old_nick->len &&
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     PORT_Strlen(nick) == old_nick->len &&
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)     !PORT_Strncmp((char *)old_nick->data, nick, old_nick->len)) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PORT_Free(nick);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PORT_SetError(SEC_ERROR_IO);
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VLOG(1) << "using nickname " << nick;
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ret_nick = PORT_ZNew(SECItem);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if(ret_nick == NULL) {
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PORT_Free(nick);
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ret_nick->data = (unsigned char *)nick;
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ret_nick->len = PORT_Strlen(nick);
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ret_nick;
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// pip_ucs2_ascii_conversion_fn
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// required to be set by NSS (to do PKCS#12), but since we've already got
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// unicode make this a no-op.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)PRBool
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)pip_ucs2_ascii_conversion_fn(PRBool toUnicode,
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             unsigned char *inBuf,
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             unsigned int inBufLen,
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             unsigned char *outBuf,
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             unsigned int maxOutBufLen,
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             unsigned int *outBufLen,
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             PRBool swapBytes)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_GE(maxOutBufLen, inBufLen);
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // do a no-op, since I've already got Unicode.  Hah!
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *outBufLen = inBufLen;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  memcpy(outBuf, inBuf, inBufLen);
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return PR_TRUE;
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Based on nsPKCS12Blob::ImportFromFileHelper.
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)nsPKCS12Blob_ImportHelper(const char* pkcs12_data,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          size_t pkcs12_len,
151c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                          const base::string16& password,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          bool is_extractable,
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          bool try_zero_length_secitem,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          PK11SlotInfo *slot,
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          net::CertificateList* imported_certs)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(pkcs12_data);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(slot);
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int import_result = net::ERR_PKCS12_IMPORT_FAILED;
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECStatus srv = SECSuccess;
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SEC_PKCS12DecoderContext *dcx = NULL;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECItem unicodePw;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECItem attribute_value;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CK_BBOOL attribute_data = CK_FALSE;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const SEC_PKCS12DecoderItem* decoder_item = NULL;
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unicodePw.type = siBuffer;
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unicodePw.len = 0;
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unicodePw.data = NULL;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!try_zero_length_secitem) {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    unicodeToItem(password.c_str(), &unicodePw);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initialize the decoder
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dcx = SEC_PKCS12DecoderStart(&unicodePw, slot,
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               // wincx
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               NULL,
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               // dOpen, dClose, dRead, dWrite, dArg: NULL
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               // specifies default impl using memory buffer.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               NULL, NULL, NULL, NULL, NULL);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!dcx) {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    srv = SECFailure;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    goto finish;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // feed input to the decoder
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srv = SEC_PKCS12DecoderUpdate(dcx,
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                (unsigned char*)pkcs12_data,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                pkcs12_len);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv) goto finish;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // verify the blob
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srv = SEC_PKCS12DecoderVerify(dcx);
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv) goto finish;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // validate bags
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv) goto finish;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // import certificate and key
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srv = SEC_PKCS12DecoderImportBags(dcx);
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv) goto finish;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attribute_value.data = &attribute_data;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  attribute_value.len = sizeof(attribute_data);
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srv = SEC_PKCS12DecoderIterateInit(dcx);
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv) goto finish;
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (imported_certs)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    imported_certs->clear();
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Collect the list of decoded certificates, and mark private keys
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // non-extractable if needed.
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (SEC_PKCS12DecoderIterateNext(dcx, &decoder_item) == SECSuccess) {
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (decoder_item->type != SEC_OID_PKCS12_V1_CERT_BAG_ID)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CERTCertificate* cert = PK11_FindCertFromDERCertItem(
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        slot, decoder_item->der,
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NULL);  // wincx
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!cert) {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Could not grab a handle to the certificate in the slot "
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "from the corresponding PKCS#12 DER certificate.";
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Add the cert to the list
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (imported_certs) {
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Empty list of intermediates.
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::X509Certificate::OSCertHandles intermediates;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      imported_certs->push_back(
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          net::X509Certificate::CreateFromHandle(cert, intermediates));
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Once we have determined that the imported certificate has an
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // associated private key too, only then can we mark the key as
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // non-extractable.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!decoder_item->hasKey) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CERT_DestroyCertificate(cert);
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Iterate through all the imported PKCS12 items and mark any accompanying
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // private keys as non-extractable.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!is_extractable) {
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SECKEYPrivateKey* privKey = PK11_FindPrivateKeyFromCert(slot, cert,
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                              NULL);  // wincx
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (privKey) {
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Mark the private key as non-extractable.
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        srv = PK11_WriteRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE,
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     &attribute_value);
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SECKEY_DestroyPrivateKey(privKey);
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (srv) {
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          LOG(ERROR) << "Could not set CKA_EXTRACTABLE attribute on private "
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     << "key.";
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          CERT_DestroyCertificate(cert);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break;
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CERT_DestroyCertificate(cert);
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (srv) goto finish;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  import_result = net::OK;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)finish:
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If srv != SECSuccess, NSS probably set a specific error code.
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We should use that error code instead of inventing a new one
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // for every error possible.
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv != SECSuccess) {
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int error = PORT_GetError();
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "PKCS#12 import failed with error " << error;
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (error) {
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_BAD_PASSWORD:
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT:
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        import_result = net::ERR_PKCS12_IMPORT_BAD_PASSWORD;
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_PKCS12_INVALID_MAC:
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        import_result = net::ERR_PKCS12_IMPORT_INVALID_MAC;
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_BAD_DER:
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_PKCS12_DECODING_PFX:
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        import_result = net::ERR_PKCS12_IMPORT_INVALID_FILE;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM:
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE:
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM:
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      case SEC_ERROR_PKCS12_UNSUPPORTED_VERSION:
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        import_result = net::ERR_PKCS12_IMPORT_UNSUPPORTED;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      default:
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        import_result = net::ERR_PKCS12_IMPORT_FAILED;
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Finish the decoder
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (dcx)
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12DecoderFinish(dcx);
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECITEM_ZfreeItem(&unicodePw, PR_FALSE);
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return import_result;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Attempt to read the CKA_EXTRACTABLE attribute on a private key inside
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a token. On success, store the attribute in |extractable| and return
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// SECSuccess.
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)SECStatus
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)isExtractable(SECKEYPrivateKey *privKey, PRBool *extractable)
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECItem value;
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECStatus rv;
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  rv=PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, &value);
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv != SECSuccess)
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return rv;
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if ((value.len == 1) && (value.data != NULL))
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *extractable = !!(*(CK_BBOOL*)value.data);
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rv = SECFailure;
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECITEM_FreeItem(&value, PR_FALSE);
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class PKCS12InitSingleton {
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // From the PKCS#12 section of nsNSSComponent::InitializeNSS in
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // nsNSSComponent.cpp.
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PKCS12InitSingleton() {
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Enable ciphers for PKCS#12
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Set no-op ascii-ucs2 conversion function to work around weird NSS
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // interface.  Thankfully, PKCS12 appears to be the only thing in NSS that
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // uses PORT_UCS2_ASCIIConversion, so this doesn't break anything else.
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PORT_SetUCS2_ASCIIConversionFunction(pip_ucs2_ascii_conversion_fn);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Leaky so it can be initialized on worker threads and because there is no
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// cleanup necessary.
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)static base::LazyInstance<PKCS12InitSingleton>::Leaky g_pkcs12_init_singleton =
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LAZY_INSTANCE_INITIALIZER;
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void EnsurePKCS12Init() {
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  g_pkcs12_init_singleton.Get();
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Based on nsPKCS12Blob::ImportFromFile.
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int nsPKCS12Blob_Import(PK11SlotInfo* slot,
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        const char* pkcs12_data,
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        size_t pkcs12_len,
358c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                        const base::string16& password,
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        bool is_extractable,
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        net::CertificateList* imported_certs) {
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     is_extractable, false, slot,
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                     imported_certs);
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // When the user entered a zero length password:
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   An empty password should be represented as an empty
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   string (a SECItem that contains a single terminating
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   NULL UTF16 character), but some applications use a
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   zero length SECItem.
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   We try both variations, zero length item and empty string,
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   without giving a user prompt when trying the different empty password
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  //   flavors.
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) {
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                   is_extractable, true, slot, imported_certs);
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return rv;
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Based on nsPKCS12Blob::ExportToFile
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Having already loaded the certs, form them into a blob (loading the keys
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// also), encode the blob, and stuff it into the file.
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// TODO: handle slots correctly
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       mirror "slotToUse" behavior from PSM 1.x
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       verify the cert array to start off with?
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//       set appropriate error codes
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)nsPKCS12Blob_Export(std::string* output,
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const net::CertificateList& certs,
393c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                    const base::string16& password)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles){
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int return_count = 0;
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECStatus srv = SECSuccess;
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SEC_PKCS12ExportContext *ecx = NULL;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SEC_PKCS12SafeInfo *certSafe = NULL, *keySafe = NULL;
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECItem unicodePw;
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unicodePw.type = siBuffer;
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unicodePw.len = 0;
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unicodePw.data = NULL;
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int numCertsExported = 0;
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // get file password (unicode)
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unicodeToItem(password.c_str(), &unicodePw);
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // what about slotToUse in psm 1.x ???
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // create export context
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ecx = SEC_PKCS12CreateExportContext(NULL, NULL, NULL /*slot*/, NULL);
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!ecx) {
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    srv = SECFailure;
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    goto finish;
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // add password integrity
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1);
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv) goto finish;
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i=0; i<certs.size(); i++) {
421eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    DCHECK(certs[i].get());
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    CERTCertificate* nssCert = certs[i]->os_cert_handle();
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(nssCert);
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We only allow certificate and private key extraction if the corresponding
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // CKA_EXTRACTABLE private key attribute is set to CK_TRUE. Most hardware
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // tokens including smartcards enforce this behavior. An internal (soft)
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // token may ignore this attribute (and hence still be able to export) but
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we still refuse to attempt an export.
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // In addition, some tokens may not support this attribute, in which case
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we still attempt the export and let the token implementation dictate
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the export behavior.
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (nssCert->slot) {
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot,
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                      nssCert,
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                      NULL);  // wincx
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       if (privKey) {
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        PRBool privKeyIsExtractable = PR_FALSE;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SECStatus rv = isExtractable(privKey, &privKeyIsExtractable);
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SECKEY_DestroyPrivateKey(privKey);
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (rv == SECSuccess && !privKeyIsExtractable) {
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          LOG(ERROR) << "Private key is not extractable";
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          continue;
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // XXX this is why, to verify the slot is the same
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // PK11_FindObjectForCert(nssCert, NULL, slot);
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // create the cert and key safes
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx);
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      certSafe = keySafe;
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      certSafe = SEC_PKCS12CreatePasswordPrivSafe(ecx, &unicodePw,
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!certSafe || !keySafe) {
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "!certSafe || !keySafe " << certSafe << " " << keySafe;
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      srv = SECFailure;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      goto finish;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // add the cert and key to the blob
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    srv = SEC_PKCS12AddCertAndKey(ecx, certSafe, NULL, nssCert,
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  CERT_GetDefaultCertDB(),
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  keySafe, NULL, PR_TRUE, &unicodePw,
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC);
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (srv) goto finish;
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++numCertsExported;
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!numCertsExported) goto finish;
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // encode and write
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  srv = SEC_PKCS12Encode(ecx, write_export_data, output);
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv) goto finish;
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return_count = numCertsExported;
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)finish:
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (srv)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "PKCS#12 export failed with error " << PORT_GetError();
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (ecx)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SEC_PKCS12DestroyExportContext(ecx);
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SECITEM_ZfreeItem(&unicodePw, PR_FALSE);
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return return_count;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace mozilla_security_manager
489