]> git.saurik.com Git - apple/security.git/blame - libsecurity_keychain/lib/Certificate.cpp
Security-55471.14.8.tar.gz
[apple/security.git] / libsecurity_keychain / lib / Certificate.cpp
CommitLineData
b1ab9ed8 1/*
427c49bc 2 * Copyright (c) 2002-2007,2012 Apple Inc. All Rights Reserved.
c2a06e24 3 *
b1ab9ed8 4 * @APPLE_LICENSE_HEADER_START@
c2a06e24 5 *
b1ab9ed8
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
c2a06e24 12 *
b1ab9ed8
A
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
c2a06e24 20 *
b1ab9ed8
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24//
25// Certificate.cpp
26//
27#include <security_keychain/Certificate.h>
28#include <security_cdsa_utilities/Schema.h>
29#include <Security/oidscert.h>
30#include <Security/oidsattr.h>
31#include <Security/SecCertificate.h>
32#include <Security/SecCertificatePriv.h>
33#include <security_cdsa_client/cspclient.h>
34#include <security_keychain/KeyItem.h>
35#include <security_keychain/KCCursor.h>
36#include <vector>
427c49bc
A
37#include <CommonCrypto/CommonDigestSPI.h>
38#include <SecBase.h>
b1ab9ed8
A
39
40using namespace KeychainCore;
41
42CL
43Certificate::clForType(CSSM_CERT_TYPE type)
44{
45 return CL(gGuidAppleX509CL);
46}
47
48Certificate::Certificate(const CSSM_DATA &data, CSSM_CERT_TYPE type, CSSM_CERT_ENCODING encoding) :
49 ItemImpl(CSSM_DL_DB_RECORD_X509_CERTIFICATE, reinterpret_cast<SecKeychainAttributeList *>(NULL), UInt32(data.Length), reinterpret_cast<const void *>(data.Data)),
50 mHaveTypeAndEncoding(true),
51 mPopulated(false),
427c49bc
A
52 mType(type),
53 mEncoding(encoding),
54 mCL(clForType(type)),
b1ab9ed8
A
55 mCertHandle(0),
56 mV1SubjectPublicKeyCStructValue(NULL),
427c49bc
A
57 mV1SubjectNameCStructValue(NULL),
58 mV1IssuerNameCStructValue(NULL),
59 mSha1Hash(NULL)
b1ab9ed8
A
60{
61 if (data.Length == 0 || data.Data == NULL)
427c49bc 62 MacOSError::throwMe(errSecParam);
b1ab9ed8
A
63}
64
65// db item constructor
66Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId) :
67 ItemImpl(keychain, primaryKey, uniqueId),
68 mHaveTypeAndEncoding(false),
69 mPopulated(false),
427c49bc 70 mCL(NULL),
b1ab9ed8
A
71 mCertHandle(0),
72 mV1SubjectPublicKeyCStructValue(NULL),
427c49bc
A
73 mV1SubjectNameCStructValue(NULL),
74 mV1IssuerNameCStructValue(NULL),
75 mSha1Hash(NULL)
b1ab9ed8
A
76{
77}
78
79
80
81Certificate* Certificate::make(const Keychain &keychain, const PrimaryKey &primaryKey, const CssmClient::DbUniqueRecord &uniqueId)
82{
83 Certificate* c = new Certificate(keychain, primaryKey, uniqueId);
84 keychain->addItem(primaryKey, c);
85 return c;
86}
87
88
89
90Certificate* Certificate::make(const Keychain &keychain, const PrimaryKey &primaryKey)
91{
92 Certificate* c = new Certificate(keychain, primaryKey);
93 keychain->addItem(primaryKey, c);
94 return c;
95}
96
97
98
99
100// PrimaryKey item constructor
101Certificate::Certificate(const Keychain &keychain, const PrimaryKey &primaryKey) :
102 ItemImpl(keychain, primaryKey),
103 mHaveTypeAndEncoding(false),
104 mPopulated(false),
427c49bc 105 mCL(NULL),
b1ab9ed8
A
106 mCertHandle(0),
107 mV1SubjectPublicKeyCStructValue(NULL),
427c49bc
A
108 mV1SubjectNameCStructValue(NULL),
109 mV1IssuerNameCStructValue(NULL),
110 mSha1Hash(NULL)
b1ab9ed8
A
111{
112 // @@@ In this case we don't know the type...
113}
114
115Certificate::Certificate(Certificate &certificate) :
116 ItemImpl(certificate),
117 mHaveTypeAndEncoding(certificate.mHaveTypeAndEncoding),
118 mPopulated(false /* certificate.mPopulated */),
427c49bc
A
119 mType(certificate.mType),
120 mEncoding(certificate.mEncoding),
121 mCL(certificate.mCL),
b1ab9ed8
A
122 mCertHandle(0),
123 mV1SubjectPublicKeyCStructValue(NULL),
427c49bc
A
124 mV1SubjectNameCStructValue(NULL),
125 mV1IssuerNameCStructValue(NULL),
126 mSha1Hash(NULL)
b1ab9ed8
A
127{
128}
129
427c49bc
A
130Certificate::~Certificate()
131try
b1ab9ed8
A
132{
133 if (mV1SubjectPublicKeyCStructValue)
134 releaseFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct, mV1SubjectPublicKeyCStructValue);
135
136 if (mCertHandle && mCL)
137 CSSM_CL_CertAbortCache(mCL->handle(), mCertHandle);
c2a06e24 138
427c49bc
A
139 if (mV1SubjectNameCStructValue)
140 releaseFieldValue(CSSMOID_X509V1SubjectNameCStruct, mV1SubjectNameCStructValue);
141
142 if (mV1IssuerNameCStructValue)
143 releaseFieldValue(CSSMOID_X509V1IssuerNameCStruct, mV1IssuerNameCStructValue);
b1ab9ed8 144
427c49bc
A
145 if (mSha1Hash)
146 CFRelease(mSha1Hash);
147}
148catch (...)
149{
b1ab9ed8
A
150}
151
152CSSM_HANDLE
153Certificate::certHandle()
154{
155 StLock<Mutex>_(mMutex);
156 const CSSM_DATA *cert = &data();
157 if (!mCertHandle)
158 {
159 if (CSSM_RETURN retval = CSSM_CL_CertCache(clHandle(), cert, &mCertHandle))
160 CssmError::throwMe(retval);
161 }
162
163 return mCertHandle;
164}
165
166/* 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. */
167CSSM_DATA_PTR *
168Certificate::copyFieldValues(const CSSM_OID &field)
169{
170 StLock<Mutex>_(mMutex);
171 CSSM_CL_HANDLE clh = clHandle();
172 CSSM_DATA_PTR fieldValue, *fieldValues;
173 CSSM_HANDLE resultsHandle = 0;
174 uint32 numberOfFields = 0;
175 CSSM_RETURN result;
176
177 result = CSSM_CL_CertGetFirstCachedFieldValue(clh, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
178 if (result)
179 {
180 if (result == CSSMERR_CL_NO_FIELD_VALUES)
181 return NULL;
182
183 CssmError::throwMe(result);
184 }
185
186 fieldValues = new CSSM_DATA_PTR[numberOfFields + 1];
187 fieldValues[0] = fieldValue;
188 fieldValues[numberOfFields] = NULL;
189
190 for (uint32 value = 1; value < numberOfFields; ++value)
191 {
192 CSSM_RETURN cresult = CSSM_CL_CertGetNextCachedFieldValue(clh, resultsHandle, &fieldValues[value]);
193 if (cresult)
194 {
195 fieldValues[value] = NULL;
196 result = cresult;
197 break; // No point in continuing really.
198 }
199 }
200
201 CSSM_CL_CertAbortQuery(clh, resultsHandle);
202
203 if (result)
204 {
205 releaseFieldValues(field, fieldValues);
206 CssmError::throwMe(result);
207 }
208
209 return fieldValues;
210}
211
212void
213Certificate::releaseFieldValues(const CSSM_OID &field, CSSM_DATA_PTR *fieldValues)
214{
215 StLock<Mutex>_(mMutex);
216 if (fieldValues)
217 {
218 CSSM_CL_HANDLE clh = clHandle();
c2a06e24 219
b1ab9ed8
A
220 for (int ix = 0; fieldValues[ix]; ++ix)
221 CSSM_CL_FreeFieldValue(clh, &field, fieldValues[ix]);
c2a06e24 222
b1ab9ed8
A
223 delete[] fieldValues;
224 }
225}
226
227void
228Certificate::addParsedAttribute(const CSSM_DB_ATTRIBUTE_INFO &info, const CSSM_OID &field)
229{
230 StLock<Mutex>_(mMutex);
231 CSSM_DATA_PTR *fieldValues = copyFieldValues(field);
232 if (fieldValues)
233 {
234 CssmDbAttributeData &anAttr = mDbAttributes->add(info);
235 for (int ix = 0; fieldValues[ix]; ++ix)
236 anAttr.add(*fieldValues[ix], *mDbAttributes);
c2a06e24 237
b1ab9ed8
A
238 releaseFieldValues(field, fieldValues);
239 }
240}
241
242void
243Certificate::addSubjectKeyIdentifier()
244{
245 StLock<Mutex>_(mMutex);
246 const CSSM_DB_ATTRIBUTE_INFO &info = Schema::attributeInfo(kSecSubjectKeyIdentifierItemAttr);
247 const CSSM_OID &field = CSSMOID_SubjectKeyIdentifier;
c2a06e24 248
b1ab9ed8
A
249 CSSM_DATA_PTR *fieldValues = copyFieldValues(field);
250 if (fieldValues)
251 {
252 CssmDbAttributeData &anAttr = mDbAttributes->add(info);
253 for (int ix = 0; fieldValues[ix]; ++ix)
254 {
255 const CSSM_X509_EXTENSION *extension = reinterpret_cast<const CSSM_X509_EXTENSION *>(fieldValues[ix]->Data);
256 if (extension == NULL || fieldValues[ix]->Length != sizeof(CSSM_X509_EXTENSION))
257 {
258 assert(extension != NULL && fieldValues[ix]->Length == sizeof(CSSM_X509_EXTENSION));
259 continue;
260 }
261 const CE_SubjectKeyID *skid = reinterpret_cast<CE_SubjectKeyID *>(extension->value.parsedValue);
262 if (skid == NULL)
263 {
264 assert(skid != NULL);
265 continue;
266 }
267 anAttr.add(*skid, *mDbAttributes);
268 }
c2a06e24 269
b1ab9ed8
A
270 releaseFieldValues(field, fieldValues);
271 }
272}
273
274/* 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. */
275CSSM_DATA_PTR
276Certificate::copyFirstFieldValue(const CSSM_OID &field)
277{
278 StLock<Mutex>_(mMutex);
279 CSSM_CL_HANDLE clh = clHandle();
280 CSSM_DATA_PTR fieldValue;
281 CSSM_HANDLE resultsHandle = 0;
282 uint32 numberOfFields = 0;
283 CSSM_RETURN result;
284
285 result = CSSM_CL_CertGetFirstCachedFieldValue(clh, certHandle(), &field, &resultsHandle, &numberOfFields, &fieldValue);
286 if (result)
287 {
288 if (result == CSSMERR_CL_NO_FIELD_VALUES)
289 return NULL;
290
291 CssmError::throwMe(result);
292 }
293
294 result = CSSM_CL_CertAbortQuery(clh, resultsHandle);
295
296 if (result)
297 {
298 releaseFieldValue(field, fieldValue);
299 CssmError::throwMe(result);
300 }
301
302 return fieldValue;
303}
304
305void
306Certificate::releaseFieldValue(const CSSM_OID &field, CSSM_DATA_PTR fieldValue)
307{
308 StLock<Mutex>_(mMutex);
309 if (fieldValue)
310 {
311 CSSM_CL_HANDLE clh = clHandle();
312 CSSM_CL_FreeFieldValue(clh, &field, fieldValue);
313 }
314}
315
316
317
318/*
319 This method computes the keyIdentifier for the public key in the cert as
320 described below:
c2a06e24 321
b1ab9ed8
A
322 The keyIdentifier is composed of the 160-bit SHA-1 hash of the
323 value of the BIT STRING subjectPublicKey (excluding the tag,
324 length, and number of unused bits).
325*/
326const CssmData &
327Certificate::publicKeyHash()
328{
329 StLock<Mutex>_(mMutex);
330 if (mPublicKeyHash.Length)
331 return mPublicKeyHash;
332
333 CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
334 if (keyPtr && keyPtr->Data)
335 {
336 CssmClient::CSP csp(gGuidAppleCSP);
337 CssmClient::PassThrough passThrough(csp);
338 CSSM_KEY *key = reinterpret_cast<CSSM_KEY *>(keyPtr->Data);
339 void *outData;
340 CssmData *cssmData;
341
c2a06e24
A
342 /* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
343 * associated key blob.
b1ab9ed8
A
344 * Key is specified in CSSM_CSP_CreatePassThroughContext.
345 * Hash is allocated by the CSP, in the App's memory, and returned
346 * in *outData. */
347 passThrough.key(key);
348 passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
349 cssmData = reinterpret_cast<CssmData *>(outData);
350
351 assert(cssmData->Length <= sizeof(mPublicKeyHashBytes));
352 mPublicKeyHash.Data = mPublicKeyHashBytes;
353 mPublicKeyHash.Length = cssmData->Length;
354 memcpy(mPublicKeyHash.Data, cssmData->Data, cssmData->Length);
355 csp.allocator().free(cssmData->Data);
356 csp.allocator().free(cssmData);
357 }
c2a06e24 358
b1ab9ed8
A
359 releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
360
361 return mPublicKeyHash;
362}
363
364const CssmData &
365Certificate::subjectKeyIdentifier()
366{
367 StLock<Mutex>_(mMutex);
368 if (mSubjectKeyID.Length)
369 return mSubjectKeyID;
370
371 CSSM_DATA_PTR fieldValue = copyFirstFieldValue(CSSMOID_SubjectKeyIdentifier);
372 if (fieldValue && fieldValue->Data && fieldValue->Length == sizeof(CSSM_X509_EXTENSION))
373 {
374 const CSSM_X509_EXTENSION *extension = reinterpret_cast<const CSSM_X509_EXTENSION *>(fieldValue->Data);
375 const CE_SubjectKeyID *skid = reinterpret_cast<CE_SubjectKeyID *>(extension->value.parsedValue); // CSSM_DATA
376
377 if (skid->Length <= sizeof(mSubjectKeyIDBytes))
378 {
379 mSubjectKeyID.Data = mSubjectKeyIDBytes;
380 mSubjectKeyID.Length = skid->Length;
381 memcpy(mSubjectKeyID.Data, skid->Data, skid->Length);
382 }
383 else
384 mSubjectKeyID.Length = 0;
385 }
c2a06e24 386
b1ab9ed8
A
387 releaseFieldValue(CSSMOID_SubjectKeyIdentifier, fieldValue);
388
389 return mSubjectKeyID;
390}
391
392
393/*
c2a06e24
A
394 * Given an CSSM_X509_NAME, Find the first (or last) name/value pair with
395 * a printable value which matches the specified OID (e.g., CSSMOID_CommonName).
b1ab9ed8 396 * Returns the CFString-style encoding associated with name component's BER tag.
c2a06e24 397 * Returns NULL if none found.
b1ab9ed8
A
398 */
399static const CSSM_DATA *
400findPrintableField(
401 const CSSM_X509_NAME &x509Name,
402 const CSSM_OID *tvpType, // NULL means "any printable field"
c2a06e24 403 bool lastInstance, // false means return first instance
b1ab9ed8
A
404 CFStringBuiltInEncodings *encoding) // RETURNED
405{
c2a06e24 406 const CSSM_DATA *result = NULL;
b1ab9ed8 407 for(uint32 rdnDex=0; rdnDex<x509Name.numberOfRDNs; rdnDex++) {
c2a06e24 408 const CSSM_X509_RDN *rdnPtr =
b1ab9ed8
A
409 &x509Name.RelativeDistinguishedName[rdnDex];
410 for(uint32 tvpDex=0; tvpDex<rdnPtr->numberOfPairs; tvpDex++) {
c2a06e24 411 const CSSM_X509_TYPE_VALUE_PAIR *tvpPtr =
b1ab9ed8 412 &rdnPtr->AttributeTypeAndValue[tvpDex];
c2a06e24 413
b1ab9ed8
A
414 /* type/value pair: match caller's specified type? */
415 if(tvpType != NULL && tvpType->Data != NULL) {
416 if(tvpPtr->type.Length != tvpType->Length) {
417 continue;
418 }
419 if(memcmp(tvpPtr->type.Data, tvpType->Data, tvpType->Length)) {
420 /* If we don't have a match but the requested OID is CSSMOID_UserID,
421 * look for a matching X.500 UserID OID: (0.9.2342.19200300.100.1.1) */
422 const char cssm_userid_oid[] = { 0x09,0x49,0x86,0x49,0x1f,0x12,0x8c,0xe4,0x81,0x81 };
423 const char x500_userid_oid[] = { 0x09,0x92,0x26,0x89,0x93,0xF2,0x2C,0x64,0x01,0x01 };
424 if(!(tvpType->Length == sizeof(cssm_userid_oid) &&
425 !memcmp(tvpPtr->type.Data, x500_userid_oid, sizeof(x500_userid_oid)) &&
426 !memcmp(tvpType->Data, cssm_userid_oid, sizeof(cssm_userid_oid)))) {
427 continue;
428 }
429 }
430 }
c2a06e24 431
b1ab9ed8
A
432 /* printable? */
433 switch(tvpPtr->valueType) {
434 case BER_TAG_PRINTABLE_STRING:
435 case BER_TAG_IA5_STRING:
436 *encoding = kCFStringEncodingASCII;
c2a06e24
A
437 result = &tvpPtr->value;
438 break;
b1ab9ed8
A
439 case BER_TAG_PKIX_UTF8_STRING:
440 case BER_TAG_GENERAL_STRING:
441 case BER_TAG_PKIX_UNIVERSAL_STRING:
442 *encoding = kCFStringEncodingUTF8;
c2a06e24
A
443 result = &tvpPtr->value;
444 break;
b1ab9ed8
A
445 case BER_TAG_T61_STRING:
446 case BER_TAG_VIDEOTEX_STRING:
447 case BER_TAG_ISO646_STRING:
448 *encoding = kCFStringEncodingISOLatin1;
c2a06e24
A
449 result = &tvpPtr->value;
450 break;
b1ab9ed8
A
451 case BER_TAG_PKIX_BMP_STRING:
452 *encoding = kCFStringEncodingUnicode;
c2a06e24
A
453 result = &tvpPtr->value;
454 break;
b1ab9ed8
A
455 default:
456 /* not printable */
457 break;
458 }
c2a06e24
A
459 /* if we found a result and we want the first instance, return it now. */
460 if(result && !lastInstance) {
461 return result;
462 }
463
b1ab9ed8
A
464 } /* for each pair */
465 } /* for each RDN */
c2a06e24
A
466
467 /* result is NULL if no printable component was found */
468 return result;
b1ab9ed8
A
469}
470
471/*
472 * Infer printable label for a given CSSM_X509_NAME. Returns NULL
473 * if no appropriate printable name found. Returns the CFString-style
c2a06e24
A
474 * encoding associated with name component's BER tag. Also optionally
475 * returns Description component and its encoding if present and the
476 * returned name component was one we explicitly requested.
b1ab9ed8
A
477 */
478static const CSSM_DATA *inferLabelFromX509Name(
479 const CSSM_X509_NAME *x509Name,
480 CFStringBuiltInEncodings *encoding, // RETURNED
481 const CSSM_DATA **description, // optionally RETURNED
482 CFStringBuiltInEncodings *descrEncoding) // RETURNED if description != NULL
483{
484 const CSSM_DATA *printValue;
c2a06e24
A
485 if(description != NULL) {
486 *description = findPrintableField(*x509Name, &CSSMOID_Description, false, descrEncoding);
487 }
b1ab9ed8
A
488 /*
489 * Search order (take the first one found with a printable
490 * value):
491 * -- common name
c2a06e24 492 * -- Organizational Unit
b1ab9ed8 493 * -- Organization
c2a06e24 494 * -- email address
b1ab9ed8
A
495 * -- field of any kind
496 */
c2a06e24 497 printValue = findPrintableField(*x509Name, &CSSMOID_CommonName, true, encoding);
b1ab9ed8 498 if(printValue != NULL) {
b1ab9ed8
A
499 return printValue;
500 }
c2a06e24 501 printValue = findPrintableField(*x509Name, &CSSMOID_OrganizationalUnitName, false, encoding);
b1ab9ed8
A
502 if(printValue != NULL) {
503 return printValue;
504 }
c2a06e24 505 printValue = findPrintableField(*x509Name, &CSSMOID_OrganizationName, false, encoding);
b1ab9ed8
A
506 if(printValue != NULL) {
507 return printValue;
508 }
c2a06e24
A
509 printValue = findPrintableField(*x509Name, &CSSMOID_EmailAddress, false, encoding);
510 if(printValue != NULL) {
511 return printValue;
512 }
513 /* if we didn't get one of the above names, don't append description */
514 if(description != NULL) {
515 *description = NULL;
516 }
b1ab9ed8 517 /* take anything */
c2a06e24 518 return findPrintableField(*x509Name, NULL, false, encoding);
b1ab9ed8
A
519}
520
521/*
522 * Infer printable label for a given an CSSM_X509_NAME. Returns NULL
523 * if no appropriate printable name found.
524 */
525const CSSM_DATA *SecInferLabelFromX509Name(
526 const CSSM_X509_NAME *x509Name)
527{
528 /* callees of this routine don't care about the encoding */
529 CFStringBuiltInEncodings encoding = kCFStringEncodingASCII;
530 return inferLabelFromX509Name(x509Name, &encoding, NULL, &encoding);
531}
532
533
534void
535Certificate::inferLabel(bool addLabel, CFStringRef *rtnString)
536{
537 StLock<Mutex>_(mMutex);
c2a06e24 538 // Set PrintName and optionally the Alias attribute for this certificate, based on the
b1ab9ed8
A
539 // X509 SubjectAltName and SubjectName.
540 const CSSM_DATA *printName = NULL;
541 const CSSM_DATA *description = NULL;
542 std::vector<CssmData> emailAddresses;
543 CSSM_DATA puntData;
544 CssmAutoData printPlusDescr(Allocator::standard());
545 CssmData printPlusDescData;
546 CFStringBuiltInEncodings printEncoding = kCFStringEncodingUTF8;
547 CFStringBuiltInEncodings descrEncoding = kCFStringEncodingUTF8;
c2a06e24 548
b1ab9ed8
A
549 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
550 const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
551 CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
552 const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
553 CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
554
4d3cab3d 555 getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
b1ab9ed8
A
556
557 if (snValue && snValue->Data)
558 {
559 const CSSM_X509_NAME &x509Name = *(const CSSM_X509_NAME *)snValue->Data;
c2a06e24 560 printName = inferLabelFromX509Name(&x509Name, &printEncoding,
b1ab9ed8
A
561 &description, &descrEncoding);
562 if (printName)
563 {
564 /* Don't ever use "Thawte Freemail Member" as the label for a cert. Instead force
565 a fall back on the email address. */
566 const char tfm[] = "Thawte Freemail Member";
c2a06e24 567 if ( (printName->Length == sizeof(tfm) - 1) &&
b1ab9ed8
A
568 !memcmp(printName->Data, tfm, sizeof(tfm) - 1)) {
569 printName = NULL;
570 }
571 }
572 }
573
574 /* Do a check to see if a '\0' was at the end of printName and strip it. */
575 CssmData cleanedUpPrintName;
c2a06e24 576 if((printName != NULL) &&
b1ab9ed8
A
577 (printName->Length != 0) &&
578 (printEncoding != kCFStringEncodingISOLatin1) &&
579 (printEncoding != kCFStringEncodingUnicode) &&
580 (printName->Data[printName->Length - 1] == '\0')) {
581 cleanedUpPrintName.Data = printName->Data;
582 cleanedUpPrintName.Length = printName->Length - 1;
583 printName = &cleanedUpPrintName;
584 }
c2a06e24
A
585
586 if((printName != NULL) && (description != NULL) && (description->Length != 0))
b1ab9ed8 587 {
c2a06e24
A
588 /*
589 * Munge Print Name (which in this case is the CommonName) and Description
b1ab9ed8 590 * together with the Description in parentheses. We convert from whatever
c2a06e24 591 * format Print Name and Description are in to UTF8 here.
b1ab9ed8
A
592 */
593 CFRef<CFMutableStringRef> combo(CFStringCreateMutable(NULL, 0));
594 CFRef<CFStringRef> cfPrint(CFStringCreateWithBytes(NULL, printName->Data,
595 (CFIndex)printName->Length, printEncoding, true));
596 CssmData cleanedUpDescr(description->Data, description->Length);
597 if ((cleanedUpDescr.Data[cleanedUpDescr.Length - 1] == '\0') &&
598 (descrEncoding != kCFStringEncodingISOLatin1) &&
599 (descrEncoding != kCFStringEncodingUnicode)) {
600 cleanedUpDescr.Length--;
601 }
602 CFRef<CFStringRef> cfDesc(CFStringCreateWithBytes(NULL, cleanedUpDescr.Data,
603 (CFIndex)cleanedUpDescr.Length, descrEncoding, true));
604 CFStringAppend(combo, cfPrint);
605 CFStringAppendCString(combo, " (", kCFStringEncodingASCII);
606 CFStringAppend(combo, cfDesc);
607 CFStringAppendCString(combo, ")", kCFStringEncodingASCII);
608 CFRef<CFDataRef> comboData(CFStringCreateExternalRepresentation(NULL, combo,
609 kCFStringEncodingUTF8, 0));
610 printPlusDescr.copy(CFDataGetBytePtr(comboData), CFDataGetLength(comboData));
611 printPlusDescData = printPlusDescr;
612 printName = &printPlusDescData;
613 printEncoding = kCFStringEncodingUTF8;
614 }
c2a06e24 615
b1ab9ed8
A
616 if (printName == NULL)
617 {
618 /* If the we couldn't find a label use the emailAddress instead. */
619 if (!emailAddresses.empty())
620 printName = &emailAddresses[0];
621 else
622 {
623 /* punt! */
624 puntData.Data = (uint8 *)"X509 Certificate";
625 puntData.Length = 16;
626 printName = &puntData;
627 }
628 printEncoding = kCFStringEncodingUTF8;
629 }
630
631 /* If we couldn't find an email address just use the printName which might be the url or something else useful. */
632 if (emailAddresses.empty())
633 emailAddresses.push_back(CssmData::overlay(*printName));
634
635 /* What do we do with the inferred label - return it or add it mDbAttributes? */
636 if (addLabel)
637 {
638 mDbAttributes->add(Schema::kX509CertificatePrintName, *printName);
639 CssmDbAttributeData &attrData = mDbAttributes->add(Schema::kX509CertificateAlias);
640
641 /* Add the email addresses to attrData and normalize them. */
642 uint32 ix = 0;
643 for (std::vector<CssmData>::const_iterator it = emailAddresses.begin(); it != emailAddresses.end(); ++it, ++ix)
644 {
645 /* Add the email address using the allocator from mDbAttributes. */
646 attrData.add(*it, *mDbAttributes);
647 /* Normalize the emailAddresses in place since attrData already copied it. */
648 normalizeEmailAddress(attrData.Value[ix]);
649 }
650 }
651
652 if (rtnString)
653 {
654 CFStringBuiltInEncodings testEncoding = printEncoding;
655 if(testEncoding == kCFStringEncodingISOLatin1) {
656 // try UTF-8 first
657 testEncoding = kCFStringEncodingUTF8;
658 }
659 *rtnString = CFStringCreateWithBytes(NULL, printName->Data,
660 (CFIndex)printName->Length, testEncoding, true);
661 if(*rtnString == NULL && printEncoding == kCFStringEncodingISOLatin1) {
662 // string cannot be represented in UTF-8, fall back to ISO Latin 1
663 *rtnString = CFStringCreateWithBytes(NULL, printName->Data,
664 (CFIndex)printName->Length, printEncoding, true);
665 }
666 }
667
668 // Clean up
669 if (snValue)
670 releaseFieldValue(snOid, snValue);
671 if (sanValues)
672 releaseFieldValues(sanOid, sanValues);
673}
674
675void
676Certificate::populateAttributes()
677{
678 StLock<Mutex>_(mMutex);
679 if (mPopulated)
680 return;
681
682 addParsedAttribute(Schema::attributeInfo(kSecSubjectItemAttr), CSSMOID_X509V1SubjectName);
683 addParsedAttribute(Schema::attributeInfo(kSecIssuerItemAttr), CSSMOID_X509V1IssuerName);
684 addParsedAttribute(Schema::attributeInfo(kSecSerialNumberItemAttr), CSSMOID_X509V1SerialNumber);
685
686 addSubjectKeyIdentifier();
687
688 if(!mHaveTypeAndEncoding)
689 MacOSError::throwMe(errSecDataNotAvailable); // @@@ Or some other error.
690
691 // Adjust mType based on the actual version of the cert.
692 CSSM_DATA_PTR versionPtr = copyFirstFieldValue(CSSMOID_X509V1Version);
693 if (versionPtr && versionPtr->Data && versionPtr->Length == sizeof(uint32))
694 {
695 mType = CSSM_CERT_X_509v1 + (*reinterpret_cast<uint32 *>(versionPtr->Data));
696 }
697 else
698 mType = CSSM_CERT_X_509v1;
699
700 releaseFieldValue(CSSMOID_X509V1Version, versionPtr);
701
702 mDbAttributes->add(Schema::attributeInfo(kSecCertTypeItemAttr), mType);
703 mDbAttributes->add(Schema::attributeInfo(kSecCertEncodingItemAttr), mEncoding);
704 mDbAttributes->add(Schema::attributeInfo(kSecPublicKeyHashItemAttr), publicKeyHash());
705 inferLabel(true);
706
707 mPopulated = true;
708}
709
710const CssmData &
711Certificate::data()
712{
713 StLock<Mutex>_(mMutex);
714 CssmDataContainer *data = mData.get();
715 if (!data && mKeychain)
716 {
717 // Make sure mUniqueId is set.
718 dbUniqueRecord();
719 CssmDataContainer _data;
720 mData = NULL;
721 /* new data allocated by CSPDL, implicitly freed by CssmDataContainer */
c2a06e24 722 mUniqueId->get(NULL, &_data);
b1ab9ed8 723 /* this saves a copy to be freed at destruction and to be passed to caller */
427c49bc 724 setData((UInt32)_data.length(), _data.data());
b1ab9ed8
A
725 return *mData.get();
726 }
727
728 // If the data hasn't been set we can't return it.
729 if (!data)
730 MacOSError::throwMe(errSecDataNotAvailable);
731
732 return *data;
733}
734
427c49bc
A
735CFHashCode Certificate::hash()
736{
737 (void)data(); // ensure that mData is set up
738 return ItemImpl::hash();
739}
740
b1ab9ed8
A
741CSSM_CERT_TYPE
742Certificate::type()
743{
744 StLock<Mutex>_(mMutex);
745 if (!mHaveTypeAndEncoding)
746 {
747 SecKeychainAttribute attr;
748 attr.tag = kSecCertTypeItemAttr;
749 attr.data = &mType;
750 attr.length = sizeof(mType);
751 getAttribute(attr, NULL);
752 }
753
754 return mType;
755}
756
757CSSM_CERT_ENCODING
758Certificate::encoding()
759{
760 StLock<Mutex>_(mMutex);
761 if (!mHaveTypeAndEncoding)
762 {
763 SecKeychainAttribute attr;
764 attr.tag = kSecCertEncodingItemAttr;
765 attr.data = &mEncoding;
766 attr.length = sizeof(mEncoding);
767 getAttribute(attr, NULL);
768 }
769
770 return mEncoding;
771}
772
773const CSSM_X509_ALGORITHM_IDENTIFIER_PTR
774Certificate::algorithmID()
775{
776 StLock<Mutex>_(mMutex);
777 if (!mV1SubjectPublicKeyCStructValue)
778 mV1SubjectPublicKeyCStructValue = copyFirstFieldValue(CSSMOID_X509V1SubjectPublicKeyCStruct);
779
780 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *info = (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO *)mV1SubjectPublicKeyCStructValue->Data;
781 CSSM_X509_ALGORITHM_IDENTIFIER *algid = &info->algorithm;
782 return algid;
783}
784
427c49bc
A
785CFDataRef
786Certificate::sha1Hash()
787{
788 StLock<Mutex>_(mMutex);
789 if (!mSha1Hash) {
790 SecCertificateRef certRef = handle(false);
791 CFAllocatorRef allocRef = (certRef) ? CFGetAllocator(certRef) : NULL;
792 CSSM_DATA certData = data();
793 if (certData.Length == 0 || !certData.Data) {
794 MacOSError::throwMe(errSecDataNotAvailable);
795 }
796 const UInt8 *dataPtr = (const UInt8 *)certData.Data;
797 CFIndex dataLen = (CFIndex)certData.Length;
798 CFMutableDataRef digest = CFDataCreateMutable(allocRef, CC_SHA1_DIGEST_LENGTH);
799 CFDataSetLength(digest, CC_SHA1_DIGEST_LENGTH);
800 CCDigest(kCCDigestSHA1, dataPtr, dataLen, CFDataGetMutableBytePtr(digest));
801 mSha1Hash = digest;
802 }
803 return mSha1Hash; /* object is owned by our instance; caller should NOT release it */
804}
805
b1ab9ed8
A
806CFStringRef
807Certificate::commonName()
808{
809 StLock<Mutex>_(mMutex);
810 return distinguishedName(&CSSMOID_X509V1SubjectNameCStruct, &CSSMOID_CommonName);
811}
812
813CFStringRef
814Certificate::distinguishedName(const CSSM_OID *sourceOid, const CSSM_OID *componentOid)
815{
816 StLock<Mutex>_(mMutex);
817 CFStringRef rtnString = NULL;
818 CSSM_DATA_PTR fieldValue = copyFirstFieldValue(*sourceOid);
819 CSSM_X509_NAME_PTR x509Name = (CSSM_X509_NAME_PTR)fieldValue->Data;
820 const CSSM_DATA *printValue = NULL;
821 CFStringBuiltInEncodings encoding;
c2a06e24 822
b1ab9ed8 823 if (fieldValue && fieldValue->Data)
c2a06e24 824 printValue = findPrintableField(*x509Name, componentOid, true, &encoding);
b1ab9ed8
A
825
826 if (printValue)
827 rtnString = CFStringCreateWithBytes(NULL, printValue->Data,
828 CFIndex(printValue->Length), encoding, true);
829
830 releaseFieldValue(*sourceOid, fieldValue);
831
832 return rtnString;
833}
834
835
836/*
c2a06e24 837 * Return a CFString containing the first email addresses for this certificate, based on the
b1ab9ed8
A
838 * X509 SubjectAltName and SubjectName.
839 */
840CFStringRef
841Certificate::copyFirstEmailAddress()
842{
843 StLock<Mutex>_(mMutex);
844 CFStringRef rtnString;
845
846 const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
847 CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
848 const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
849 CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
850 std::vector<CssmData> emailAddresses;
851
4d3cab3d 852 getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
b1ab9ed8
A
853 if (emailAddresses.empty())
854 rtnString = NULL;
855 else
856 {
857 /* Encoding is kCFStringEncodingUTF8 since the string is either
858 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
859 rtnString = CFStringCreateWithBytes(NULL, emailAddresses[0].Data,
860 (CFIndex)emailAddresses[0].Length, kCFStringEncodingUTF8, true);
861 }
862
863 // Clean up
864 if (snValue)
865 releaseFieldValue(snOid, snValue);
866 if (sanValues)
867 releaseFieldValues(sanOid, sanValues);
868
869 return rtnString;
870}
871
4d3cab3d
A
872/*
873 * Return a CFArray containing the DNS hostnames for this certificate, based on the
874 * X509 SubjectAltName and SubjectName.
875 */
876CFArrayRef
877Certificate::copyDNSNames()
878{
879 StLock<Mutex>_(mMutex);
880 CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
881 std::vector<CssmData> dnsNames;
882
883 // Find the SubjectAltName fields, if any, and extract the GNT_DNSName entries from all of them
884 const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
885 CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
886
887 const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
888 CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
889
890 getNames(sanValues, snValue, GNT_DNSName, dnsNames);
891
892 for (std::vector<CssmData>::const_iterator it = dnsNames.begin(); it != dnsNames.end(); ++it)
893 {
894 /* Encoding is kCFStringEncodingUTF8 since the string is either
895 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
896 CFStringRef string = CFStringCreateWithBytes(NULL, it->Data, static_cast<CFIndex>(it->Length), kCFStringEncodingUTF8, true);
420ff9d9
A
897 /* Be prepared for improperly formatted (non-UTF8) strings! */
898 if (!string) continue;
4d3cab3d
A
899 CFArrayAppendValue(array, string);
900 CFRelease(string);
901 }
902
903 // Clean up
904 if (snValue)
905 releaseFieldValue(snOid, snValue);
906 if (sanValues)
907 releaseFieldValues(sanOid, sanValues);
908
909 return array;
910}
911
b1ab9ed8 912/*
c2a06e24 913 * Return a CFArray containing the email addresses for this certificate, based on the
b1ab9ed8
A
914 * X509 SubjectAltName and SubjectName.
915 */
916CFArrayRef
917Certificate::copyEmailAddresses()
918{
919 StLock<Mutex>_(mMutex);
920 CFMutableArrayRef array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
921 std::vector<CssmData> emailAddresses;
922
923 // Find the SubjectAltName fields, if any, and extract all the GNT_RFC822Name entries from all of them
924 const CSSM_OID &sanOid = CSSMOID_SubjectAltName;
925 CSSM_DATA_PTR *sanValues = copyFieldValues(sanOid);
926
927 const CSSM_OID &snOid = CSSMOID_X509V1SubjectNameCStruct;
928 CSSM_DATA_PTR snValue = copyFirstFieldValue(snOid);
929
4d3cab3d 930 getNames(sanValues, snValue, GNT_RFC822Name, emailAddresses);
b1ab9ed8
A
931
932 for (std::vector<CssmData>::const_iterator it = emailAddresses.begin(); it != emailAddresses.end(); ++it)
933 {
934 /* Encoding is kCFStringEncodingUTF8 since the string is either
935 PRINTABLE_STRING, IA5_STRING, T61_STRING or PKIX_UTF8_STRING. */
936 CFStringRef string = CFStringCreateWithBytes(NULL, it->Data, static_cast<CFIndex>(it->Length), kCFStringEncodingUTF8, true);
420ff9d9
A
937 /* Be prepared for improperly formatted (non-UTF8) strings! */
938 if (!string) continue;
b1ab9ed8
A
939 CFArrayAppendValue(array, string);
940 CFRelease(string);
941 }
942
943 // Clean up
944 if (snValue)
945 releaseFieldValue(snOid, snValue);
946 if (sanValues)
947 releaseFieldValues(sanOid, sanValues);
948
949 return array;
950}
951
952const CSSM_X509_NAME_PTR
953Certificate::subjectName()
954{
955 StLock<Mutex>_(mMutex);
956 if (!mV1SubjectNameCStructValue)
957 if ((mV1SubjectNameCStructValue = copyFirstFieldValue(CSSMOID_X509V1SubjectNameCStruct)) == NULL)
958 return NULL;
959
960 return (const CSSM_X509_NAME_PTR)mV1SubjectNameCStructValue->Data;
961}
962
963const CSSM_X509_NAME_PTR
964Certificate::issuerName()
965{
966 StLock<Mutex>_(mMutex);
967 if (!mV1IssuerNameCStructValue)
968 if ((mV1IssuerNameCStructValue = copyFirstFieldValue(CSSMOID_X509V1IssuerNameCStruct)) == NULL)
969 return NULL;
970
971 return (const CSSM_X509_NAME_PTR)mV1IssuerNameCStructValue->Data;
972}
973
974CSSM_CL_HANDLE
975Certificate::clHandle()
976{
977 StLock<Mutex>_(mMutex);
978 if (!mCL)
979 mCL = clForType(type());
980
981 return mCL->handle();
982}
983
984bool
985Certificate::operator < (Certificate &other)
986{
987 // Certificates in different keychains are considered equal if data is equal
988 // Note that the Identity '<' operator relies on this assumption.
989 return data() < other.data();
990}
991
992bool
993Certificate::operator == (Certificate &other)
994{
995 // Certificates in different keychains are considered equal if data is equal
996 // Note that the Identity '==' operator relies on this assumption.
997 return data() == other.data();
998}
999
b1ab9ed8
A
1000void
1001Certificate::update()
1002{
1003 ItemImpl::update();
1004}
1005
1006Item
1007Certificate::copyTo(const Keychain &keychain, Access *newAccess)
1008{
1009 StLock<Mutex>_(mMutex);
1010 /* Certs can't have access controls. */
1011 if (newAccess)
1012 MacOSError::throwMe(errSecNoAccessForItem);
1013
1014 Item item(new Certificate(data(), type(), encoding()));
1015 keychain->add(item);
1016 return item;
1017}
1018
1019void
1020Certificate::didModify()
1021{
1022}
1023
1024PrimaryKey
1025Certificate::add(Keychain &keychain)
1026{
1027 StLock<Mutex>_(mMutex);
1028 // If we already have a Keychain we can't be added.
1029 if (mKeychain)
1030 MacOSError::throwMe(errSecDuplicateItem);
1031
1032 populateAttributes();
1033
1034 CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();
1035
1036 Db db(keychain->database());
1037 // add the item to the (regular) db
1038 try
1039 {
1040 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
1041 }
1042 catch (const CssmError &e)
1043 {
1044 if (e.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE)
1045 throw;
1046
1047 // Create the cert relation and try again.
1048 db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE,
1049 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
1050 Schema::X509CertificateSchemaAttributeCount,
1051 Schema::X509CertificateSchemaAttributeList,
1052 Schema::X509CertificateSchemaIndexCount,
1053 Schema::X509CertificateSchemaIndexList);
1054 keychain->keychainSchema()->didCreateRelation(
1055 CSSM_DL_DB_RECORD_X509_CERTIFICATE,
1056 "CSSM_DL_DB_RECORD_X509_CERTIFICATE",
1057 Schema::X509CertificateSchemaAttributeCount,
1058 Schema::X509CertificateSchemaAttributeList,
1059 Schema::X509CertificateSchemaIndexCount,
1060 Schema::X509CertificateSchemaIndexList);
1061
1062 mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
1063 }
1064
1065 mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
1066 mKeychain = keychain;
1067
1068 return mPrimaryKey;
1069}
1070
1071SecPointer<KeyItem>
1072Certificate::publicKey()
1073{
1074 StLock<Mutex>_(mMutex);
1075 SecPointer<KeyItem> keyItem;
1076 // Return a CSSM_DATA_PTR with the value of the first field specified by field.
1077 // Caller must call releaseFieldValue to free the storage allocated by this call.
1078 // call OSStatus SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey); to retrieve
1079
1080 CSSM_DATA_PTR keyPtr = copyFirstFieldValue(CSSMOID_CSSMKeyStruct);
1081 if (keyPtr && keyPtr->Data)
1082 {
1083 CssmClient::CSP csp(gGuidAppleCSP);
1084 CssmKey *cssmKey = reinterpret_cast<CssmKey *>(keyPtr->Data);
1085 CssmClient::Key key(csp, *cssmKey);
1086 keyItem = new KeyItem(key);
1087 // Clear out KeyData since KeyItem() takes over ownership of the key, and we don't want it getting released.
1088 cssmKey->KeyData.Data = NULL;
1089 cssmKey->KeyData.Length = 0;
1090 }
1091
1092 releaseFieldValue(CSSMOID_CSSMKeyStruct, keyPtr);
1093
1094 return keyItem;
1095}
1096
1097// This function "borrowed" from the X509 CL, which is (currently) linked into
1098// the Security.framework as a built-in plugin.
1099extern "C" bool getField_normRDN_NSS (
1100 const CSSM_DATA &derName,
1101 uint32 &numFields, // RETURNED (if successful, 0 or 1)
1102 CssmOwnedData &fieldValue); // RETURNED
1103
1104KCCursor
1105Certificate::cursorForIssuerAndSN(const StorageManager::KeychainList &keychains, const CssmData &issuer, const CssmData &serialNumber)
1106{
1107 CssmAutoData fieldValue(Allocator::standard(Allocator::normal));
1108 uint32 numFields;
c2a06e24 1109
b1ab9ed8
A
1110 // We need to decode issuer, normalize it, then re-encode it
1111 if (!getField_normRDN_NSS(issuer, numFields, fieldValue))
1112 MacOSError::throwMe(errSecDataNotAvailable);
c2a06e24 1113
b1ab9ed8
A
1114 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
1115 KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
1116 cursor->conjunctive(CSSM_DB_AND);
1117 cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateIssuer, fieldValue.get());
1118 cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSerialNumber, serialNumber);
c2a06e24 1119
b1ab9ed8
A
1120 return cursor;
1121}
1122
1123KCCursor
1124Certificate::cursorForIssuerAndSN_CF(const StorageManager::KeychainList &keychains, CFDataRef issuer, CFDataRef serialNumber)
1125{
1126 // This assumes a normalized issuer
1127 CSSM_DATA issuerCSSM, serialNumberCSSM;
c2a06e24 1128
b1ab9ed8
A
1129 issuerCSSM.Length = CFDataGetLength(issuer);
1130 issuerCSSM.Data = const_cast<uint8 *>(CFDataGetBytePtr(issuer));
c2a06e24 1131
b1ab9ed8
A
1132 serialNumberCSSM.Length = CFDataGetLength(serialNumber);
1133 serialNumberCSSM.Data = const_cast<uint8 *>(CFDataGetBytePtr(serialNumber));
1134
1135 // Code basically copied from SecKeychainSearchCreateFromAttributes and SecKeychainSearchCopyNext:
1136 KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
1137 cursor->conjunctive(CSSM_DB_AND);
1138 cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateIssuer, issuerCSSM);
1139 cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSerialNumber, serialNumberCSSM);
c2a06e24 1140
b1ab9ed8
A
1141 return cursor;
1142}
1143
1144KCCursor
1145Certificate::cursorForSubjectKeyID(const StorageManager::KeychainList &keychains, const CssmData &subjectKeyID)
1146{
1147 KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
1148 cursor->conjunctive(CSSM_DB_AND);
1149 cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateSubjectKeyIdentifier, subjectKeyID);
1150
1151 return cursor;
1152}
1153
1154KCCursor
1155Certificate::cursorForEmail(const StorageManager::KeychainList &keychains, const char *emailAddress)
1156{
1157 KCCursor cursor(keychains, kSecCertificateItemClass, NULL);
1158 if (emailAddress)
1159 {
1160 cursor->conjunctive(CSSM_DB_AND);
1161 CssmSelectionPredicate &pred = cursor->add(CSSM_DB_EQUAL, Schema::kX509CertificateAlias, emailAddress);
1162 /* Normalize the emailAddresses in place since cursor already copied it. */
1163 normalizeEmailAddress(pred.Attribute.Value[0]);
1164 }
1165
1166 return cursor;
1167}
1168
1169SecPointer<Certificate>
1170Certificate::findInKeychain(const StorageManager::KeychainList &keychains)
1171{
1172 StLock<Mutex>_(mMutex);
1173 const CSSM_OID &issuerOid = CSSMOID_X509V1IssuerName;
1174 CSSM_DATA_PTR issuerPtr = copyFirstFieldValue(issuerOid);
1175 CssmData issuer(issuerPtr->Data, issuerPtr->Length);
1176
1177 const CSSM_OID &serialOid = CSSMOID_X509V1SerialNumber;
1178 CSSM_DATA_PTR serialPtr = copyFirstFieldValue(serialOid);
1179 CssmData serial(serialPtr->Data, serialPtr->Length);
1180
1181 SecPointer<Certificate> foundCert = NULL;
1182 try {
1183 foundCert = findByIssuerAndSN(keychains, issuer, serial);
1184 } catch (...) {
1185 foundCert = NULL;
1186 }
1187
1188 releaseFieldValue(issuerOid, issuerPtr);
c2a06e24 1189 releaseFieldValue(serialOid, serialPtr);
b1ab9ed8
A
1190
1191 return foundCert;
1192}
1193
1194SecPointer<Certificate>
1195Certificate::findByIssuerAndSN(const StorageManager::KeychainList &keychains, const CssmData &issuer, const CssmData &serialNumber)
1196{
1197 Item item;
1198 if (!cursorForIssuerAndSN(keychains, issuer, serialNumber)->next(item))
1199 CssmError::throwMe(errSecItemNotFound);
1200
1201 return static_cast<Certificate *>(&*item);
1202}
1203
1204SecPointer<Certificate>
1205Certificate::findBySubjectKeyID(const StorageManager::KeychainList &keychains, const CssmData &subjectKeyID)
1206{
1207 Item item;
1208 if (!cursorForSubjectKeyID(keychains, subjectKeyID)->next(item))
1209 CssmError::throwMe(errSecItemNotFound);
1210
1211 return static_cast<Certificate *>(&*item);
1212}
1213
1214SecPointer<Certificate>
1215Certificate::findByEmail(const StorageManager::KeychainList &keychains, const char *emailAddress)
1216{
1217 Item item;
1218 if (!cursorForEmail(keychains, emailAddress)->next(item))
1219 CssmError::throwMe(errSecItemNotFound);
1220
1221 return static_cast<Certificate *>(&*item);
1222}
1223
1224/* Normalize emailAddresses in place. */
1225void
1226Certificate::normalizeEmailAddress(CSSM_DATA &emailAddress)
1227{
1228 /* Do a check to see if a '\0' was at the end of emailAddress and strip it. */
1229 if (emailAddress.Length && emailAddress.Data[emailAddress.Length - 1] == '\0')
1230 emailAddress.Length--;
1231 bool foundAt = false;
1232 for (uint32 ix = 0; ix < emailAddress.Length; ++ix)
1233 {
1234 uint8 ch = emailAddress.Data[ix];
1235 if (foundAt)
1236 {
1237 if ('A' <= ch && ch <= 'Z')
1238 emailAddress.Data[ix] = ch + 'a' - 'A';
1239 }
1240 else if (ch == '@')
1241 foundAt = true;
1242 }
1243}
1244
1245void
4d3cab3d 1246Certificate::getNames(CSSM_DATA_PTR *sanValues, CSSM_DATA_PTR snValue, CE_GeneralNameType generalNameType, std::vector<CssmData> &names)
b1ab9ed8 1247{
4d3cab3d
A
1248 // Get the DNS host names or RFC822 email addresses for this certificate (depending on generalNameType),
1249 // within the X509 SubjectAltName and SubjectName.
b1ab9ed8 1250
4d3cab3d 1251 // Find the SubjectAltName fields, if any, and extract the nameType entries from all of them
b1ab9ed8
A
1252 if (sanValues)
1253 {
1254 for (CSSM_DATA_PTR *sanIx = sanValues; *sanIx; ++sanIx)
1255 {
1256 CSSM_DATA_PTR sanValue = *sanIx;
1257 if (sanValue && sanValue->Data)
1258 {
1259 CSSM_X509_EXTENSION *cssmExt = (CSSM_X509_EXTENSION *)sanValue->Data;
1260 CE_GeneralNames *parsedValue = (CE_GeneralNames *)cssmExt->value.parsedValue;
c2a06e24 1261
4d3cab3d 1262 /* Grab all the values that are of the specified name type. */
b1ab9ed8
A
1263 for (uint32 i = 0; i < parsedValue->numNames; ++i)
1264 {
4d3cab3d 1265 if (parsedValue->generalName[i].nameType == generalNameType)
b1ab9ed8
A
1266 {
1267 if (parsedValue->generalName[i].berEncoded) // can't handle this
1268 continue;
c2a06e24 1269
4d3cab3d 1270 names.push_back(CssmData::overlay(parsedValue->generalName[i].name));
b1ab9ed8
A
1271 }
1272 }
1273 }
1274 }
1275 }
1276
4d3cab3d 1277 if (names.empty() && snValue && snValue->Data)
b1ab9ed8
A
1278 {
1279 const CSSM_X509_NAME &x509Name = *(const CSSM_X509_NAME *)snValue->Data;
1280 for (uint32 rdnDex = 0; rdnDex < x509Name.numberOfRDNs; rdnDex++)
1281 {
c2a06e24 1282 const CSSM_X509_RDN *rdnPtr =
b1ab9ed8
A
1283 &x509Name.RelativeDistinguishedName[rdnDex];
1284 for (uint32 tvpDex = 0; tvpDex < rdnPtr->numberOfPairs; tvpDex++)
1285 {
c2a06e24 1286 const CSSM_X509_TYPE_VALUE_PAIR *tvpPtr =
b1ab9ed8 1287 &rdnPtr->AttributeTypeAndValue[tvpDex];
c2a06e24 1288
4d3cab3d
A
1289 /* type/value pair: match caller's specified type */
1290 if (GNT_RFC822Name == generalNameType) {
1291 if (((tvpPtr->type.Length != CSSMOID_EmailAddress.Length) ||
1292 memcmp(tvpPtr->type.Data, CSSMOID_EmailAddress.Data, CSSMOID_EmailAddress.Length))) {
1293 continue;
1294 }
1295 }
1296 if (GNT_DNSName == generalNameType) {
1297 if (((tvpPtr->type.Length != CSSMOID_CommonName.Length) ||
1298 memcmp(tvpPtr->type.Data, CSSMOID_CommonName.Data, CSSMOID_CommonName.Length))) {
1299 continue;
1300 }
b1ab9ed8
A
1301 }
1302
1303 /* printable? */
1304 switch (tvpPtr->valueType)
1305 {
1306 case BER_TAG_PRINTABLE_STRING:
1307 case BER_TAG_IA5_STRING:
1308 case BER_TAG_T61_STRING:
1309 case BER_TAG_PKIX_UTF8_STRING:
1310 /* success */
4d3cab3d 1311 names.push_back(CssmData::overlay(tvpPtr->value));
b1ab9ed8
A
1312 break;
1313 default:
1314 break;
1315 }
1316 } /* for each pair */
1317 } /* for each RDN */
1318 }
1319}
1320
1321void Certificate::willRead()
1322{
1323 populateAttributes();
1324}
1325
1326Boolean Certificate::isSelfSigned()
c2a06e24 1327{
b1ab9ed8
A
1328 StLock<Mutex>_(mMutex);
1329 CSSM_DATA_PTR issuer = NULL;
1330 CSSM_DATA_PTR subject = NULL;
427c49bc 1331 OSStatus ortn = errSecSuccess;
b1ab9ed8
A
1332 Boolean brtn = false;
1333
1334 issuer = copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd);
1335 subject = copyFirstFieldValue(CSSMOID_X509V1SubjectNameStd);
1336 if((issuer == NULL) || (subject == NULL)) {
427c49bc 1337 ortn = errSecParam;
b1ab9ed8
A
1338 }
1339 else if((issuer->Length == subject->Length) &&
1340 !memcmp(issuer->Data, subject->Data, issuer->Length)) {
1341 brtn = true;
1342 }
1343 if(brtn) {
1344 /* names match: verify signature */
1345 CSSM_RETURN crtn;
1346 CSSM_DATA certData = data();
1347 crtn = CSSM_CL_CertVerify(clHandle(), 0,
1348 &certData, &certData, NULL, 0);
1349 if(crtn) {
1350 brtn = false;
1351 }
1352 }
1353 if(issuer) {
1354 releaseFieldValue(CSSMOID_X509V1IssuerNameStd, issuer);
1355 }
1356 if(subject) {
1357 releaseFieldValue(CSSMOID_X509V1SubjectNameStd, subject);
1358 }
1359 if(ortn) {
1360 MacOSError::throwMe(ortn);
1361 }
1362 return brtn;
1363}