- add sources.
[platform/framework/web/crosswalk.git] / src / net / third_party / mozilla_security_manager / nsPKCS12Blob.cpp
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 #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 static base::LazyInstance<PKCS12InitSingleton> g_pkcs12_init_singleton =
344     LAZY_INSTANCE_INITIALIZER;
345
346 }  // namespace
347
348 void EnsurePKCS12Init() {
349   g_pkcs12_init_singleton.Get();
350 }
351
352 // Based on nsPKCS12Blob::ImportFromFile.
353 int nsPKCS12Blob_Import(PK11SlotInfo* slot,
354                         const char* pkcs12_data,
355                         size_t pkcs12_len,
356                         const base::string16& password,
357                         bool is_extractable,
358                         net::CertificateList* imported_certs) {
359
360   int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
361                                      is_extractable, false, slot,
362                                      imported_certs);
363
364   // When the user entered a zero length password:
365   //   An empty password should be represented as an empty
366   //   string (a SECItem that contains a single terminating
367   //   NULL UTF16 character), but some applications use a
368   //   zero length SECItem.
369   //   We try both variations, zero length item and empty string,
370   //   without giving a user prompt when trying the different empty password
371   //   flavors.
372   if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) {
373     rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password,
374                                    is_extractable, true, slot, imported_certs);
375   }
376   return rv;
377 }
378
379 // Based on nsPKCS12Blob::ExportToFile
380 //
381 // Having already loaded the certs, form them into a blob (loading the keys
382 // also), encode the blob, and stuff it into the file.
383 //
384 // TODO: handle slots correctly
385 //       mirror "slotToUse" behavior from PSM 1.x
386 //       verify the cert array to start off with?
387 //       set appropriate error codes
388 int
389 nsPKCS12Blob_Export(std::string* output,
390                     const net::CertificateList& certs,
391                     const base::string16& password)
392 {
393   int return_count = 0;
394   SECStatus srv = SECSuccess;
395   SEC_PKCS12ExportContext *ecx = NULL;
396   SEC_PKCS12SafeInfo *certSafe = NULL, *keySafe = NULL;
397   SECItem unicodePw;
398   unicodePw.type = siBuffer;
399   unicodePw.len = 0;
400   unicodePw.data = NULL;
401
402   int numCertsExported = 0;
403
404   // get file password (unicode)
405   unicodeToItem(password.c_str(), &unicodePw);
406
407   // what about slotToUse in psm 1.x ???
408   // create export context
409   ecx = SEC_PKCS12CreateExportContext(NULL, NULL, NULL /*slot*/, NULL);
410   if (!ecx) {
411     srv = SECFailure;
412     goto finish;
413   }
414   // add password integrity
415   srv = SEC_PKCS12AddPasswordIntegrity(ecx, &unicodePw, SEC_OID_SHA1);
416   if (srv) goto finish;
417
418   for (size_t i=0; i<certs.size(); i++) {
419     DCHECK(certs[i].get());
420     CERTCertificate* nssCert = certs[i]->os_cert_handle();
421     DCHECK(nssCert);
422
423     // We only allow certificate and private key extraction if the corresponding
424     // CKA_EXTRACTABLE private key attribute is set to CK_TRUE. Most hardware
425     // tokens including smartcards enforce this behavior. An internal (soft)
426     // token may ignore this attribute (and hence still be able to export) but
427     // we still refuse to attempt an export.
428     // In addition, some tokens may not support this attribute, in which case
429     // we still attempt the export and let the token implementation dictate
430     // the export behavior.
431     if (nssCert->slot) {
432       SECKEYPrivateKey *privKey=PK11_FindKeyByDERCert(nssCert->slot,
433                                                       nssCert,
434                                                       NULL);  // wincx
435        if (privKey) {
436         PRBool privKeyIsExtractable = PR_FALSE;
437         SECStatus rv = isExtractable(privKey, &privKeyIsExtractable);
438         SECKEY_DestroyPrivateKey(privKey);
439
440         if (rv == SECSuccess && !privKeyIsExtractable) {
441           LOG(ERROR) << "Private key is not extractable";
442           continue;
443         }
444       }
445     }
446
447     // XXX this is why, to verify the slot is the same
448     // PK11_FindObjectForCert(nssCert, NULL, slot);
449     // create the cert and key safes
450     keySafe = SEC_PKCS12CreateUnencryptedSafe(ecx);
451     if (!SEC_PKCS12IsEncryptionAllowed() || PK11_IsFIPS()) {
452       certSafe = keySafe;
453     } else {
454       certSafe = SEC_PKCS12CreatePasswordPrivSafe(ecx, &unicodePw,
455                            SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
456     }
457     if (!certSafe || !keySafe) {
458       LOG(ERROR) << "!certSafe || !keySafe " << certSafe << " " << keySafe;
459       srv = SECFailure;
460       goto finish;
461     }
462     // add the cert and key to the blob
463     srv = SEC_PKCS12AddCertAndKey(ecx, certSafe, NULL, nssCert,
464                                   CERT_GetDefaultCertDB(),
465                                   keySafe, NULL, PR_TRUE, &unicodePw,
466                       SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC);
467     if (srv) goto finish;
468     ++numCertsExported;
469   }
470
471   if (!numCertsExported) goto finish;
472
473   // encode and write
474   srv = SEC_PKCS12Encode(ecx, write_export_data, output);
475   if (srv) goto finish;
476   return_count = numCertsExported;
477 finish:
478   if (srv)
479     LOG(ERROR) << "PKCS#12 export failed with error " << PORT_GetError();
480   if (ecx)
481     SEC_PKCS12DestroyExportContext(ecx);
482   SECITEM_ZfreeItem(&unicodePw, PR_FALSE);
483   return return_count;
484 }
485
486 }  // namespace mozilla_security_manager