]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2002-2004,2011-2014 Apple Inc. All Rights Reserved. |
427c49bc | 3 | * |
b1ab9ed8 | 4 | * @APPLE_LICENSE_HEADER_START@ |
d8f41ccd | 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. | |
d8f41ccd | 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. | |
d8f41ccd | 20 | * |
b1ab9ed8 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | // | |
25 | // TrustStore.h - Abstract interface to permanent user trust assignments | |
26 | // | |
27 | #include <security_keychain/TrustStore.h> | |
28 | #include <security_keychain/Globals.h> | |
29 | #include <security_keychain/Certificate.h> | |
30 | #include <security_keychain/KCCursor.h> | |
31 | #include <security_keychain/SecCFTypes.h> | |
32 | #include <security_cdsa_utilities/Schema.h> | |
33 | #include <security_keychain/SecTrustSettingsPriv.h> | |
34 | ||
35 | namespace Security { | |
36 | namespace KeychainCore { | |
37 | ||
38 | ||
39 | // | |
40 | // Make and break: trivial | |
41 | // | |
42 | TrustStore::TrustStore(Allocator &alloc) | |
43 | : allocator(alloc), mRootsValid(false), mRootBytes(allocator), mMutex(Mutex::recursive) | |
44 | { | |
45 | } | |
46 | ||
47 | TrustStore::~TrustStore() | |
48 | { } | |
49 | ||
50 | // | |
51 | // Retrieve the trust setting for a (certificate, policy) pair. | |
52 | // | |
53 | SecTrustUserSetting TrustStore::find(Certificate *cert, Policy *policy, | |
54 | StorageManager::KeychainList &keychainList) | |
55 | { | |
56 | StLock<Mutex> _(mMutex); | |
427c49bc | 57 | |
b1ab9ed8 A |
58 | if (Item item = findItem(cert, policy, keychainList)) { |
59 | // Make sure that the certificate is available in some keychain, | |
60 | // to provide a basis for editing the trust setting that we're returning. | |
61 | if (cert->keychain() == NULL) { | |
62 | if (cert->findInKeychain(keychainList) == NULL) { | |
63 | Keychain defaultKeychain = Keychain::optional(NULL); | |
64 | if (Keychain location = item->keychain()) { | |
65 | try { | |
66 | cert->copyTo(location); // add cert to the trust item's keychain | |
67 | } catch (...) { | |
68 | secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"", | |
69 | cert, location->name()); | |
70 | try { | |
71 | if (&*location != &*defaultKeychain) | |
72 | cert->copyTo(defaultKeychain); // try the default (if it's not the same) | |
73 | } catch (...) { | |
74 | // unable to add the certificate | |
75 | secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"", | |
76 | cert, defaultKeychain->name()); | |
77 | } | |
78 | } | |
79 | } | |
80 | } | |
81 | } | |
82 | CssmDataContainer data; | |
83 | item->getData(data); | |
84 | if (data.length() != sizeof(TrustData)) | |
85 | MacOSError::throwMe(errSecInvalidTrustSetting); | |
86 | TrustData &trust = *data.interpretedAs<TrustData>(); | |
87 | if (trust.version != UserTrustItem::currentVersion) | |
88 | MacOSError::throwMe(errSecInvalidTrustSetting); | |
89 | return trust.trust; | |
90 | } else { | |
91 | return kSecTrustResultUnspecified; | |
92 | } | |
93 | } | |
94 | ||
95 | ||
96 | // | |
97 | // Set an individual trust element | |
98 | // | |
99 | void TrustStore::assign(Certificate *cert, Policy *policy, SecTrustUserSetting trust) | |
100 | { | |
101 | StLock<Mutex> _(mMutex); | |
427c49bc | 102 | |
b1ab9ed8 A |
103 | TrustData trustData = { UserTrustItem::currentVersion, trust }; |
104 | Keychain defaultKeychain = Keychain::optional(NULL); | |
105 | Keychain trustLocation = defaultKeychain; // default keychain, unless trust entry found | |
106 | StorageManager::KeychainList searchList; | |
107 | globals().storageManager.getSearchList(searchList); | |
108 | ||
109 | if (Item item = findItem(cert, policy, searchList)) { | |
110 | // user has a trust setting in a keychain - modify that | |
111 | trustLocation = item->keychain(); | |
112 | if (trust == kSecTrustResultUnspecified) | |
113 | item->keychain()->deleteItem(item); | |
114 | else | |
115 | item->modifyContent(NULL, sizeof(trustData), &trustData); | |
116 | } else { | |
117 | // no trust entry: make one | |
118 | if (trust != kSecTrustResultUnspecified) { | |
119 | Item item = new UserTrustItem(cert, policy, trustData); | |
120 | if (Keychain location = cert->keychain()) { | |
121 | try { | |
122 | location->add(item); // try the cert's keychain first | |
123 | trustLocation = location; | |
124 | } catch (...) { | |
125 | if (&*location != &*defaultKeychain) | |
126 | defaultKeychain->add(item); // try the default (if it's not the same) | |
127 | } | |
128 | } else { | |
129 | defaultKeychain->add(item); // raw cert - use default keychain | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | // Make sure that the certificate is available in some keychain, | |
135 | // to provide a basis for editing the trust setting that we're assigning. | |
136 | if (cert->keychain() == NULL) { | |
137 | if (cert->findInKeychain(searchList) == NULL) { | |
138 | try { | |
139 | cert->copyTo(trustLocation); // add cert to the trust item's keychain | |
140 | } catch (...) { | |
141 | secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"", | |
142 | cert, trustLocation->name()); | |
143 | try { | |
144 | if (&*trustLocation != &*defaultKeychain) | |
145 | cert->copyTo(defaultKeychain); // try the default (if it's not the same) | |
146 | } catch (...) { | |
147 | // unable to add the certificate | |
148 | secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"", | |
149 | cert, defaultKeychain->name()); | |
150 | } | |
151 | } | |
152 | } | |
153 | } | |
154 | } | |
155 | ||
156 | ||
157 | // | |
158 | // Search the user's configured keychains for a trust setting. | |
159 | // If found, return it (as a TrustItem). Otherwise, return NULL. | |
160 | // Note that this function throws if a "real" error is encountered. | |
161 | // | |
162 | Item TrustStore::findItem(Certificate *cert, Policy *policy, | |
163 | StorageManager::KeychainList &keychainList) | |
164 | { | |
427c49bc A |
165 | // As of OS X 10.5, user trust records are no longer stored in keychains. |
166 | // SecTrustSetUserTrust was replaced with SecTrustSettingsSetTrustSettings, | |
167 | // which stores per-user trust in a separate root-owned file. This method, | |
168 | // however, would continue to find old trust records created prior to 10.5. | |
169 | // Since those are increasingly unlikely to exist (and cannot be edited), | |
170 | // we no longer need or want to look for them anymore. | |
171 | return ((ItemImpl*)NULL); | |
172 | ||
b1ab9ed8 | 173 | StLock<Mutex> _(mMutex); |
427c49bc | 174 | |
b1ab9ed8 A |
175 | try { |
176 | SecKeychainAttribute attrs[2]; | |
177 | CssmAutoData certIndex(Allocator::standard()); | |
178 | UserTrustItem::makeCertIndex(cert, certIndex); | |
179 | attrs[0].tag = kSecTrustCertAttr; | |
427c49bc | 180 | attrs[0].length = (UInt32)certIndex.length(); |
b1ab9ed8 A |
181 | attrs[0].data = certIndex.data(); |
182 | const CssmOid &policyOid = policy->oid(); | |
183 | attrs[1].tag = kSecTrustPolicyAttr; | |
427c49bc | 184 | attrs[1].length = (UInt32)policyOid.length(); |
b1ab9ed8 A |
185 | attrs[1].data = policyOid.data(); |
186 | SecKeychainAttributeList attrList = { 2, attrs }; | |
187 | KCCursor cursor(keychainList, CSSM_DL_DB_RECORD_USER_TRUST, &attrList); | |
188 | Item item; | |
189 | if (cursor->next(item)) | |
190 | return item; | |
b1ab9ed8 | 191 | } |
427c49bc A |
192 | catch (const CommonError &error) {} |
193 | ||
194 | return ((ItemImpl*)NULL); // no trust schema, no records, no error | |
b1ab9ed8 A |
195 | } |
196 | ||
197 | void TrustStore::getCssmRootCertificates(CertGroup &rootCerts) | |
198 | { | |
199 | StLock<Mutex> _(mMutex); | |
427c49bc | 200 | |
b1ab9ed8 A |
201 | if (!mRootsValid) |
202 | loadRootCertificates(); | |
203 | rootCerts = CertGroup(CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_BER, CSSM_CERTGROUP_DATA); | |
204 | rootCerts.blobCerts() = &mRoots[0]; | |
427c49bc | 205 | rootCerts.count() = (uint32)mRoots.size(); |
b1ab9ed8 A |
206 | } |
207 | ||
208 | // | |
209 | // Load root (anchor) certificates from disk | |
210 | // | |
211 | void TrustStore::loadRootCertificates() | |
212 | { | |
213 | StLock<Mutex> _(mMutex); | |
427c49bc | 214 | |
b1ab9ed8 A |
215 | CFRef<CFArrayRef> anchors; |
216 | OSStatus ortn; | |
217 | ||
427c49bc | 218 | /* |
b1ab9ed8 A |
219 | * Get the current set of all positively trusted anchors. |
220 | */ | |
221 | ortn = SecTrustSettingsCopyUnrestrictedRoots( | |
222 | true, true, true, /* all domains */ | |
223 | anchors.take()); | |
224 | if(ortn) { | |
225 | MacOSError::throwMe(ortn); | |
226 | } | |
227 | ||
228 | // how many data bytes do we need? | |
229 | size_t size = 0; | |
230 | CFIndex numCerts = CFArrayGetCount(anchors); | |
231 | CSSM_RETURN crtn; | |
232 | for(CFIndex dex=0; dex<numCerts; dex++) { | |
233 | SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, dex); | |
234 | CSSM_DATA certData; | |
235 | crtn = SecCertificateGetData(certRef, &certData); | |
236 | if(crtn) { | |
237 | CssmError::throwMe(crtn); | |
427c49bc | 238 | } |
b1ab9ed8 A |
239 | size += certData.Length; |
240 | } | |
241 | mRootBytes.length(size); | |
242 | ||
243 | // fill CssmData vector while copying data bytes together | |
244 | mRoots.clear(); | |
245 | uint8 *base = mRootBytes.data<uint8>(); | |
246 | for(CFIndex dex=0; dex<numCerts; dex++) { | |
247 | SecCertificateRef certRef = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, dex); | |
248 | CSSM_DATA certData; | |
249 | SecCertificateGetData(certRef, &certData); | |
250 | memcpy(base, certData.Data, certData.Length); | |
251 | mRoots.push_back(CssmData(base, certData.Length)); | |
252 | base += certData.Length; | |
253 | } | |
254 | ||
255 | secdebug("anchors", "%ld anchors loaded", (long)numCerts); | |
256 | ||
257 | mRootsValid = true; // ready to roll | |
258 | } | |
259 | ||
260 | } // end namespace KeychainCore | |
261 | } // end namespace Security |