]>
Commit | Line | Data |
---|---|---|
29654253 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 | // TrustStore.h - Abstract interface to permanent user trust assignments | |
20 | // | |
21 | #include <Security/TrustStore.h> | |
22 | #include <Security/Globals.h> | |
23 | #include <Security/Certificate.h> | |
df0e469f | 24 | #include <Security/KCCursor.h> |
29654253 A |
25 | #include <Security/SecCFTypes.h> |
26 | #include <Security/schema.h> | |
27 | ||
28 | ||
29 | namespace Security { | |
30 | namespace KeychainCore { | |
31 | ||
32 | ||
33 | // | |
34 | // Make and break: trivial | |
35 | // | |
36 | TrustStore::TrustStore(CssmAllocator &alloc) | |
37 | : allocator(alloc), mRootsValid(false), mRootBytes(allocator) | |
38 | { | |
39 | } | |
40 | ||
41 | TrustStore::~TrustStore() | |
42 | { } | |
43 | ||
44 | ||
45 | // | |
46 | // Retrieve the trust setting for a (certificate, policy) pair. | |
47 | // | |
48 | SecTrustUserSetting TrustStore::find(Certificate *cert, Policy *policy) | |
49 | { | |
50 | if (Item item = findItem(cert, policy)) { | |
51 | CssmDataContainer data; | |
52 | item->getData(data); | |
53 | if (data.length() != sizeof(TrustData)) | |
54 | MacOSError::throwMe(errSecInvalidTrustSetting); | |
55 | TrustData &trust = *data.interpretedAs<TrustData>(); | |
56 | if (trust.version != UserTrustItem::currentVersion) | |
57 | MacOSError::throwMe(errSecInvalidTrustSetting); | |
58 | return trust.trust; | |
59 | } else { | |
60 | return kSecTrustResultUnspecified; | |
61 | } | |
62 | } | |
63 | ||
64 | ||
65 | // | |
66 | // Set an individual trust element | |
67 | // | |
68 | void TrustStore::assign(Certificate *cert, Policy *policy, SecTrustUserSetting trust) | |
69 | { | |
70 | TrustData trustData = { UserTrustItem::currentVersion, trust }; | |
71 | if (Item item = findItem(cert, policy)) { | |
72 | // user has a trust setting in a keychain - modify that | |
df0e469f A |
73 | if (trust == kSecTrustResultUnspecified) |
74 | item->keychain()->deleteItem(item); | |
75 | else | |
76 | item->modifyContent(NULL, sizeof(trustData), &trustData); | |
29654253 A |
77 | } else { |
78 | // no trust entry: make one | |
df0e469f A |
79 | if (trust != kSecTrustResultUnspecified) { |
80 | Item item = new UserTrustItem(cert, policy, trustData); | |
81 | if (Keychain location = cert->keychain()) | |
82 | location->add(item); // in the cert's keychain | |
83 | else | |
84 | Keychain::optional(NULL)->add(item); // in the default keychain | |
85 | } | |
29654253 A |
86 | } |
87 | } | |
88 | ||
89 | ||
90 | // | |
91 | // Search the user's configured keychains for a trust setting. | |
92 | // If found, return it (as a TrustItem). Otherwise, return NULL. | |
93 | // Note that this function throws if a "real" error is encountered. | |
94 | // | |
95 | Item TrustStore::findItem(Certificate *cert, Policy *policy) | |
96 | { | |
97 | try { | |
98 | SecKeychainAttribute attrs[2]; | |
df0e469f A |
99 | CssmAutoData certIndex(CssmAllocator::standard()); |
100 | UserTrustItem::makeCertIndex(cert, certIndex); | |
29654253 | 101 | attrs[0].tag = kSecTrustCertAttr; |
df0e469f A |
102 | attrs[0].length = certIndex.length(); |
103 | attrs[0].data = certIndex.data(); | |
29654253 A |
104 | const CssmOid &policyOid = policy->oid(); |
105 | attrs[1].tag = kSecTrustPolicyAttr; | |
106 | attrs[1].length = policyOid.length(); | |
107 | attrs[1].data = policyOid.data(); | |
108 | SecKeychainAttributeList attrList = { 2, attrs }; | |
109 | KCCursor cursor = globals().storageManager.createCursor(CSSM_DL_DB_RECORD_USER_TRUST, &attrList); | |
110 | Item item; | |
111 | if (cursor->next(item)) | |
112 | return item; | |
113 | else | |
114 | return NULL; | |
df0e469f | 115 | } catch (const CssmCommonError &error) { |
29654253 A |
116 | if (error.cssmError() == CSSMERR_DL_INVALID_RECORDTYPE) |
117 | return NULL; // no trust schema, no records, no error | |
118 | throw; | |
119 | } | |
120 | } | |
121 | ||
122 | ||
123 | // | |
124 | // Return the root certificate list. | |
125 | // This list is cached. | |
126 | // | |
127 | CFArrayRef TrustStore::copyRootCertificates() | |
128 | { | |
129 | if (!mRootsValid) { | |
130 | loadRootCertificates(); | |
131 | mCFRoots = NULL; | |
132 | } | |
133 | if (!mCFRoots) { | |
134 | uint32 count = mRoots.size(); | |
df0e469f | 135 | secdebug("anchors", "building %ld CF-style anchor certificates", count); |
29654253 A |
136 | vector<SecCertificateRef> roots(count); |
137 | for (uint32 n = 0; n < count; n++) { | |
df0e469f | 138 | SecPointer<Certificate> cert = new Certificate(mRoots[n], |
29654253 | 139 | CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER); |
df0e469f | 140 | roots[n] = cert->handle(); |
29654253 A |
141 | } |
142 | mCFRoots = CFArrayCreate(NULL, (const void **)&roots[0], count, | |
143 | &kCFTypeArrayCallBacks); | |
144 | for (uint32 n = 0; n < count; n++) | |
145 | CFRelease(roots[n]); // undo CFArray's retain | |
146 | } | |
147 | CFRetain(mCFRoots); | |
148 | return mCFRoots; | |
149 | } | |
150 | ||
151 | void TrustStore::getCssmRootCertificates(CertGroup &rootCerts) | |
152 | { | |
153 | if (!mRootsValid) | |
154 | loadRootCertificates(); | |
155 | rootCerts = CertGroup(CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, CSSM_CERTGROUP_DATA); | |
156 | rootCerts.blobCerts() = &mRoots[0]; | |
157 | rootCerts.count() = mRoots.size(); | |
158 | } | |
159 | ||
160 | void TrustStore::refreshRootCertificates() | |
161 | { | |
162 | if (mRootsValid) { | |
df0e469f | 163 | secdebug("anchors", "clearing %ld cached anchor certificates", mRoots.size()); |
29654253 A |
164 | |
165 | // throw out the CF version | |
166 | if (mCFRoots) { | |
167 | CFRelease(mCFRoots); | |
168 | mCFRoots = NULL; | |
169 | } | |
170 | ||
171 | // release cert memory | |
172 | mRootBytes.reset(); | |
173 | mRoots.clear(); | |
174 | ||
175 | // all pristine again | |
176 | mRootsValid = false; | |
177 | } | |
178 | } | |
179 | ||
180 | ||
181 | // | |
182 | // Load root (anchor) certificates from disk | |
183 | // | |
184 | void TrustStore::loadRootCertificates() | |
185 | { | |
186 | using namespace CssmClient; | |
187 | using namespace KeychainCore::Schema; | |
188 | ||
189 | // release previous cached data (if any) | |
190 | refreshRootCertificates(); | |
191 | ||
192 | static const char anchorLibrary[] = "/System/Library/Keychains/X509Anchors"; | |
193 | ||
194 | // open anchor database and formulate query (x509v3 certs) | |
df0e469f | 195 | secdebug("anchors", "Loading anchors from %s", anchorLibrary); |
29654253 A |
196 | DL dl(gGuidAppleFileDL); |
197 | Db db(dl, anchorLibrary); | |
198 | DbCursor search(db); | |
199 | search->recordType(CSSM_DL_DB_RECORD_X509_CERTIFICATE); | |
200 | search->conjunctive(CSSM_DB_OR); | |
201 | #if 0 // if we ever need to support v1/v2 certificates... | |
202 | search->add(CSSM_DB_EQUAL, kX509CertificateCertType, UInt32(CSSM_CERT_X_509v1)); | |
203 | search->add(CSSM_DB_EQUAL, kX509CertificateCertType, UInt32(CSSM_CERT_X_509v2)); | |
204 | search->add(CSSM_DB_EQUAL, kX509CertificateCertType, UInt32(CSSM_CERT_X_509v3)); | |
205 | #endif | |
206 | ||
207 | // collect certificate data | |
208 | typedef list<CssmDataContainer> ContainerList; | |
209 | ContainerList::iterator last; | |
210 | ContainerList certs; | |
211 | for (;;) { | |
212 | DbUniqueRecord id; | |
df0e469f | 213 | last = certs.insert(certs.end(), CssmDataContainer()); |
29654253 A |
214 | if (!search->next(NULL, &*last, id)) |
215 | break; | |
216 | } | |
217 | ||
218 | // how many data bytes do we need? | |
219 | size_t size = 0; | |
220 | for (ContainerList::const_iterator it = certs.begin(); it != last; it++) | |
221 | size += it->length(); | |
222 | mRootBytes.length(size); | |
223 | ||
224 | // fill CssmData vector while copying data bytes together | |
225 | mRoots.clear(); | |
226 | uint8 *base = mRootBytes.data<uint8>(); | |
227 | for (ContainerList::const_iterator it = certs.begin(); it != last; it++) { | |
228 | memcpy(base, it->data(), it->length()); | |
229 | mRoots.push_back(CssmData(base, it->length())); | |
230 | base += it->length(); | |
231 | } | |
df0e469f | 232 | secdebug("anchors", "%ld anchors loaded", mRoots.size()); |
29654253 A |
233 | |
234 | mRootsValid = true; // ready to roll | |
235 | } | |
236 | ||
237 | ||
238 | } // end namespace KeychainCore | |
239 | } // end namespace Security |