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