]> git.saurik.com Git - apple/security.git/blob - AppleX509TP/TPCertInfo.cpp
Security-29.tar.gz
[apple/security.git] / AppleX509TP / TPCertInfo.cpp
1 /*
2 * Copyright (c) 2000-2001 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 * TPCertInfo.h - TP's private certificate info classes
21 *
22 * Written 10/23/2000 by Doug Mitchell.
23 */
24
25 #include "TPCertInfo.h"
26 #include "tpdebugging.h"
27 #include "tpTime.h"
28 #include "certGroupUtils.h"
29 #include <Security/cssmapi.h>
30 #include <Security/x509defs.h>
31 #include <Security/oidscert.h>
32 #include <Security/oidsalg.h>
33 #include <string.h> /* for memcmp */
34 #include <Security/threading.h> /* for Mutex */
35 #include <Security/globalizer.h>
36 #include <Security/debugging.h>
37
38 #define tpTimeDbg(args...) debug("tpTime", ## args)
39
40 /*
41 * No default constructor - this is the only way.
42 * This caches the cert and fetches subjectName and issuerName
43 * to ensure the incoming certData is well-constructed.
44 */
45 TPCertInfo::TPCertInfo(
46 const CSSM_DATA *certData,
47 CSSM_CL_HANDLE clHand,
48 bool copyCertData) : // true: we copy, we free
49 // false - caller owns
50 mClHand(clHand),
51 mCacheHand(CSSM_INVALID_HANDLE),
52 mSubjectName(NULL),
53 mIssuerName(NULL)
54 {
55 CSSM_RETURN crtn;
56
57 if(copyCertData) {
58 mCertData = tpMallocCopyCssmData(CssmAllocator::standard(), certData);
59 }
60 else {
61 mCertData = const_cast<CSSM_DATA *>(certData);
62 }
63 mWeOwnTheData = copyCertData;
64
65 /* cache the cert */
66 mClHand = clHand;
67 crtn = CSSM_CL_CertCache(clHand, mCertData, &mCacheHand);
68 if(crtn) {
69 /* bad cert */
70 CssmError::throwMe(crtn);
71 }
72
73 /* fetch subject name */
74 crtn = fetchField(&CSSMOID_X509V1SubjectName, &mSubjectName);
75 if(crtn) {
76 /* bad cert */
77 releaseResources();
78 CssmError::throwMe(crtn);
79 }
80
81 /* fetch issuer name */
82 crtn = fetchField(&CSSMOID_X509V1IssuerName, &mIssuerName);
83 if(crtn) {
84 /* bad cert */
85 releaseResources();
86 CssmError::throwMe(crtn);
87 }
88 }
89
90 /* frees mSubjectName, mIssuerName, mCacheHand via mClHand */
91 TPCertInfo::~TPCertInfo()
92 {
93 releaseResources();
94 }
95
96 void TPCertInfo::releaseResources()
97 {
98 if(mWeOwnTheData && (mCertData != NULL)) {
99 tpFreeCssmData(CssmAllocator::standard(), mCertData, CSSM_TRUE);
100 }
101 if(mSubjectName) {
102 freeField(&CSSMOID_X509V1SubjectName, mSubjectName);
103 }
104 if(mIssuerName) {
105 freeField(&CSSMOID_X509V1IssuerName, mIssuerName);
106 }
107 if(mCacheHand != CSSM_INVALID_HANDLE) {
108 CSSM_CL_CertAbortCache(mClHand, mCacheHand);
109 }
110 }
111
112 /* fetch arbitrary field from cached cert */
113 CSSM_RETURN TPCertInfo::fetchField(
114 const CSSM_OID *fieldOid,
115 CSSM_DATA_PTR *fieldData) // mallocd by CL and RETURNED
116 {
117 CSSM_RETURN crtn;
118
119 uint32 NumberOfFields = 0;
120 CSSM_HANDLE resultHand = 0;
121 *fieldData = NULL;
122
123 crtn = CSSM_CL_CertGetFirstCachedFieldValue(
124 mClHand,
125 mCacheHand,
126 fieldOid,
127 &resultHand,
128 &NumberOfFields,
129 fieldData);
130 if(crtn) {
131 return crtn;
132 }
133 if(NumberOfFields != 1) {
134 errorLog1("TPCertInfo::fetchField: numFields %d, expected 1\n",
135 (int)NumberOfFields);
136 }
137 CSSM_CL_CertAbortQuery(mClHand, resultHand);
138 return CSSM_OK;
139 }
140
141 /* free arbitrary field obtained from fetchField() */
142 CSSM_RETURN TPCertInfo::freeField(
143 const CSSM_OID *fieldOid,
144 CSSM_DATA_PTR fieldData)
145 {
146 return CSSM_CL_FreeFieldValue(mClHand, fieldOid, fieldData);
147
148 }
149
150 /* accessors */
151 CSSM_CL_HANDLE TPCertInfo::clHand()
152 {
153 return mClHand;
154 }
155
156 CSSM_HANDLE TPCertInfo::cacheHand()
157 {
158 return mCacheHand;
159 }
160
161 const CSSM_DATA *TPCertInfo::certData()
162 {
163 CASSERT(mCertData != NULL);
164 return mCertData;
165 }
166
167 const CSSM_DATA *TPCertInfo::subjectName()
168 {
169 CASSERT(mSubjectName != NULL);
170 return mSubjectName;
171 }
172
173 const CSSM_DATA *TPCertInfo::issuerName()
174 {
175 CASSERT(mIssuerName != NULL);
176 return mIssuerName;
177 }
178
179 bool TPCertInfo::isSelfSigned() // i.e., subject == issuer
180 {
181 return tpCompareCssmData(mSubjectName, mIssuerName) ? true : false;
182 }
183
184 /*
185 * Verify validity (not before/after). Returns
186 * CSSMERR_TP_CERT_NOT_VALID_YET
187 * CSSMERR_TP_CERT_EXPIRED
188 * CSSM_OK
189 * CSSMERR_TP_INVALID_CERT_POINTER, other "bogus cert" errors
190 *
191 * We use some stdlib time calls over in tpTime.c; the stdlib function
192 * gmtime() is not thread-safe, so we do the protection here. Note that
193 * this makes *our* calls to gmtime() thread-safe, but if the app has
194 * other threads which are also calling gmtime, we're out of luck.
195 */
196 static ModuleNexus<Mutex> tpTimeLock;
197
198 CSSM_RETURN TPCertInfo::isCurrent(
199 CSSM_BOOL allowExpired)
200 {
201 CSSM_DATA_PTR notBeforeField = NULL;
202 CSSM_DATA_PTR notAfterField = NULL;
203 CSSM_RETURN crtn = CSSM_OK;
204
205 CASSERT(mCacheHand != CSSM_INVALID_HANDLE);
206 crtn = fetchField(&CSSMOID_X509V1ValidityNotBefore, &notBeforeField);
207 if(crtn) {
208 errorLog0("TPCertInfo::isCurrent: GetField error");
209 return crtn;
210 }
211
212 struct tm now;
213 {
214 StLock<Mutex> _(tpTimeLock());
215 nowTime(&now);
216 }
217 struct tm notBefore;
218 CSSM_X509_TIME *xNotBefore = (CSSM_X509_TIME *)notBeforeField->Data;
219
220 if(timeStringToTm((char *)xNotBefore->time.Data, xNotBefore->time.Length,
221 &notBefore)) {
222 errorLog0("TPCertInfo::isCurrent: malformed notBefore time\n");
223 crtn = CSSMERR_TP_INVALID_CERT_POINTER;
224 goto errOut;
225 }
226 if(compareTimes(&now, &notBefore) < 0) {
227 crtn = CSSMERR_TP_CERT_NOT_VALID_YET;
228 tpTimeDbg("\nTP_CERT_NOT_VALID_YET:\n now y:%d m:%d d:%d h:%d m:%d",
229 now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour,
230 now.tm_min);
231 tpTimeDbg(" notBefore y:%d m:%d d:%d h:%d m:%d",
232 notBefore.tm_year, notBefore.tm_mon, notBefore.tm_mday,
233 notBefore.tm_hour, notBefore.tm_min);
234 struct tm now2;
235 {
236 StLock<Mutex> _(tpTimeLock());
237 nowTime(&now2);
238 }
239 tpTimeDbg(" now2 y:%d m:%d d:%d h:%d m:%d",
240 now2.tm_year, now2.tm_mon, now2.tm_mday, now2.tm_hour,
241 now2.tm_min);
242 goto errOut;
243 }
244
245 if(!allowExpired) {
246 struct tm notAfter;
247 crtn = fetchField(&CSSMOID_X509V1ValidityNotAfter, &notAfterField);
248 if(crtn) {
249 errorLog0("TPCertInfo::isCurrent: GetField error");
250 goto errOut;
251 }
252
253 CSSM_X509_TIME *xNotAfter = (CSSM_X509_TIME *)notAfterField->Data;
254 if(timeStringToTm((char *)xNotAfter->time.Data, xNotAfter->time.Length,
255 &notAfter)) {
256 errorLog0("TPCertInfo::isCurrent: malformed notAfter time\n");
257 crtn = CSSMERR_TP_INVALID_CERT_POINTER;
258 }
259 else if(compareTimes(&now, &notAfter) > 0) {
260 crtn = CSSMERR_TP_CERT_EXPIRED;
261 tpTimeDbg("\nTP_CERT_EXPIRED: \n now y:%d m:%d d:%d "
262 "h:%d m:%d",
263 now.tm_year, now.tm_mon, now.tm_mday,
264 now.tm_hour, now.tm_min);
265 tpTimeDbg(" notAfter y:%d m:%d d:%d h:%d m:%d",
266 notAfter.tm_year, notAfter.tm_mon, notAfter.tm_mday,
267 notAfter.tm_hour, notAfter.tm_min);
268 struct tm now2;
269 {
270 StLock<Mutex> _(tpTimeLock());
271 nowTime(&now2);
272 }
273 tpTimeDbg(" now2 y:%d m:%d d:%d h:%d m:%d",
274 now2.tm_year, now2.tm_mon, now2.tm_mday, now2.tm_hour,
275 now2.tm_min);
276 }
277 else {
278 crtn = CSSM_OK;
279 }
280 }
281 else {
282 crtn = CSSM_OK;
283 }
284 errOut:
285 if(notAfterField) {
286 freeField(&CSSMOID_X509V1ValidityNotAfter, notAfterField);
287 }
288 if(notBeforeField) {
289 freeField(&CSSMOID_X509V1ValidityNotBefore, notBeforeField);
290 }
291 return crtn;
292 }
293
294 /***
295 *** TPCertGroup class
296 ***/
297 TPCertGroup::TPCertGroup(
298 CssmAllocator &alloc,
299 unsigned numCerts) :
300 mAlloc(alloc),
301 mNumCerts(0)
302 {
303 mCertInfo = (TPCertInfo **)alloc.malloc(numCerts * sizeof(TPCertInfo *));
304 mSizeofCertInfo = numCerts;
305 }
306
307 /*
308 * Deletes all TPCertInfo's.
309 */
310 TPCertGroup::~TPCertGroup()
311 {
312 unsigned i;
313 for(i=0; i<mNumCerts; i++) {
314 delete mCertInfo[i];
315 }
316 mAlloc.free(mCertInfo);
317 }
318
319 /* add/remove/access TPTCertInfo's. */
320 void TPCertGroup::appendCert(
321 TPCertInfo *certInfo) // appends to end of mCertInfo
322 {
323 if(mNumCerts == mSizeofCertInfo) {
324 /* FIXME - do we need the realloc workaround we used to have in TPSession? */
325 mSizeofCertInfo *= 2;
326 mCertInfo = (TPCertInfo **)mAlloc.realloc(mCertInfo,
327 mSizeofCertInfo * sizeof(TPCertInfo *));
328 }
329 mCertInfo[mNumCerts++] = certInfo;
330 }
331
332 TPCertInfo *TPCertGroup::certAtIndex(
333 unsigned index)
334 {
335 if(index > (mNumCerts - 1)) {
336 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
337 }
338 return mCertInfo[index];
339 }
340
341 TPCertInfo *TPCertGroup::removeCertAtIndex(
342 unsigned index) // doesn't delete the cert, just
343 // removes it from out list
344 {
345 if(index > (mNumCerts - 1)) {
346 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
347 }
348 TPCertInfo *rtn = mCertInfo[index];
349
350 /* removed requested element and compact remaining array */
351 unsigned i;
352 for(i=index; i<(mNumCerts - 1); i++) {
353 mCertInfo[i] = mCertInfo[i+1];
354 }
355 mNumCerts--;
356 return rtn;
357 }
358
359 unsigned TPCertGroup::numCerts()
360 {
361 return mNumCerts;
362 }
363
364 TPCertInfo *TPCertGroup::firstCert()
365 {
366 if(mNumCerts == 0) {
367 /* the caller really should not do this... */
368 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
369 }
370 else {
371 return mCertInfo[0];
372 }
373 }
374
375 TPCertInfo *TPCertGroup::lastCert()
376 {
377 if(mNumCerts == 0) {
378 /* the caller really should not do this... */
379 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
380 }
381 else {
382 return mCertInfo[mNumCerts - 1];
383 }
384 }
385
386 /* build a CSSM_CERTGROUP corresponding with our mCertInfo */
387 CSSM_CERTGROUP_PTR TPCertGroup::buildCssmCertGroup()
388 {
389 CSSM_CERTGROUP_PTR cgrp =
390 (CSSM_CERTGROUP_PTR)mAlloc.malloc(sizeof(CSSM_CERTGROUP));
391 cgrp->NumCerts = mNumCerts;
392 cgrp->CertGroupType = CSSM_CERTGROUP_ENCODED_CERT;
393 cgrp->CertType = CSSM_CERT_X_509v3;
394 cgrp->CertEncoding = CSSM_CERT_ENCODING_DER;
395 if(mNumCerts == 0) {
396 /* legal */
397 cgrp->GroupList.CertList = NULL;
398 return cgrp;
399 }
400 cgrp->GroupList.CertList = (CSSM_DATA_PTR)mAlloc.calloc(mNumCerts,
401 sizeof(CSSM_DATA));
402 for(unsigned i=0; i<mNumCerts; i++) {
403 tpCopyCssmData(mAlloc, mCertInfo[i]->certData(),
404 &cgrp->GroupList.CertList[i]);
405 }
406 return cgrp;
407 }