]> git.saurik.com Git - apple/security.git/blob - Keychain/Certificate.cpp
Security-54.1.tar.gz
[apple/security.git] / Keychain / Certificate.cpp
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 // Certificate.cpp
20 //
21 #include <Security/Certificate.h>
22 #include <Security/Schema.h>
23 #include <Security/oidscert.h>
24 #include <Security/SecCertificate.h>
25 #include <Security/cspclient.h>
26
27 using namespace KeychainCore;
28
29 CL
30 Certificate::clForType(CSSM_CERT_TYPE type)
31 {
32 return CL(gGuidAppleX509CL);
33 }
34
35 Certificate::Certificate(const CSSM_DATA &data, CSSM_CERT_TYPE type, CSSM_CERT_ENCODING encoding) :
36 ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE, reinterpret_cast<SecKeychainAttributeList *>(NULL), UInt32(data.Length), reinterpret_cast<const void *>(data.Data)),
37 mHaveTypeAndEncoding(true),
38 mType(type),
39 mEncoding(encoding),
40 mCL(clForType(type)),
41 mCertHandle(0)
42 {
43 }
44
45 // db item contstructor
46 Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
47 ItemImpl(keychain, primaryKey, uniqueId),
48 mHaveTypeAndEncoding(false),
49 mCL(NULL),
50 mCertHandle(0)
51 {
52 }
53
54 // PrimaryKey item contstructor
55 Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey) :
56 ItemImpl(keychain, primaryKey),
57 mHaveTypeAndEncoding(false),
58 mCL(NULL),
59 mCertHandle(0)
60 {
61 // @@@ In this case we don't know the type...
62 }
63
64 Certificate::Certificate(Certificate &certificate) :
65 ItemImpl(certificate),
66 mHaveTypeAndEncoding(certificate.mHaveTypeAndEncoding),
67 mType(certificate.mType),
68 mEncoding(certificate.mEncoding),
69 mCL(certificate.mCL),
70 mCertHandle(0)
71 {
72 }
73
74 Certificate::~Certificate()
75 {
76 if (mCertHandle)
77 CSSM_CL_CertAbortCache(mCL->handle(), mCertHandle);
78 }
79
80 CSSM_HANDLE
81 Certificate::certHandle()
82 {
83 const CSSM_DATA *cert = &data();
84 if (!mCertHandle)
85 {
86 if (CSSM_RETURN retval = CSSM_CL_CertCache(mCL->handle(), cert, &mCertHandle))
87 CssmError::throwMe(retval);
88 }
89
90 return mCertHandle;
91 }
92
93 /* Return a zero terminated list of CSSM_DATA_PTR's with the values of the field specified by field. Caller must call releaseFieldValues to free the storage allocated by this call. */
94 CSSM_DATA_PTR *
95 Certificate::copyFieldValues(const CSSM_OID &field)
96 {
97 CSSM_CL_HANDLE clHandle = mCL->handle();
98 CSSM_DATA_PTR fieldValue, *fieldValues;
99 CSSM_HANDLE resultsHandle = 0;
100 uint32 numberOfFields = 0;
101 CSSM_RETURN result;
102
103 result = CSSM_CL_CertGetFirstCachedFieldValue(clHandle, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
104 if (result)
105 {
106 if (result == CSSMERR_CL_NO_FIELD_VALUES)
107 return NULL;
108
109 CssmError::throwMe(result);
110 }
111
112 fieldValues = new CSSM_DATA_PTR[numberOfFields + 1];
113 fieldValues[0] = fieldValue;
114 fieldValues[numberOfFields] = NULL;
115
116 for (uint32 value = 1; value < numberOfFields; ++value)
117 {
118 CSSM_RETURN cresult = CSSM_CL_CertGetNextCachedFieldValue(clHandle, resultsHandle, &fieldValues[value]);
119 if (cresult)
120 {
121 fieldValues[value] = NULL;
122 result = cresult;
123 break; // No point in continuing really.
124 }
125 }
126
127 if (result)
128 {
129 releaseFieldValues(field, fieldValues);
130 CssmError::throwMe(result);
131 }
132
133 return fieldValues;
134 }
135
136 void
137 Certificate::releaseFieldValues(const CSSM_OID &field, CSSM_DATA_PTR *fieldValues)
138 {
139 if (fieldValues)
140 {
141 CSSM_CL_HANDLE clHandle = mCL->handle();
142
143 for (int ix = 0; fieldValues[ix]; ++ix)
144 CSSM_CL_FreeFieldValue(clHandle, &field, fieldValues[ix]);
145
146 delete[] fieldValues;
147 }
148 }
149
150 void
151 Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO &info, const CSSM_OID &field)
152 {
153 CSSM_DATA_PTR *fieldValues = copyFieldValues(field);
154 if (fieldValues)
155 {
156 CssmDbAttributeData &anAttr = mDbAttributes->add(info);
157 for (int ix = 0; fieldValues[ix]; ++ix)
158 anAttr.add(*fieldValues[ix], *mDbAttributes);
159
160 releaseFieldValues(field, fieldValues);
161 }
162 }
163
164 /* Return a CSSM_DATA_PTR with the value of the first field specified by field. Caller must call releaseFieldValue to free the storage allocated by this call. */
165 CSSM_DATA_PTR
166 Certificate::copyFirstFieldValue(const CSSM_OID &field)
167 {
168 CSSM_CL_HANDLE clHandle = mCL->handle();
169 CSSM_DATA_PTR fieldValue;
170 CSSM_HANDLE resultsHandle = 0;
171 uint32 numberOfFields = 0;
172 CSSM_RETURN result;
173
174 result = CSSM_CL_CertGetFirstCachedFieldValue(clHandle, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
175 if (result)
176 {
177 if (result == CSSMERR_CL_NO_FIELD_VALUES)
178 return NULL;
179
180 CssmError::throwMe(result);
181 }
182
183 result = CSSM_CL_CertAbortQuery(clHandle, resultsHandle);
184
185 if (result)
186 {
187 releaseFieldValue(field, fieldValue);
188 CssmError::throwMe(result);
189 }
190
191 return fieldValue;
192 }
193
194 void
195 Certificate::releaseFieldValue(const CSSM_OID &field, CSSM_DATA_PTR fieldValue)
196 {
197 if (fieldValue)
198 {
199 CSSM_CL_HANDLE clHandle = mCL->handle();
200 CSSM_CL_FreeFieldValue(clHandle, &field, fieldValue);
201 }
202 }
203
204
205
206 /*
207 This method computes the keyIdentifier for the public key in the cert as
208 described below:
209
210 The keyIdentifier is composed of the 160-bit SHA-1 hash of the
211 value of the BIT STRING subjectPublicKey (excluding the tag,
212 length, and number of unused bits).
213 */
214 void
215 Certificate::publicKeyHash(CssmData &digestData)
216 {
217 #if 0
218 CSSM_DATA_PTR *keysPtr = copyFieldValues(CSSMOID_X509V1SubjectPublicKey);
219
220 if (keysPtr && keysPtr[0])
221 {
222 CssmData &key = CssmData::overlay(*keysPtr[0]);
223 CssmClient::CSP csp(gGuidAppleCSP);
224 CssmClient::Digest digest(csp, CSSM_ALGID_SHA1);
225 digest.digest(key, digestData);
226 }
227
228 releaseFieldValues(CSSMOID_X509V1SubjectPublicKey, keysPtr);
229 #else
230 CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
231 if (keyPtr && keyPtr->Data)
232 {
233 CssmClient::CSP csp(gGuidAppleCSP);
234 CssmClient::PassThrough passThrough(csp);
235 CSSM_KEY *key = reinterpret_cast<CSSM_KEY *>(keyPtr->Data);
236 void *outData;
237 CssmData *cssmData;
238
239 /* Given a CSSM_KEY_PTR in any format, obtain the SSHA-1 hash of the
240 * associated key blob.
241 * Key is specified in CSSM_CSP_CreatePassThroughContext.
242 * Hash is allocated bythe CSP, in the App's memory, and returned
243 * in *outData. */
244 passThrough.key(key);
245 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
246 cssmData = reinterpret_cast<CssmData *>(outData);
247 assert(cssmData->Length <= digestData.Length);
248 digestData.Length = cssmData->Length;
249 memcpy(digestData.Data, cssmData->Data, cssmData->Length);
250 csp.allocator().free(cssmData->Data);
251 csp.allocator().free(cssmData);
252 }
253
254 releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
255 #endif
256 }
257
258 void
259 Certificate::addLabel()
260 {
261 // Set label attribute for this certificate, based on the X509 subject name.
262 const CSSM_OID &fieldOid = CSSMOID_X509V1SubjectNameCStruct;
263 CSSM_DATA_PTR fieldValue = copyFirstFieldValue(fieldOid);
264 if (fieldValue && fieldValue->Data)
265 {
266 CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue->Data;
267 CSSM_X509_TYPE_VALUE_PAIR *ptvp=0;
268 CSSM_X509_RDN_PTR rdnp;
269 unsigned int rdnDex, pairDex;
270
271 // iterate through all RDN pairs; ptvp points to last entry when done
272 if (x509Name->numberOfRDNs) {
273 rdnp = &x509Name->RelativeDistinguishedName[x509Name->numberOfRDNs-1];
274 if (rdnp->numberOfPairs)
275 ptvp = &rdnp->AttributeTypeAndValue[rdnp->numberOfPairs-1];
276 }
277 if (ptvp)
278 {
279 CSSM_BER_TAG btag = ptvp->valueType;
280 if (btag==BER_TAG_PRINTABLE_STRING || btag==BER_TAG_IA5_STRING ||
281 btag==BER_TAG_T61_STRING || btag==BER_TAG_PKIX_UTF8_STRING)
282 {
283 mDbAttributes->add(Schema::attributeInfo(kSecLabelItemAttr), ptvp->value);
284 }
285 }
286 releaseFieldValue(fieldOid, fieldValue);
287 }
288 }
289
290 void
291 Certificate::populateAttributes()
292 {
293 addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr), CSSMOID_X509V1SubjectName);
294 addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr), CSSMOID_X509V1IssuerName);
295 addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr), CSSMOID_X509V1SerialNumber);
296
297 addParsedAttribute(Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr), CSSMOID_SubjectKeyIdentifier);
298
299 if(!mHaveTypeAndEncoding)
300 MacOSError::throwMe(errSecDataNotAvailable); // @@@ Or some other error.
301
302 // Adjust mType based on the actual version of the cert.
303 CSSM_DATA_PTR versionPtr = copyFirstFieldValue(CSSMOID_X509V1Version);
304 if (versionPtr && versionPtr->Data && versionPtr->Length == sizeof(uint32))
305 {
306 mType = CSSM_CERT_X_509v1 + (*reinterpret_cast<uint32 *>(versionPtr->Data));
307 }
308 else
309 mType = CSSM_CERT_X_509v1;
310
311 releaseFieldValue(CSSMOID_X509V1Version, versionPtr);
312
313 mDbAttributes->add(Schema::attributeInfo(kSecCertTypeItemAttr), mType);
314 mDbAttributes->add(Schema::attributeInfo(kSecCertEncodingItemAttr), mEncoding);
315
316 uint8 digestBytes[20];
317 CssmData digestData(digestBytes, 20);
318 publicKeyHash(digestData);
319
320 mDbAttributes->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr), digestData);
321 addLabel();
322 }
323
324 const CssmData &
325 Certificate::data()
326 {
327 CssmDataContainer *data = mData.get();
328 if (!data && mKeychain)
329 {
330 // Make sure mUniqueId is set.
331 dbUniqueRecord();
332 data = new CssmDataContainer();
333 mData.reset(data);
334 mUniqueId->get(NULL, data);
335 }
336
337 // If the data hasn't been set we can't return it.
338 if (!data)
339 MacOSError::throwMe(errSecDataNotAvailable);
340
341 return *data;
342 }
343
344 CSSM_CERT_TYPE
345 Certificate::type()
346 {
347 if (!mHaveTypeAndEncoding)
348 {
349 SecKeychainAttribute attr;
350 attr.tag = kSecCertTypeItemAttr;
351 attr.data = &mType;
352 attr.length = sizeof(mType);
353 getAttribute(attr, NULL);
354 }
355
356 return mType;
357 }
358
359 CSSM_CERT_ENCODING
360 Certificate::encoding()
361 {
362 if (!mHaveTypeAndEncoding)
363 {
364 SecKeychainAttribute attr;
365 attr.tag = kSecCertEncodingItemAttr;
366 attr.data = &mEncoding;
367 attr.length = sizeof(mEncoding);
368 getAttribute(attr, NULL);
369 }
370
371 return mEncoding;
372 }
373
374 void
375 Certificate::getSubject(CSSM_X509_NAME &outSubject)
376 {
377 }
378
379 void
380 Certificate::getIssuer(CSSM_X509_NAME &outName)
381 {
382 }
383
384 CSSM_CL_HANDLE
385 Certificate::clHandle()
386 {
387 if (!mCL)
388 mCL = clForType(type());
389
390 return mCL->handle();
391 }
392
393 bool
394 Certificate::operator < (Certificate &other)
395 {
396 return data() < other.data();
397 }
398
399 bool
400 Certificate::operator == (Certificate &other)
401 {
402 return data() == other.data();
403 }
404
405 void
406 Certificate::update()
407 {
408 ItemImpl::update();
409 }
410
411 Item
412 Certificate::copyTo(const Keychain &keychain)
413 {
414 return ItemImpl::copyTo(keychain);
415 }
416
417 void
418 Certificate::didModify()
419 {
420 }
421
422 PrimaryKey
423 Certificate::add(Keychain &keychain)
424 {
425 // If we already have a Keychain we can't be added.
426 if (mKeychain)
427 MacOSError::throwMe(errSecDuplicateItem);
428
429 populateAttributes();
430
431 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
432
433 Db db(keychain->database());
434 // add the item to the (regular) db
435 try
436 {
437 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
438 }
439 catch (const CssmError &e)
440 {
441 if (e.cssmError() != CSSMERR_DL_INVALID_RECORDTYPE)
442 throw;
443
444 // Create the cert relation and try again.
445 db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE, "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
446 Schema::X509CertificateSchemaAttributeCount,
447 Schema::X509CertificateSchemaAttributeList,
448 Schema::X509CertificateSchemaIndexCount,
449 Schema::X509CertificateSchemaIndexList);
450
451 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
452 }
453
454 mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
455 mKeychain = keychain;
456
457 return mPrimaryKey;
458 }