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