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