]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SOSCircle/SecureObjectSync/SOSChangeTracker.c
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSChangeTracker.c
1 /*
2 * Copyright (c) 2015 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SOSChangeTracker.c - Implementation of a manifest caching change tracker that forwards changes to children
26 */
27
28 #include <Security/SecureObjectSync/SOSChangeTracker.h>
29 #include <Security/SecureObjectSync/SOSDigestVector.h>
30 #include <Security/SecureObjectSync/SOSEngine.h>
31 #include <Security/SecureObjectSync/SOSManifest.h>
32 #include <Security/SecureObjectSync/SOSInternal.h>
33 #include <utilities/SecCFError.h>
34 #include <utilities/SecCFWrappers.h>
35
36 CFStringRef SOSChangeCopyDescription(SOSChangeRef change) {
37 CFTypeRef object = NULL;
38 bool isAdd = SOSChangeGetObject(change, &object);
39 // TODO: Print objects or digests
40 return (isData(object)
41 ? isAdd ? CFSTR("a") : CFSTR("d")
42 : isAdd ? CFSTR("A") : CFSTR("D"));
43 }
44
45 CFDataRef SOSChangeCopyDigest(SOSDataSourceRef dataSource, SOSChangeRef change, bool *isDel, SOSObjectRef *object, CFErrorRef *error) {
46 CFDataRef digest = NULL;
47 if (isArray(change)) {
48 if (CFArrayGetCount(change) != 1) {
49 SecError(errSecDecode, error, CFSTR("change array count: %ld"), CFArrayGetCount(change));
50 return NULL;
51 }
52 change = CFArrayGetValueAtIndex(change, 0);
53 *isDel = true;
54 } else {
55 *isDel = false;
56 }
57
58 // If the change is a CFData, this is the signal that it is a delete
59 if (isData(change)) {
60 digest = (CFDataRef)CFRetain(change);
61 } else {
62 digest = SOSObjectCopyDigest(dataSource, (SOSObjectRef)change, error);
63 *object = (SOSObjectRef)change;
64 }
65 assert(digest && CFDataGetLength(digest) == CCSHA1_OUTPUT_SIZE);
66 return digest;
67 }
68
69 CFStringRef SOSChangesCopyDescription(CFArrayRef changes) {
70 CFMutableStringRef desc = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("("));
71 CFTypeRef change;
72 if (changes) CFArrayForEachC(changes, change) {
73 CFStringRef changeDesc = SOSChangeCopyDescription(change);
74 CFStringAppend(desc, changeDesc);
75 CFRetainSafe(changeDesc);
76 }
77 CFStringAppend(desc, CFSTR(")"));
78 return desc;
79 }
80
81
82 /* SOSChangeTracker implementation. */
83 struct __OpaqueSOSChangeTracker {
84 CFRuntimeBase _base;
85 SOSManifestRef manifest; // Optional: Only concrete cts have a manifest
86 CFMutableArrayRef changeChildren; // Optional: cts can have children
87 CFMutableArrayRef manifestChildren; // Optional: cts can have children
88 };
89
90 static CFStringRef SOSChangeTrackerCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
91 SOSChangeTrackerRef ct = (SOSChangeTrackerRef)cf;
92 CFStringRef desc = CFStringCreateWithFormat(kCFAllocatorDefault, formatOptions, CFSTR("<ChangeTracker %@ children %ld/%ld>"),
93 ct->manifest ? ct->manifest : (SOSManifestRef)CFSTR("NonConcrete"),
94 CFArrayGetCount(ct->changeChildren), CFArrayGetCount(ct->manifestChildren));
95 return desc;
96 }
97
98 static void SOSChangeTrackerDestroy(CFTypeRef cf) {
99 SOSChangeTrackerRef ct = (SOSChangeTrackerRef)cf;
100 CFReleaseSafe(ct->manifest);
101 CFReleaseSafe(ct->changeChildren);
102 CFReleaseSafe(ct->manifestChildren);
103 }
104
105 // Even though SOSChangeTracker instances are used as keys in dictionaries, they are treated as pointers when used as such
106 // which is fine since the engine ensures instances are singletons.
107 CFGiblisFor(SOSChangeTracker);
108
109 SOSChangeTrackerRef SOSChangeTrackerCreate(CFAllocatorRef allocator, bool isConcrete, CFArrayRef changeChildren, CFErrorRef *error) {
110 SOSChangeTrackerRef ct = NULL;
111 ct = CFTypeAllocate(SOSChangeTracker, struct __OpaqueSOSChangeTracker, allocator);
112 if (ct && isConcrete) {
113 ct->manifest = SOSManifestCreateWithData(NULL, error);
114 if (!ct->manifest)
115 CFReleaseNull(ct);
116 }
117 if (ct) {
118 if (changeChildren)
119 ct->changeChildren = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, changeChildren);
120 else
121 ct->changeChildren = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
122 ct->manifestChildren = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
123 }
124
125 return ct;
126 }
127
128 // Change the concreteness of the current ct (a non concrete ct does not support SOSChangeTrackerCopyManifest().
129 void SOSChangeTrackerSetConcrete(SOSChangeTrackerRef ct, bool isConcrete) {
130 if (!isConcrete)
131 CFReleaseNull(ct->manifest);
132 else if (!ct->manifest) {
133 ct->manifest = SOSManifestCreateWithData(NULL, NULL);
134 }
135 }
136
137 // Add a child to the current ct
138 void SOSChangeTrackerRegisterChangeUpdate(SOSChangeTrackerRef ct, SOSChangeTrackerUpdatesChanges child) {
139 CFArrayAppendValue(ct->changeChildren, child);
140 }
141
142 void SOSChangeTrackerRegisterManifestUpdate(SOSChangeTrackerRef ct, SOSChangeTrackerUpdatesManifests child) {
143 CFArrayAppendValue(ct->manifestChildren, child);
144 }
145
146 void SOSChangeTrackerResetRegistration(SOSChangeTrackerRef ct) {
147 CFArrayRemoveAllValues(ct->changeChildren);
148 CFArrayRemoveAllValues(ct->manifestChildren);
149 }
150
151 void SOSChangeTrackerSetManifest(SOSChangeTrackerRef ct, SOSManifestRef manifest) {
152 CFRetainAssign(ct->manifest, manifest);
153 }
154
155 SOSManifestRef SOSChangeTrackerCopyManifest(SOSChangeTrackerRef ct, CFErrorRef *error) {
156 if (ct->manifest) {
157 return (SOSManifestRef)CFRetain(ct->manifest);
158 }
159 SOSErrorCreate(kSOSErrorNotConcreteError, error, NULL, CFSTR("ChangeTracker is not concrete"));
160 return NULL;
161 }
162
163 static bool SOSChangeTrackerCreateManifestsWithChanges(SOSEngineRef engine, CFArrayRef changes, SOSManifestRef *removals, SOSManifestRef *additions, CFErrorRef *error) {
164 bool ok = true;
165 struct SOSDigestVector dvdels = SOSDigestVectorInit;
166 struct SOSDigestVector dvadds = SOSDigestVectorInit;
167 struct SOSDigestVector *dv;
168 CFTypeRef change;
169 CFArrayForEachC(changes, change) {
170 CFDataRef digest, allocatedDigest = NULL;
171 if (isArray(change)) {
172 assert(CFArrayGetCount(change) == 1);
173 change = CFArrayGetValueAtIndex(change, 0);
174 dv = &dvdels;
175 } else {
176 dv = &dvadds;
177 }
178
179 if (isData(change)) {
180 digest = (CFDataRef)change;
181 } else {
182 CFErrorRef digestError = NULL;
183 digest = allocatedDigest = SOSObjectCopyDigest(SOSEngineGetDataSource(engine), (SOSObjectRef)change, &digestError);
184 if (!digest) {
185 secerror("change %@ SOSObjectCopyDigest: %@", change, digestError);
186 CFReleaseNull(digestError);
187 continue;
188 }
189 }
190
191 if (CFDataGetLength(digest) == 20) {
192 SOSDigestVectorAppend(dv, CFDataGetBytePtr(digest));
193 } else {
194 secerror("change %@ bad length digest: %@", change, digest);
195 }
196 CFReleaseNull(allocatedDigest);
197 }
198 if (ok && removals)
199 ok = *removals = SOSManifestCreateWithDigestVector(&dvdels, error);
200 if (ok && additions)
201 ok = *additions = SOSManifestCreateWithDigestVector(&dvadds, error);
202
203 SOSDigestVectorFree(&dvadds);
204 SOSDigestVectorFree(&dvdels);
205
206 return ok;
207 }
208
209 bool SOSChangeTrackerTrackChanges(SOSChangeTrackerRef ct, SOSEngineRef engine, SOSTransactionRef txn, SOSDataSourceTransactionSource source, SOSDataSourceTransactionPhase phase, CFArrayRef changes, CFErrorRef *error) {
210 bool ok = true;
211 if (changes && CFArrayGetCount(changes)) {
212 CFStringRef changesDesc = SOSChangesCopyDescription(changes);
213 secnotice("tracker", "%@ %s %s changes: %@", ct, phase == kSOSDataSourceTransactionWillCommit ? "will-commit" : phase == kSOSDataSourceTransactionDidCommit ? "did-commit" : "did-rollback", source == kSOSDataSourceSOSTransaction ? "sos" : "api", changesDesc);
214 CFReleaseSafe(changesDesc);
215 if (ct->manifest || ct->manifestChildren) {
216 SOSManifestRef additions = NULL;
217 SOSManifestRef removals = NULL;
218 ok &= SOSChangeTrackerCreateManifestsWithChanges(engine, changes, &removals, &additions, error);
219 if (ok) {
220 if (ct->manifest) {
221 SOSManifestRef updatedManifest = SOSManifestCreateWithPatch(ct->manifest, removals, additions, error);
222 if (updatedManifest){
223 CFTransferRetained(ct->manifest, updatedManifest);
224 }
225 }
226 if (ct->manifestChildren) {
227 SOSChangeTrackerUpdatesManifests child;
228 CFArrayForEachC(ct->manifestChildren, child) {
229 ok = ok && child(ct, engine, txn, source, phase, removals, additions, error);
230 }
231 }
232 }
233 CFReleaseSafe(removals);
234 CFReleaseSafe(additions);
235 // TODO: Potentially filter changes to eliminate any changes that were already in our manifest
236 // Backup Peers and the like would probably enjoy this so they don't have to do it themselves.
237 }
238
239 if (ct->changeChildren) {
240 SOSChangeTrackerUpdatesChanges child;
241 CFArrayForEachC(ct->changeChildren, child) {
242 ok = ok && child(ct, engine, txn, source, phase, changes, error);
243 }
244 }
245 }
246
247 return ok;
248 }
249