2 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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 xLicense.
21 * @APPLE_LICENSE_HEADER_END@
24 #import <Security/Security.h>
25 #import <Security/SecCertificatePriv.h>
27 #include "keychain_regressions.h"
28 #include "kc-helpers.h"
29 #include "kc-item-helpers.h"
30 #include "kc-key-helpers.h"
32 #import <Foundation/Foundation.h>
34 #include <Security/SecCertificate.h>
35 #include <Security/SecPolicyPriv.h>
36 #include <Security/SecPolicySearch.h>
37 #include <Security/SecIdentity.h>
38 #include <Security/SecIdentityPriv.h>
39 #include <Security/SecIdentitySearch.h>
40 #include <Security/SecIdentitySearchPriv.h>
41 #include <Security/SecTrust.h>
42 #include <Security/SecKeychain.h>
43 #include <Security/SecKeychainItem.h>
44 #include <Security/SecKeychainItemPriv.h>
45 #include <SecurityFoundation/SFCertificateData.h>
46 #include <Security/oidsalg.h>
49 static NSString* printDataAsHex(
52 if (!d || !d->Data) return NULL;
55 CSSM_SIZE len = d->Length;
57 NSString *str = [NSString string];
59 for(i=0; i<len; i++) {
60 str = [str stringByAppendingFormat:@"%02X", ((unsigned char *)cp)[i]];
65 static NSString* printDigest(
66 CSSM_ALGORITHMS digestAlgorithm,
67 const CSSM_DATA* thingToDigest)
71 uint8 buf[64]; // we really only expect 16 or 20 byte digests, but...
74 digest.Length = sizeof(buf);
75 crtn = SecDigestGetData (digestAlgorithm, &digest, thingToDigest);
77 if (crtn || !digest.Length) return NULL;
78 return printDataAsHex(&digest);
81 static void printCertificate(SecCertificateRef certificate, SecPolicyRef policy, int ordinalValue)
83 CSSM_DATA certData = { 0, nil };
84 (void) SecCertificateGetData(certificate, &certData);
85 NSString *digestStr = printDigest(CSSM_ALGID_MD5, &certData);
86 const char *digest = [digestStr UTF8String];
87 fprintf(stdout, "%3d) %s", ordinalValue, (digest) ? digest : "!-- unable to get md5 digest --!");
89 CFStringRef label=nil;
90 OSStatus status = SecCertificateInferLabel(certificate, &label);
94 if (!CFStringGetCString(label, buf, 1024-1, kCFStringEncodingUTF8))
96 fprintf(stdout, " \"%s\"", buf);
100 // Default to X.509 Basic if no policy was specified
102 SecPolicySearchRef policySearch = NULL;
103 if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &policySearch)==noErr) {
104 SecPolicySearchCopyNext(policySearch, &policy);
108 // Create a trust reference, given policy and certificates
109 SecTrustRef trust=nil;
110 NSArray *certificates = [NSArray arrayWithObject:(__bridge id)certificate];
111 status = SecTrustCreateWithCertificates((CFArrayRef)certificates, policy, &trust);
113 SFCertificateData *sfCertData = [[SFCertificateData alloc] initWithCertificate:certificate trust:trust parse:NO];
114 const char *statusStr = [[sfCertData statusString] UTF8String];
115 // Skip the status string if the certificate is valid, but print it otherwise
116 if (statusStr && (strcmp(statusStr, "This certificate is valid") != 0))
117 fprintf(stdout, " (%s)", statusStr);
118 fprintf(stdout, "\n");
121 static BOOL certificateHasExpired(SecCertificateRef certificate)
123 SFCertificateData *sfCertData = [[SFCertificateData alloc] initWithCertificate:certificate trust:nil parse:NO];
124 BOOL result = [sfCertData expired];
129 static void doCertificateSearchForEmailAddress(SecKeychainRef kc, const char *emailAddr, bool showAll)
131 OSStatus status = errSecSuccess;
133 // Enumerate matching certificates
134 fprintf(stdout, "%s certificates matching \"%s\":\n", (showAll) ? "All" : "Valid", emailAddr);
135 SecKeychainSearchRef searchRef;
136 status = SecKeychainSearchCreateForCertificateByEmail(kc, emailAddr, &searchRef);
137 ok_status(status, "%s: SecKeychainSearchCreateForCertificateByEmail", testName);
139 SecCertificateRef preferredCert = nil;
140 CFStringRef emailStr = (emailAddr) ? CFStringCreateWithCStringNoCopy(NULL, emailAddr, kCFStringEncodingUTF8, kCFAllocatorNull) : NULL;
143 SecKeychainItemRef itemRef=nil;
145 while (SecKeychainSearchCopyNext(searchRef, &itemRef)==noErr) {
146 if (showAll || !certificateHasExpired((SecCertificateRef)itemRef)) {
147 printCertificate((SecCertificateRef)itemRef, nil, ++i);
150 // Set this certificate as preferred for this email address
151 ok_status(SecCertificateSetPreferred((SecCertificateRef)itemRef, emailStr, 0), "%s: SecCertificateSetPreferred", testName);
155 is(i, 1, "%s: Wrong number of certificates found", testName);
157 CFRelease(searchRef);
160 // Check that our certificate is new preferred
161 status = SecCertificateCopyPreference(emailStr, (CSSM_KEYUSE) 0, &preferredCert);
162 ok_status(status, "%s: SecCertificateCopyPreference", testName);
165 CFRelease(preferredCert);
170 int kc_06_cert_search_email(int argc, char *const *argv)
172 bool showAll = false;
175 initializeKeychainTests(__FUNCTION__);
177 // Delete any existing preferences for our certificate, but don't test
178 // status since maybe it doesn't exist yet
179 CFMutableDictionaryRef q = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
180 CFDictionarySetValue(q, kSecClass, kSecClassGenericPassword);
181 q = addLabel(q, CFSTR("nobody_certificate@apple.com"));
185 SecKeychainRef kc = getPopulatedTestKeychain();
188 doCertificateSearchForEmailAddress(kc, "nobody_certificate@apple.com", showAll);
190 ok_status(SecKeychainDelete(kc), "%s: SecKeychainDelete", testName);