OLD | NEW |
1 /* ***** BEGIN LICENSE BLOCK ***** | 1 /* ***** BEGIN LICENSE BLOCK ***** |
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
3 * | 3 * |
4 * The contents of this file are subject to the Mozilla Public License Version | 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 | 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 | 6 * the License. You may obtain a copy of the License at |
7 * http://www.mozilla.org/MPL/ | 7 * http://www.mozilla.org/MPL/ |
8 * | 8 * |
9 * Software distributed under the License is distributed on an "AS IS" basis, | 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 | 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 #include "net/base/x509_certificate.h" | 50 #include "net/base/x509_certificate.h" |
51 | 51 |
52 namespace mozilla_security_manager { | 52 namespace mozilla_security_manager { |
53 | 53 |
54 namespace { | 54 namespace { |
55 | 55 |
56 // unicodeToItem | 56 // unicodeToItem |
57 // | 57 // |
58 // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to | 58 // For the NSS PKCS#12 library, must convert PRUnichars (shorts) to |
59 // a buffer of octets. Must handle byte order correctly. | 59 // a buffer of octets. Must handle byte order correctly. |
60 // TODO: Is there a mozilla way to do this? In the string lib? | 60 // TODO: Is there a Mozilla way to do this? In the string lib? |
61 void unicodeToItem(const PRUnichar *uni, SECItem *item) | 61 void unicodeToItem(const PRUnichar *uni, SECItem *item) |
62 { | 62 { |
63 int len = 0; | 63 int len = 0; |
64 while (uni[len++] != 0); | 64 while (uni[len++] != 0); |
65 SECITEM_AllocItem(NULL, item, sizeof(PRUnichar) * len); | 65 SECITEM_AllocItem(NULL, item, sizeof(PRUnichar) * len); |
66 #ifdef IS_LITTLE_ENDIAN | 66 #ifdef IS_LITTLE_ENDIAN |
67 int i = 0; | 67 int i = 0; |
68 for (i=0; i<len; i++) { | 68 for (i=0; i<len; i++) { |
69 item->data[2*i ] = (unsigned char )(uni[i] << 8); | 69 item->data[2*i ] = (unsigned char )(uni[i] << 8); |
70 item->data[2*i+1] = (unsigned char )(uni[i]); | 70 item->data[2*i+1] = (unsigned char )(uni[i]); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 PRBool | 131 PRBool |
132 pip_ucs2_ascii_conversion_fn(PRBool toUnicode, | 132 pip_ucs2_ascii_conversion_fn(PRBool toUnicode, |
133 unsigned char *inBuf, | 133 unsigned char *inBuf, |
134 unsigned int inBufLen, | 134 unsigned int inBufLen, |
135 unsigned char *outBuf, | 135 unsigned char *outBuf, |
136 unsigned int maxOutBufLen, | 136 unsigned int maxOutBufLen, |
137 unsigned int *outBufLen, | 137 unsigned int *outBufLen, |
138 PRBool swapBytes) | 138 PRBool swapBytes) |
139 { | 139 { |
140 CHECK_GE(maxOutBufLen, inBufLen); | 140 CHECK_GE(maxOutBufLen, inBufLen); |
141 // do a no-op, since I've already got unicode. Hah! | 141 // do a no-op, since I've already got Unicode. Hah! |
142 *outBufLen = inBufLen; | 142 *outBufLen = inBufLen; |
143 memcpy(outBuf, inBuf, inBufLen); | 143 memcpy(outBuf, inBuf, inBufLen); |
144 return PR_TRUE; | 144 return PR_TRUE; |
145 } | 145 } |
146 | 146 |
147 // Based on nsPKCS12Blob::ImportFromFileHelper. | 147 // Based on nsPKCS12Blob::ImportFromFileHelper. |
148 int | 148 int |
149 nsPKCS12Blob_ImportHelper(const char* pkcs12_data, | 149 nsPKCS12Blob_ImportHelper(const char* pkcs12_data, |
150 size_t pkcs12_len, | 150 size_t pkcs12_len, |
151 const string16& password, | 151 const string16& password, |
152 bool is_extractable, | 152 bool is_extractable, |
153 bool try_zero_length_secitem, | 153 bool try_zero_length_secitem, |
154 PK11SlotInfo *slot) | 154 PK11SlotInfo *slot, |
| 155 net::CertificateList* imported_certs) |
155 { | 156 { |
156 DCHECK(pkcs12_data); | 157 DCHECK(pkcs12_data); |
157 DCHECK(slot); | 158 DCHECK(slot); |
158 int import_result = net::ERR_PKCS12_IMPORT_FAILED; | 159 int import_result = net::ERR_PKCS12_IMPORT_FAILED; |
159 SECStatus srv = SECSuccess; | 160 SECStatus srv = SECSuccess; |
160 SEC_PKCS12DecoderContext *dcx = NULL; | 161 SEC_PKCS12DecoderContext *dcx = NULL; |
161 SECItem unicodePw; | 162 SECItem unicodePw; |
| 163 SECItem attribute_value; |
| 164 CK_BBOOL attribute_data = CK_FALSE; |
| 165 const SEC_PKCS12DecoderItem* decoder_item = NULL; |
| 166 |
162 unicodePw.type = siBuffer; | 167 unicodePw.type = siBuffer; |
163 unicodePw.len = 0; | 168 unicodePw.len = 0; |
164 unicodePw.data = NULL; | 169 unicodePw.data = NULL; |
165 if (!try_zero_length_secitem) { | 170 if (!try_zero_length_secitem) { |
166 unicodeToItem(password.c_str(), &unicodePw); | 171 unicodeToItem(password.c_str(), &unicodePw); |
167 } | 172 } |
168 | 173 |
169 // Initialize the decoder | 174 // Initialize the decoder |
170 dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, | 175 dcx = SEC_PKCS12DecoderStart(&unicodePw, slot, |
171 // wincx | 176 // wincx |
172 NULL, | 177 NULL, |
173 // dOpen, dClose, dRead, dWrite, dArg: NULL | 178 // dOpen, dClose, dRead, dWrite, dArg: NULL |
174 // specifies default impl using memory buffer. | 179 // specifies default impl using memory buffer. |
175 NULL, NULL, NULL, NULL, NULL); | 180 NULL, NULL, NULL, NULL, NULL); |
176 if (!dcx) { | 181 if (!dcx) { |
177 srv = SECFailure; | 182 srv = SECFailure; |
178 goto finish; | 183 goto finish; |
179 } | 184 } |
180 // feed input to the decoder | 185 // feed input to the decoder |
181 srv = SEC_PKCS12DecoderUpdate(dcx, | 186 srv = SEC_PKCS12DecoderUpdate(dcx, |
182 (unsigned char*)pkcs12_data, | 187 (unsigned char*)pkcs12_data, |
183 pkcs12_len); | 188 pkcs12_len); |
184 if (srv) goto finish; | 189 if (srv) goto finish; |
185 // verify the blob | 190 // verify the blob |
186 srv = SEC_PKCS12DecoderVerify(dcx); | 191 srv = SEC_PKCS12DecoderVerify(dcx); |
187 if (srv) goto finish; | 192 if (srv) goto finish; |
188 // validate bags | 193 // validate bags |
189 srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision); | 194 srv = SEC_PKCS12DecoderValidateBags(dcx, nickname_collision); |
190 if (srv) goto finish; | 195 if (srv) goto finish; |
191 // import cert and key | 196 // import certificate and key |
192 srv = SEC_PKCS12DecoderImportBags(dcx); | 197 srv = SEC_PKCS12DecoderImportBags(dcx); |
193 if (srv) goto finish; | 198 if (srv) goto finish; |
194 | 199 |
195 if (!is_extractable) { | 200 attribute_value.data = &attribute_data; |
196 SECItem attribute_value; | 201 attribute_value.len = sizeof(attribute_data); |
197 CK_BBOOL attribute_data = CK_FALSE; | |
198 attribute_value.data = &attribute_data; | |
199 attribute_value.len = sizeof(attribute_data); | |
200 | 202 |
201 srv = SEC_PKCS12DecoderIterateInit(dcx); | 203 srv = SEC_PKCS12DecoderIterateInit(dcx); |
202 if (srv) goto finish; | 204 if (srv) goto finish; |
203 | 205 |
204 const SEC_PKCS12DecoderItem* decoder_item = NULL; | 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 |
205 // Iterate through all the imported PKCS12 items and mark any accompanying | 240 // Iterate through all the imported PKCS12 items and mark any accompanying |
206 // private keys as unextractable. | 241 // private keys as non-extractable. |
207 while (SEC_PKCS12DecoderIterateNext(dcx, &decoder_item) == SECSuccess) { | 242 if (!is_extractable) { |
208 if (decoder_item->type != SEC_OID_PKCS12_V1_CERT_BAG_ID) | |
209 continue; | |
210 if (!decoder_item->hasKey) | |
211 continue; | |
212 | |
213 // Once we have determined that the imported certificate has an | |
214 // associated private key too, only then can we mark the key as | |
215 // unextractable. | |
216 CERTCertificate* cert = PK11_FindCertFromDERCertItem( | |
217 slot, decoder_item->der, | |
218 NULL); // wincx | |
219 if (!cert) { | |
220 LOG(ERROR) << "Could not grab a handle to the certificate in the slot " | |
221 << "from the corresponding PKCS#12 DER certificate."; | |
222 continue; | |
223 } | |
224 SECKEYPrivateKey* privKey = PK11_FindPrivateKeyFromCert(slot, cert, | 243 SECKEYPrivateKey* privKey = PK11_FindPrivateKeyFromCert(slot, cert, |
225 NULL); // wincx | 244 NULL); // wincx |
226 CERT_DestroyCertificate(cert); | |
227 if (privKey) { | 245 if (privKey) { |
228 // Mark the private key as unextractable. | 246 // Mark the private key as non-extractable. |
229 srv = PK11_WriteRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, | 247 srv = PK11_WriteRawAttribute(PK11_TypePrivKey, privKey, CKA_EXTRACTABLE, |
230 &attribute_value); | 248 &attribute_value); |
231 SECKEY_DestroyPrivateKey(privKey); | 249 SECKEY_DestroyPrivateKey(privKey); |
232 if (srv) { | 250 if (srv) { |
233 LOG(ERROR) << "Could not set CKA_EXTRACTABLE attribute on private " | 251 LOG(ERROR) << "Could not set CKA_EXTRACTABLE attribute on private " |
234 << "key."; | 252 << "key."; |
| 253 CERT_DestroyCertificate(cert); |
235 break; | 254 break; |
236 } | 255 } |
237 } | 256 } |
238 } | 257 } |
| 258 CERT_DestroyCertificate(cert); |
239 if (srv) goto finish; | 259 if (srv) goto finish; |
240 } | 260 } |
241 | |
242 import_result = net::OK; | 261 import_result = net::OK; |
243 finish: | 262 finish: |
244 // If srv != SECSuccess, NSS probably set a specific error code. | 263 // If srv != SECSuccess, NSS probably set a specific error code. |
245 // We should use that error code instead of inventing a new one | 264 // We should use that error code instead of inventing a new one |
246 // for every error possible. | 265 // for every error possible. |
247 if (srv != SECSuccess) { | 266 if (srv != SECSuccess) { |
248 int error = PORT_GetError(); | 267 int error = PORT_GetError(); |
249 LOG(ERROR) << "PKCS#12 import failed with error " << error; | 268 LOG(ERROR) << "PKCS#12 import failed with error " << error; |
250 switch (error) { | 269 switch (error) { |
251 case SEC_ERROR_BAD_PASSWORD: | 270 case SEC_ERROR_BAD_PASSWORD: |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
328 | 347 |
329 void EnsurePKCS12Init() { | 348 void EnsurePKCS12Init() { |
330 g_pkcs12_init_singleton.Get(); | 349 g_pkcs12_init_singleton.Get(); |
331 } | 350 } |
332 | 351 |
333 // Based on nsPKCS12Blob::ImportFromFile. | 352 // Based on nsPKCS12Blob::ImportFromFile. |
334 int nsPKCS12Blob_Import(PK11SlotInfo* slot, | 353 int nsPKCS12Blob_Import(PK11SlotInfo* slot, |
335 const char* pkcs12_data, | 354 const char* pkcs12_data, |
336 size_t pkcs12_len, | 355 size_t pkcs12_len, |
337 const string16& password, | 356 const string16& password, |
338 bool is_extractable) { | 357 bool is_extractable, |
| 358 net::CertificateList* imported_certs) { |
339 | 359 |
340 int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, | 360 int rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, |
341 is_extractable, false, slot); | 361 is_extractable, false, slot, |
| 362 imported_certs); |
342 | 363 |
343 // When the user entered a zero length password: | 364 // When the user entered a zero length password: |
344 // An empty password should be represented as an empty | 365 // An empty password should be represented as an empty |
345 // string (a SECItem that contains a single terminating | 366 // string (a SECItem that contains a single terminating |
346 // NULL UTF16 character), but some applications use a | 367 // NULL UTF16 character), but some applications use a |
347 // zero length SECItem. | 368 // zero length SECItem. |
348 // We try both variations, zero length item and empty string, | 369 // We try both variations, zero length item and empty string, |
349 // without giving a user prompt when trying the different empty password | 370 // without giving a user prompt when trying the different empty password |
350 // flavors. | 371 // flavors. |
351 if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) { | 372 if (rv == net::ERR_PKCS12_IMPORT_BAD_PASSWORD && password.empty()) { |
352 rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, | 373 rv = nsPKCS12Blob_ImportHelper(pkcs12_data, pkcs12_len, password, |
353 is_extractable, true, slot); | 374 is_extractable, true, slot, imported_certs); |
354 } | 375 } |
355 return rv; | 376 return rv; |
356 } | 377 } |
357 | 378 |
358 // Based on nsPKCS12Blob::ExportToFile | 379 // Based on nsPKCS12Blob::ExportToFile |
359 // | 380 // |
360 // Having already loaded the certs, form them into a blob (loading the keys | 381 // Having already loaded the certs, form them into a blob (loading the keys |
361 // also), encode the blob, and stuff it into the file. | 382 // also), encode the blob, and stuff it into the file. |
362 // | 383 // |
363 // TODO: handle slots correctly | 384 // TODO: handle slots correctly |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
456 finish: | 477 finish: |
457 if (srv) | 478 if (srv) |
458 LOG(ERROR) << "PKCS#12 export failed with error " << PORT_GetError(); | 479 LOG(ERROR) << "PKCS#12 export failed with error " << PORT_GetError(); |
459 if (ecx) | 480 if (ecx) |
460 SEC_PKCS12DestroyExportContext(ecx); | 481 SEC_PKCS12DestroyExportContext(ecx); |
461 SECITEM_ZfreeItem(&unicodePw, PR_FALSE); | 482 SECITEM_ZfreeItem(&unicodePw, PR_FALSE); |
462 return return_count; | 483 return return_count; |
463 } | 484 } |
464 | 485 |
465 } // namespace mozilla_security_manager | 486 } // namespace mozilla_security_manager |
OLD | NEW |