]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2002-2010 Apple Inc. All Rights Reserved. | |
3 | * | |
4 | * The contents of this file constitute Original Code as defined in and are | |
5 | * subject to the Apple Public Source License Version 1.2 (the 'License'). | |
6 | * You may not use this file except in compliance with the License. Please obtain | |
7 | * a copy of the License at http://www.apple.com/publicsource and read it before | |
8 | * using this file. | |
9 | * | |
10 | * This Original Code and all software distributed under the License are | |
11 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS | |
12 | * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT | |
13 | * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR | |
14 | * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the | |
15 | * specific language governing rights and limitations under the License. | |
16 | */ | |
17 | ||
18 | ||
19 | /* | |
20 | * CLFieldsCommon.h - get/set/free routines common to certs and CRLs | |
21 | */ | |
22 | ||
23 | #include "CLFieldsCommon.h" | |
24 | #include "clNameUtils.h" | |
25 | #include "clNssUtils.h" | |
26 | #include "AppleX509CLSession.h" | |
27 | #include <Security/cssmapple.h> | |
28 | #include <Security/oidscert.h> | |
29 | #include <Security/nameTemplates.h> | |
30 | #include <Security/certExtensionTemplates.h> | |
31 | #include <Security/SecAsn1Templates.h> | |
32 | ||
33 | /* | |
34 | * Table to map an OID to the info needed to decode the | |
35 | * associated extension | |
36 | */ | |
37 | typedef struct { | |
38 | const CSSM_OID &oid; | |
39 | unsigned nssObjLen; | |
40 | const SecAsn1Template *templ; | |
41 | } NssExtenInfo; | |
42 | ||
43 | static const NssExtenInfo nssExtenInfo[] = { | |
44 | { CSSMOID_KeyUsage, | |
45 | sizeof(CSSM_DATA), | |
46 | kSecAsn1KeyUsageTemplate }, | |
47 | { CSSMOID_BasicConstraints, | |
48 | sizeof(NSS_BasicConstraints), | |
49 | kSecAsn1BasicConstraintsTemplate }, | |
50 | { CSSMOID_ExtendedKeyUsage, | |
51 | sizeof(NSS_ExtKeyUsage), | |
52 | kSecAsn1ExtKeyUsageTemplate }, | |
53 | { CSSMOID_SubjectKeyIdentifier, | |
54 | sizeof(CSSM_DATA), | |
55 | kSecAsn1SubjectKeyIdTemplate }, | |
56 | { CSSMOID_AuthorityKeyIdentifier, | |
57 | sizeof(NSS_AuthorityKeyId), | |
58 | kSecAsn1AuthorityKeyIdTemplate }, | |
59 | { CSSMOID_SubjectAltName, | |
60 | sizeof(NSS_GeneralNames), | |
61 | kSecAsn1GeneralNamesTemplate }, | |
62 | { CSSMOID_IssuerAltName, | |
63 | sizeof(NSS_GeneralNames), | |
64 | kSecAsn1GeneralNamesTemplate }, | |
65 | { CSSMOID_CertificatePolicies, | |
66 | sizeof(NSS_CertPolicies), | |
67 | kSecAsn1CertPoliciesTemplate }, | |
68 | { CSSMOID_NetscapeCertType, | |
69 | sizeof(CSSM_DATA), | |
70 | kSecAsn1NetscapeCertTypeTemplate }, | |
71 | { CSSMOID_CrlDistributionPoints, | |
72 | sizeof(NSS_CRLDistributionPoints), | |
73 | kSecAsn1CRLDistributionPointsTemplate }, | |
74 | { CSSMOID_CertIssuer, | |
75 | sizeof(NSS_GeneralNames), | |
76 | kSecAsn1GeneralNamesTemplate }, | |
77 | { CSSMOID_AuthorityInfoAccess, | |
78 | sizeof(NSS_AuthorityInfoAccess), | |
79 | kSecAsn1AuthorityInfoAccessTemplate }, | |
80 | { CSSMOID_SubjectInfoAccess, | |
81 | sizeof(NSS_AuthorityInfoAccess), | |
82 | kSecAsn1AuthorityInfoAccessTemplate }, | |
83 | /* CRL extensions */ | |
84 | { CSSMOID_CrlNumber, | |
85 | sizeof(CSSM_DATA), | |
86 | kSecAsn1IntegerTemplate }, | |
87 | { CSSMOID_IssuingDistributionPoint, | |
88 | sizeof(NSS_IssuingDistributionPoint), | |
89 | kSecAsn1IssuingDistributionPointTemplate }, | |
90 | { CSSMOID_HoldInstructionCode, | |
91 | sizeof(CSSM_OID), | |
92 | kSecAsn1ObjectIDTemplate }, | |
93 | { CSSMOID_CrlReason, | |
94 | sizeof(CSSM_DATA), | |
95 | kSecAsn1EnumeratedTemplate }, | |
96 | { CSSMOID_DeltaCrlIndicator, | |
97 | sizeof(CSSM_DATA), | |
98 | kSecAsn1IntegerTemplate }, | |
99 | { CSSMOID_InvalidityDate, | |
100 | sizeof(CSSM_DATA), | |
101 | kSecAsn1GeneralizedTimeTemplate }, | |
102 | { CSSMOID_QC_Statements, | |
103 | sizeof(NSS_QC_Statements), | |
104 | kSecAsn1QC_StatementsTemplate }, | |
105 | { CSSMOID_NameConstraints, | |
106 | sizeof(NSS_NameConstraints), | |
107 | kSecAsn1NameConstraintsTemplate }, | |
108 | { CSSMOID_PolicyMappings, | |
109 | sizeof(NSS_PolicyMappings), | |
110 | kSecAsn1PolicyMappingsTemplate }, | |
111 | { CSSMOID_PolicyConstraints, | |
112 | sizeof(NSS_PolicyConstraints), | |
113 | kSecAsn1PolicyConstraintsTemplate }, | |
114 | { CSSMOID_InhibitAnyPolicy, | |
115 | sizeof(CSSM_DATA), | |
116 | kSecAsn1IntegerTemplate }, | |
117 | }; | |
118 | ||
119 | #define NUM_NSS_EXTEN_INFOS (sizeof(nssExtenInfo) / sizeof(nssExtenInfo[0])) | |
120 | ||
121 | /* | |
122 | * Returns true if we find the OID. | |
123 | */ | |
124 | bool clOidToNssInfo( | |
125 | const CSSM_OID &oid, | |
126 | unsigned &nssObjLen, // RETURNED | |
127 | const SecAsn1Template *&templ) // RETURNED | |
128 | { | |
129 | for(unsigned dex=0; dex<NUM_NSS_EXTEN_INFOS; dex++) { | |
130 | const NssExtenInfo &info = nssExtenInfo[dex]; | |
131 | if(clCompareCssmData(&info.oid, &oid)) { | |
132 | nssObjLen = info.nssObjLen; | |
133 | templ = info.templ; | |
134 | return true; | |
135 | } | |
136 | } | |
137 | return false; | |
138 | } | |
139 | ||
140 | ||
141 | ||
142 | /* | |
143 | * Common code to pass info from a DecodedExten back to app. | |
144 | * Called from getField*(). | |
145 | */ | |
146 | void getFieldExtenCommon( | |
147 | void *cdsaObj, // e.g. CE_KeyUsage | |
148 | // NULL for berEncoded | |
149 | const DecodedExten &decodedExt, | |
150 | CssmOwnedData &fieldValue) | |
151 | { | |
152 | CSSM_X509_EXTENSION_PTR cssmExt; | |
153 | Allocator &alloc = fieldValue.allocator; | |
154 | CssmData &fdata = fieldValue.get(); | |
155 | ||
156 | cssmExt = (CSSM_X509_EXTENSION_PTR)alloc. | |
157 | malloc(sizeof(CSSM_X509_EXTENSION)); | |
158 | fdata.Data = (uint8 *)cssmExt; | |
159 | fdata.Length = sizeof(CSSM_X509_EXTENSION); | |
160 | decodedExt.convertToCdsa(cdsaObj, cssmExt, alloc); | |
161 | } | |
162 | ||
163 | /* | |
164 | * Common code for top of setField* and freeField*(). | |
165 | */ | |
166 | CSSM_X509_EXTENSION_PTR verifySetFreeExtension( | |
167 | const CssmData &fieldValue, | |
168 | bool berEncoded) // false: value in value.parsedValue | |
169 | // true : value in BERValue | |
170 | { | |
171 | if(fieldValue.length() != sizeof(CSSM_X509_EXTENSION)) { | |
172 | clFieldLog("Set/FreeExtension: bad length : exp %d got %d", | |
173 | (int)sizeof(CSSM_X509_EXTENSION), (int)fieldValue.length()); | |
174 | CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); | |
175 | } | |
176 | CSSM_X509_EXTENSION_PTR cssmExt = | |
177 | reinterpret_cast<CSSM_X509_EXTENSION_PTR>(fieldValue.data()); | |
178 | if(berEncoded) { | |
179 | if(cssmExt->BERvalue.Data == NULL) { | |
180 | CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); | |
181 | } | |
182 | } | |
183 | else { | |
184 | if(cssmExt->value.parsedValue == NULL) { | |
185 | CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); | |
186 | } | |
187 | } | |
188 | return cssmExt; | |
189 | } | |
190 | ||
191 | /* | |
192 | * Common free code for all extensions. Extension-specific code must | |
193 | * free anything beyond cdsaExt->Value.parsedValue, then we free everything | |
194 | * else (except the extension struct itself, which is freed by | |
195 | * DecodedCert::freeCertFieldData()). | |
196 | * The value union may contain a parsed value, or a CSSM_X509EXT_TAGandVALUE; | |
197 | * wed ont' care, we just free it. | |
198 | */ | |
199 | void freeFieldExtenCommon( | |
200 | CSSM_X509_EXTENSION_PTR exten, | |
201 | Allocator &alloc) | |
202 | { | |
203 | alloc.free(exten->extnId.Data); | |
204 | alloc.free(exten->BERvalue.Data); // may be NULL | |
205 | alloc.free(exten->value.parsedValue); // may be NULL | |
206 | } | |
207 | ||
208 | /* | |
209 | * One common free for extensions whose parsed value doesn't go any deeper | |
210 | * than cssmExt->value.parsedValue. | |
211 | */ | |
212 | void freeFieldSimpleExtension ( | |
213 | CssmOwnedData &fieldValue) | |
214 | { | |
215 | CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, | |
216 | false); | |
217 | freeFieldExtenCommon(cssmExt, fieldValue.allocator); | |
218 | } | |
219 | ||
220 | ||
221 | /*** | |
222 | *** Common code for get/set subject/issuer name (C struct version) | |
223 | *** Format = CSSM_X509_NAME | |
224 | *** class Name from sm_x501if | |
225 | ***/ | |
226 | bool getField_RDN_NSS ( | |
227 | const NSS_Name &nssName, | |
228 | CssmOwnedData &fieldValue) // RETURNED | |
229 | { | |
230 | /* alloc top-level CSSM_X509_NAME */ | |
231 | Allocator &alloc = fieldValue.allocator; | |
232 | fieldValue.malloc(sizeof(CSSM_X509_NAME)); | |
233 | CSSM_X509_NAME_PTR cssmName = (CSSM_X509_NAME_PTR)fieldValue.data(); | |
234 | ||
235 | CL_nssNameToCssm(nssName, *cssmName, alloc); | |
236 | return true; | |
237 | } | |
238 | ||
239 | void freeField_RDN ( | |
240 | CssmOwnedData &fieldValue) | |
241 | { | |
242 | if(fieldValue.data() == NULL) { | |
243 | return; | |
244 | } | |
245 | if(fieldValue.length() != sizeof(CSSM_X509_NAME)) { | |
246 | CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); | |
247 | } | |
248 | Allocator &alloc = fieldValue.allocator; | |
249 | CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue.data(); | |
250 | CL_freeX509Name(x509Name, alloc); | |
251 | ||
252 | /* top-level x509Name pointer freed by freeCertFieldData() */ | |
253 | } | |
254 | ||
255 | /*** | |
256 | *** Common code for Issuer Name, Subject Name (normalized and encoded | |
257 | *** version) | |
258 | *** Format = CSSM_DATA containing the DER encoding of the normalized name | |
259 | ***/ | |
260 | bool getField_normRDN_NSS ( | |
261 | const CSSM_DATA &derName, | |
262 | uint32 &numFields, // RETURNED (if successful, 0 or 1) | |
263 | CssmOwnedData &fieldValue) // RETURNED | |
264 | { | |
265 | if(derName.Data == NULL) { | |
266 | /* This can happen during CertGetAllTemplateFields() because | |
267 | * the normalized fields are only set up during cert/CRL decode */ | |
268 | return false; | |
269 | } | |
270 | ||
271 | /* | |
272 | * First make a temp decoded copy which we'll be manipulating. | |
273 | */ | |
274 | SecNssCoder coder; | |
275 | NSS_Name decodedName; | |
276 | ||
277 | memset(&decodedName, 0, sizeof(decodedName)); | |
278 | PRErrorCode prtn = coder.decodeItem(derName, kSecAsn1NameTemplate, &decodedName); | |
279 | if(prtn) { | |
280 | /* | |
281 | * Actually should never happen since this same bag of bits successfully | |
282 | * decoded when the cert as a whole was decoded... | |
283 | */ | |
284 | clErrorLog("getField_normRDN decode error\n"); | |
285 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); | |
286 | ||
287 | } | |
288 | ||
289 | /* normalize */ | |
290 | CL_normalizeX509NameNSS(decodedName, coder); | |
291 | ||
292 | /* encode result */ | |
293 | prtn = SecNssEncodeItemOdata(&decodedName, kSecAsn1NameTemplate, fieldValue); | |
294 | if(prtn) { | |
295 | clErrorLog("getField_normRDN encode error\n"); | |
296 | CssmError::throwMe(CSSMERR_CL_INTERNAL_ERROR); | |
297 | } | |
298 | numFields = 1; | |
299 | return true; | |
300 | } | |
301 | ||
302 | /*** | |
303 | *** Common code for Time fields - Validity not before, Not After, | |
304 | *** This Update, Next Update | |
305 | *** Format: CSSM_X509_TIME | |
306 | ***/ | |
307 | bool getField_TimeNSS ( | |
308 | const NSS_Time &nssTime, | |
309 | unsigned index, // which occurrence (0 = first) | |
310 | uint32 &numFields, // RETURNED | |
311 | CssmOwnedData &fieldValue) // RETURNED | |
312 | { | |
313 | if(!tbsGetCheck(nssTime.item.Data, index)) { | |
314 | return false; | |
315 | } | |
316 | Allocator &alloc = fieldValue.allocator; | |
317 | fieldValue.malloc(sizeof(CSSM_X509_TIME)); | |
318 | CSSM_X509_TIME *cssmTime = | |
319 | (CSSM_X509_TIME *)fieldValue.data(); | |
320 | if(CL_nssTimeToCssm(nssTime, *cssmTime, alloc)) { | |
321 | numFields = 1; | |
322 | return true; | |
323 | } | |
324 | else { | |
325 | return false; | |
326 | } | |
327 | } | |
328 | ||
329 | void setField_TimeNSS ( | |
330 | const CssmData &fieldValue, | |
331 | NSS_Time &nssTime, | |
332 | SecNssCoder &coder) | |
333 | { | |
334 | CSSM_X509_TIME *cssmTime = | |
335 | (CSSM_X509_TIME *)fieldValue.data(); | |
336 | CL_cssmTimeToNss(*cssmTime, nssTime, coder); | |
337 | } | |
338 | ||
339 | void freeField_Time ( | |
340 | CssmOwnedData &fieldValue) | |
341 | { | |
342 | CSSM_X509_TIME *cssmTime = (CSSM_X509_TIME *)fieldValue.data(); | |
343 | if(cssmTime == NULL) { | |
344 | return; | |
345 | } | |
346 | if(fieldValue.length() != sizeof(CSSM_X509_TIME)) { | |
347 | CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); | |
348 | } | |
349 | CL_freeCssmTime(cssmTime, fieldValue.allocator); | |
350 | } | |
351 | ||
352 | /*** | |
353 | *** TBS AlgId, Signature AlgId | |
354 | *** Format = CSSM_X509_ALGORITHM_IDENTIFIER | |
355 | ***/ | |
356 | void getField_AlgIdNSS ( | |
357 | const CSSM_X509_ALGORITHM_IDENTIFIER &srcAlgId, | |
358 | CssmOwnedData &fieldValue) // RETURNED | |
359 | { | |
360 | Allocator &alloc = fieldValue.allocator; | |
361 | fieldValue.malloc(sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); | |
362 | CSSM_X509_ALGORITHM_IDENTIFIER *destAlgId = | |
363 | (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldValue.data(); | |
364 | CL_copyAlgId(srcAlgId, *destAlgId, alloc); | |
365 | } | |
366 | ||
367 | void setField_AlgIdNSS ( | |
368 | const CssmData &fieldValue, | |
369 | CSSM_X509_ALGORITHM_IDENTIFIER &dstAlgId, | |
370 | SecNssCoder &coder) | |
371 | { | |
372 | CSSM_X509_ALGORITHM_IDENTIFIER *srcAlgId = | |
373 | (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldValue.data(); | |
374 | /* allocator for this coder */ | |
375 | ArenaAllocator areanAlloc(coder); | |
376 | CL_copyAlgId(*srcAlgId, dstAlgId, areanAlloc); | |
377 | } | |
378 | ||
379 | void freeField_AlgId ( | |
380 | CssmOwnedData &fieldValue) | |
381 | { | |
382 | CSSM_X509_ALGORITHM_IDENTIFIER *cssmAlgId = | |
383 | (CSSM_X509_ALGORITHM_IDENTIFIER *)fieldValue.data(); | |
384 | if(cssmAlgId == NULL) { | |
385 | return; | |
386 | } | |
387 | if(fieldValue.length() != sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)) { | |
388 | CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); | |
389 | } | |
390 | Allocator &alloc = fieldValue.allocator; | |
391 | alloc.free(cssmAlgId->algorithm.Data); | |
392 | alloc.free(cssmAlgId->parameters.Data); | |
393 | memset(cssmAlgId, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); | |
394 | } | |
395 | ||
396 | /* | |
397 | * Routines for common validity checking for certificateToSign fields. | |
398 | * | |
399 | * Call from setField*: verify field isn't already set, optionally validate | |
400 | * input length | |
401 | */ | |
402 | void tbsSetCheck( | |
403 | void *fieldToSet, | |
404 | const CssmData &fieldValue, | |
405 | uint32 expLength, | |
406 | const char *op) | |
407 | { | |
408 | if(fieldToSet != NULL) { | |
409 | /* can't add another */ | |
410 | clErrorLog("setField(%s): field already set", op); | |
411 | CssmError::throwMe(CSSMERR_CL_INVALID_NUMBER_OF_FIELDS); | |
412 | } | |
413 | if((expLength != 0) && (fieldValue.length() != expLength)) { | |
414 | clErrorLog("setField(%s): bad length : exp %d got %d", | |
415 | op, (int)expLength, (int)fieldValue.length()); | |
416 | CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); | |
417 | } | |
418 | } | |
419 | ||
420 | /* | |
421 | * Call from getField* for unique fields - detect missing field or | |
422 | * index out of bounds. | |
423 | */ | |
424 | bool tbsGetCheck( | |
425 | const void *requiredField, | |
426 | uint32 reqIndex) | |
427 | { | |
428 | if((requiredField == NULL) || (reqIndex != 0)) { | |
429 | return false; | |
430 | } | |
431 | else { | |
432 | return true; | |
433 | } | |
434 | } | |
435 | ||
436 | /*** | |
437 | *** unknown extensions | |
438 | *** CDSA format: raw bytes in a CSSM_DATA. This data is the BER-encoding of | |
439 | *** some extension struct we don't know about. | |
440 | *** NSS format CSSM_DATA | |
441 | *** OID CSSMOID_X509V3CertificateExtensionCStruct | |
442 | ***/ | |
443 | ||
444 | void setFieldUnknownExt( | |
445 | DecodedItem &cert, | |
446 | const CssmData &fieldValue) | |
447 | { | |
448 | CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, true); | |
449 | SecNssCoder &coder = cert.coder(); | |
450 | CSSM_DATA *rawExtn = (CSSM_DATA *)coder.malloc(sizeof(CSSM_DATA)); | |
451 | coder.allocCopyItem(cssmExt->BERvalue, *rawExtn); | |
452 | cert.addExtension(NULL, cssmExt->extnId, cssmExt->critical, | |
453 | true, NULL /* no template */, rawExtn); | |
454 | } | |
455 | ||
456 | bool getFieldUnknownExt( | |
457 | DecodedItem &cert, | |
458 | unsigned index, // which occurrence (0 = first) | |
459 | uint32 &numFields, // RETURNED | |
460 | CssmOwnedData &fieldValue) | |
461 | { | |
462 | uint8 noOidDataLikeThis[2] = {1, 2}; // a dummy argument | |
463 | CSSM_OID noOidLikeThis = {2, noOidDataLikeThis}; | |
464 | const DecodedExten *decodedExt = | |
465 | cert.DecodedItem::findDecodedExt(noOidLikeThis, | |
466 | true, index, numFields); | |
467 | if(decodedExt == NULL) { | |
468 | return false; | |
469 | } | |
470 | getFieldExtenCommon(NULL, *decodedExt, fieldValue); | |
471 | return true; | |
472 | } | |
473 | ||
474 | void freeFieldUnknownExt ( | |
475 | CssmOwnedData &fieldValue) | |
476 | { | |
477 | CSSM_X509_EXTENSION_PTR cssmExt = verifySetFreeExtension(fieldValue, true); | |
478 | Allocator &alloc = fieldValue.allocator; | |
479 | freeFieldExtenCommon(cssmExt, alloc); // frees extnId, parsedValue, BERvalue | |
480 | } | |
481 | ||
482 | /* setField for read-only OIDs (i.e., the ones in cert/CRL, not TBS) */ | |
483 | void setField_ReadOnly ( | |
484 | DecodedItem &item, | |
485 | const CssmData &fieldValue) | |
486 | { | |
487 | clErrorLog("Attempt to set a read-only field"); | |
488 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); | |
489 | } | |
490 | ||
491 | bool getField_Unimplemented ( | |
492 | DecodedItem &item, | |
493 | unsigned index, // which occurrence (0 = first) | |
494 | uint32 &numFields, // RETURNED | |
495 | CssmOwnedData &fieldValue) // RETURNED | |
496 | { | |
497 | clErrorLog("Attempt to get an unimplemented field"); | |
498 | CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); | |
499 | } | |
500 | ||
501 | ||
502 |