]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecItemDataSource.c
Security-57336.10.29.tar.gz
[apple/security.git] / OSX / sec / securityd / SecItemDataSource.c
1 /*
2 * Copyright (c) 2006-2014 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 * SecItemDataSource.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
27 passwords.)
28 */
29
30 #include <securityd/SecItemDataSource.h>
31
32 #include <securityd/SecItemDb.h>
33 #include <securityd/SecItemSchema.h>
34 #include <securityd/SOSCloudCircleServer.h>
35 #include <Security/SecureObjectSync/SOSDigestVector.h>
36 #include <Security/SecureObjectSync/SOSViews.h>
37 #include <Security/SecBasePriv.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecItemBackup.h>
40 #include <Security/SecItemPriv.h>
41 #include <utilities/array_size.h>
42
43 /*
44 *
45 *
46 * SecItemDataSource
47 *
48 *
49 */
50 typedef struct SecItemDataSource *SecItemDataSourceRef;
51
52 struct SecItemDataSource {
53 struct SOSDataSource ds;
54 SecDbRef db; // The database we operate on
55 CFStringRef name; // The name of the slice of the database we represent.
56 };
57
58 static const SecDbClass *dsSyncedClassesV0[] = {
59 &genp_class,
60 &inet_class,
61 &keys_class,
62 };
63
64 static const SecDbClass *dsSyncedClasses[] = {
65 &genp_class, // genp must be first!
66 &inet_class,
67 &keys_class,
68 &cert_class,
69 };
70
71 static bool SecDbItemSelectSHA1(SecDbQueryRef query, SecDbConnectionRef dbconn, CFErrorRef *error,
72 bool (^use_attr_in_where)(const SecDbAttr *attr),
73 bool (^add_where_sql)(CFMutableStringRef sql, bool *needWhere),
74 bool (^bind_added_where)(sqlite3_stmt *stmt, int col),
75 void (^row)(sqlite3_stmt *stmt, bool *stop)) {
76 __block bool ok = true;
77 bool (^return_attr)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) {
78 return attr->kind == kSecDbSHA1Attr;
79 };
80 CFStringRef sql = SecDbItemCopySelectSQL(query, return_attr, use_attr_in_where, add_where_sql);
81 if (sql) {
82 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) {
83 ok = (SecDbItemSelectBind(query, stmt, error, use_attr_in_where, bind_added_where) &&
84 SecDbStep(dbconn, stmt, error, ^(bool *stop){ row(stmt, stop); }));
85 });
86 CFRelease(sql);
87 } else {
88 ok = false;
89 }
90 return ok;
91 }
92
93 static SOSManifestRef SecItemDataSourceCopyManifestWithQueries(SecItemDataSourceRef ds, CFArrayRef queries, CFErrorRef *error) {
94 __block SOSManifestRef manifest = NULL;
95 __block CFErrorRef localError = NULL;
96 if (!SecDbPerformRead(ds->db, error, ^(SecDbConnectionRef dbconn) {
97 __block struct SOSDigestVector dv = SOSDigestVectorInit;
98 Query *q;
99 bool ok = true;
100 CFArrayForEachC(queries, q) {
101 if (!(ok &= SecDbItemSelectSHA1(q, dbconn, &localError, ^bool(const SecDbAttr *attr) {
102 return CFDictionaryContainsKey(q->q_item, attr->name);
103 }, NULL, NULL, ^(sqlite3_stmt *stmt, bool *stop) {
104 const uint8_t *digest = sqlite3_column_blob(stmt, 0);
105 size_t digestLen = sqlite3_column_bytes(stmt, 0);
106 if (digestLen != SOSDigestSize) {
107 secerror("digest %zu bytes", digestLen);
108 } else {
109 SOSDigestVectorAppend(&dv, digest);
110 }
111 }))) {
112 secerror("SecDbItemSelectSHA1 failed: %@", localError);
113 break;
114 }
115 }
116 if (ok) {
117 // TODO: This code assumes that the passed in queries do not overlap, otherwise we'd need something to eliminate dupes:
118 //SOSDigestVectorUniqueSorted(&dv);
119 manifest = SOSManifestCreateWithDigestVector(&dv, &localError);
120 }
121 SOSDigestVectorFree(&dv);
122 })) {
123 CFReleaseSafe(manifest);
124 }
125 CFErrorPropagate(localError, error);
126 return manifest;
127 }
128
129 static Query *SecItemDataSourceAppendQuery(CFMutableArrayRef queries, const SecDbClass *qclass, bool noTombstones, CFErrorRef *error) {
130 Query *q = query_create(qclass, NULL, error);
131 if (q) {
132 q->q_return_type = kSecReturnDataMask | kSecReturnAttributesMask;
133 q->q_limit = kSecMatchUnlimited;
134 q->q_keybag = KEYBAG_DEVICE;
135 query_add_attribute(kSecAttrSynchronizable, kCFBooleanTrue, q);
136 query_add_or_attribute(kSecAttrAccessible, kSecAttrAccessibleWhenUnlocked, q);
137 query_add_or_attribute(kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock, q);
138 query_add_or_attribute(kSecAttrAccessible, kSecAttrAccessibleAlways, q);
139 query_add_or_attribute(kSecAttrAccessible, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, q);
140 query_add_or_attribute(kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, q);
141 query_add_or_attribute(kSecAttrAccessible, kSecAttrAccessibleAlwaysThisDeviceOnly, q);
142
143 if (noTombstones) {
144 query_add_attribute(kSecAttrTombstone, kCFBooleanFalse, q);
145 }
146
147 CFArrayAppendValue(queries, q);
148 }
149 return q;
150 }
151
152 static Query *SecItemDataSourceAppendQueryWithClassAndViewHint(CFMutableArrayRef queries, const SecDbClass *qclass, bool noTombstones, bool allowTkid, CFStringRef viewHint, CFErrorRef *error) {
153 Query *q = SecItemDataSourceAppendQuery(queries, qclass, noTombstones, error);
154 if (q) {
155 // For each attribute in current schema but not in v6, look for the
156 // default value of those attributes in the query, since old items
157 // will all have that as their values for these new attributes.
158 SecDbForEachAttr(qclass, attr) {
159 if ((attr->flags & (kSecDbPrimaryKeyFlag | kSecDbDefaultEmptyFlag | kSecDbDefault0Flag | kSecDbNotNullFlag)) == kSecDbPrimaryKeyFlag) {
160 // attr is a primary key attribute added in schema version 7 or later
161 if (!allowTkid || attr != &v7tkid) {
162 if (attr == &v7vwht && viewHint) {
163 query_add_attribute_with_desc(attr, viewHint, q);
164 } else {
165 CFTypeRef value = SecDbAttrCopyDefaultValue(attr, &q->q_error);
166 if (value) {
167 query_add_attribute_with_desc(attr, value, q);
168 CFRelease(value);
169 }
170 }
171 }
172 }
173 }
174 }
175 return q;
176 }
177
178 static Query *SecItemDataSourceAppendQueryWithClass(CFMutableArrayRef queries, const SecDbClass *qclass, bool noTombstones, bool allowTkid, CFErrorRef *error) {
179 return SecItemDataSourceAppendQueryWithClassAndViewHint(queries, qclass, noTombstones, allowTkid, NULL, error);
180 }
181
182 static Query *SecItemDataSourceAppendQueryWithClassAndAgrp(CFMutableArrayRef queries, const SecDbClass *qclass, bool noTombstones, bool allowTkid, CFStringRef agrp, CFErrorRef *error) {
183 Query *q = SecItemDataSourceAppendQueryWithClass(queries, qclass, noTombstones, allowTkid, error);
184 if (q && agrp) {
185 query_add_attribute(kSecAttrAccessGroup, agrp, q);
186 }
187 return q;
188 }
189
190 static bool SecItemDataSourceAppendQueriesForViewName(SecItemDataSourceRef ds, CFMutableArrayRef queries, CFStringRef compositeViewName, CFErrorRef *error) {
191 bool ok = true;
192 CFStringRef viewName;
193 bool noTombstones = CFStringHasSuffix(compositeViewName, CFSTR("-tomb"));
194 if (noTombstones) {
195 viewName = CFStringCreateWithSubstring(kCFAllocatorDefault, compositeViewName, CFRangeMake(0, CFStringGetLength(compositeViewName) - 5));
196 } else {
197 viewName = CFRetain(compositeViewName);
198 }
199
200 const bool noTKID = false;
201 const bool allowTKID = true;
202 if (CFEqual(viewName, kSOSViewKeychainV0)) {
203 for (size_t class_ix = 0; class_ix < array_size(dsSyncedClassesV0); ++class_ix) {
204 SecItemDataSourceAppendQueryWithClass(queries, dsSyncedClassesV0[class_ix], noTombstones, noTKID, error);
205 }
206 } else if (CFEqual(viewName, kSOSViewWiFi)) {
207 Query *q = SecItemDataSourceAppendQueryWithClassAndAgrp(queries, &genp_class, noTombstones, allowTKID, CFSTR("apple"), error);
208 if (q) {
209 query_add_attribute(kSecAttrService, CFSTR("AirPort"), q);
210 }
211 } else if (CFEqual(viewName, kSOSViewAutofillPasswords)) {
212 SecItemDataSourceAppendQueryWithClassAndAgrp(queries, &inet_class, noTombstones, allowTKID, CFSTR("com.apple.cfnetwork"), error);
213 } else if (CFEqual(viewName, kSOSViewSafariCreditCards)) {
214 SecItemDataSourceAppendQueryWithClassAndAgrp(queries, &genp_class, noTombstones, allowTKID, CFSTR("com.apple.safari.credit-cards"), error);
215 } else if (CFEqual(viewName, kSOSViewiCloudIdentity)) {
216 SecItemDataSourceAppendQueryWithClassAndAgrp(queries, &keys_class, noTombstones, allowTKID, CFSTR("com.apple.security.sos"), error);
217 } else if (CFEqual(viewName, kSOSViewBackupBagV0)) {
218 SecItemDataSourceAppendQueryWithClassAndAgrp(queries, &genp_class, noTombstones, allowTKID, CFSTR("com.apple.sbd"), error);
219 } else if (CFEqual(viewName, kSOSViewOtherSyncable)) {
220 SecItemDataSourceAppendQueryWithClass(queries, &cert_class, noTombstones, allowTKID, error);
221
222 Query *q1_genp = SecItemDataSourceAppendQueryWithClassAndAgrp(queries, &genp_class, noTombstones, allowTKID, CFSTR("apple"), error);
223 query_add_not_attribute(kSecAttrService, CFSTR("AirPort"), q1_genp);
224
225 Query *q2_genp = SecItemDataSourceAppendQueryWithClass(queries, &genp_class, noTombstones, allowTKID, error);
226 query_add_not_attribute(kSecAttrAccessGroup, CFSTR("apple"), q2_genp);
227 query_add_not_attribute(kSecAttrAccessGroup, CFSTR("com.apple.safari.credit-cards"), q2_genp);
228 query_add_not_attribute(kSecAttrAccessGroup, CFSTR("com.apple.sbd"), q2_genp);
229
230 Query *q_inet = SecItemDataSourceAppendQueryWithClass(queries, &inet_class, noTombstones, allowTKID, error);
231 query_add_not_attribute(kSecAttrAccessGroup, CFSTR("com.apple.cfnetwork"), q_inet);
232
233 Query *q_keys = SecItemDataSourceAppendQueryWithClass(queries, &keys_class, noTombstones, allowTKID, error);
234 query_add_not_attribute(kSecAttrAccessGroup, CFSTR("com.apple.security.sos"), q_keys);
235 } else {
236 // All other viewNames should match on the ViewHint attribute.
237 for (size_t class_ix = 0; class_ix < array_size(dsSyncedClasses); ++class_ix) {
238 SecItemDataSourceAppendQueryWithClassAndViewHint(queries, dsSyncedClasses[class_ix], noTombstones, allowTKID, viewName, error);
239 }
240 }
241
242 CFReleaseSafe(viewName);
243 return ok;
244 }
245
246 static SOSManifestRef SecItemDataSourceCopyManifestWithViewNameSet(SecItemDataSourceRef ds, CFSetRef viewNames, CFErrorRef *error) {
247 CFMutableArrayRef queries = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
248 SOSManifestRef manifest = NULL;
249 __block bool ok = true;
250 CFSetForEach(viewNames, ^(const void *value) {
251 CFStringRef viewName = (CFStringRef)value;
252 ok &= SecItemDataSourceAppendQueriesForViewName(ds, queries, viewName, error);
253 });
254 if (ok)
255 manifest = SecItemDataSourceCopyManifestWithQueries(ds, queries, error);
256 Query *q;
257 CFArrayForEachC(queries, q) {
258 CFErrorRef localError = NULL;
259 if (!query_destroy(q, &localError)) {
260 secerror("query_destroy failed: %@", localError);
261 CFErrorPropagate(localError, error);
262 CFReleaseNull(manifest);
263 }
264 }
265 CFReleaseSafe(queries);
266 return manifest;
267 }
268
269 // Return the newest object (conflict resolver)
270 static SecDbItemRef SecItemDataSourceCopyMergedItem(SecDbItemRef item1, SecDbItemRef item2, CFErrorRef *error) {
271 CFErrorRef localError = NULL;
272 SecDbItemRef result = NULL;
273 CFDateRef m1, m2;
274 const SecDbAttr *desc = SecDbAttrWithKey(SecDbItemGetClass(item1), kSecAttrModificationDate, error);
275 m1 = SecDbItemGetValue(item1, desc, &localError);
276 m2 = SecDbItemGetValue(item2, desc, &localError);
277 if (m1 && m2) switch (CFDateCompare(m1, m2, NULL)) {
278 case kCFCompareGreaterThan:
279 result = item1;
280 break;
281 case kCFCompareLessThan:
282 result = item2;
283 break;
284 case kCFCompareEqualTo:
285 {
286 // Return the item with the smallest digest.
287 CFDataRef digest1 = SecDbItemGetSHA1(item1, &localError);
288 CFDataRef digest2 = SecDbItemGetSHA1(item2, &localError);
289 if (digest1 && digest2) switch (CFDataCompare(digest1, digest2)) {
290 case kCFCompareGreaterThan:
291 case kCFCompareEqualTo:
292 result = item2;
293 break;
294 case kCFCompareLessThan:
295 result = item1;
296 break;
297 } else if (SecErrorGetOSStatus(localError) == errSecDecode) {
298 if (digest1) result = item1;
299 if (digest2) result = item2;
300 }
301 break;
302 }
303 } else if (SecErrorGetOSStatus(localError) == errSecDecode) {
304 // If one of the two objects has an unparsable date,
305 // the object with the parsable date wins.
306 if (m1) result = item1;
307 if (m2) result = item2;
308 }
309
310 if (localError) {
311 if (!result && error && !*error)
312 *error = localError;
313 else
314 CFRelease(localError);
315 }
316 return CFRetainSafe(result);
317 }
318
319 //
320 // MARK: DataSource protocol implementation
321 //
322
323 static CFStringRef dsGetName(SOSDataSourceRef data_source) {
324 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
325 return ds->name;
326 }
327
328 static void dsSetNotifyPhaseBlock(SOSDataSourceRef data_source, dispatch_queue_t queue, SOSDataSourceNotifyBlock notifyBlock) {
329 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
330 SecDbSetNotifyPhaseBlock(ds->db, queue, notifyBlock
331 ? ^(SecDbConnectionRef dbconn, SecDbTransactionPhase phase, SecDbTransactionSource source, CFArrayRef changes) {
332 notifyBlock(&ds->ds, (SOSTransactionRef)dbconn, phase, source, changes);
333 }
334 : NULL);
335 }
336
337 static SOSManifestRef dsCopyManifestWithViewNameSet(SOSDataSourceRef data_source, CFSetRef viewNameSet, CFErrorRef *error) {
338 struct SecItemDataSource *ds = (struct SecItemDataSource *)data_source;
339 return SecItemDataSourceCopyManifestWithViewNameSet(ds, viewNameSet, error);
340 }
341
342 static bool dsForEachObject(SOSDataSourceRef data_source, SOSManifestRef manifest, CFErrorRef *error, void (^handle_object)(CFDataRef key, SOSObjectRef object, bool *stop)) {
343 struct SecItemDataSource *ds = (struct SecItemDataSource *)data_source;
344 __block bool result = true;
345 const SecDbAttr *sha1Attr = SecDbClassAttrWithKind(&genp_class, kSecDbSHA1Attr, error);
346 if (!sha1Attr) return false;
347 bool (^return_attr)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) {
348 return attr->kind == kSecDbRowIdAttr || attr->kind == kSecDbEncryptedDataAttr;
349 };
350 bool (^use_attr_in_where)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) {
351 return attr->kind == kSecDbSHA1Attr;
352 };
353 Query *select_queries[array_size(dsSyncedClasses)] = {};
354 CFStringRef select_sql[array_size(dsSyncedClasses)] = {};
355 sqlite3_stmt *select_stmts[array_size(dsSyncedClasses)] = {};
356
357 __block Query **queries = select_queries;
358 __block CFStringRef *sqls = select_sql;
359 __block sqlite3_stmt **stmts = select_stmts;
360
361 result &= SecDbPerformRead(ds->db, error, ^(SecDbConnectionRef dbconn) {
362 // Setup
363 for (size_t class_ix = 0; class_ix < array_size(dsSyncedClasses); ++class_ix) {
364 result = (result
365 && (queries[class_ix] = query_create(dsSyncedClasses[class_ix], NULL, error))
366 && (sqls[class_ix] = SecDbItemCopySelectSQL(queries[class_ix], return_attr, use_attr_in_where, NULL))
367 && (stmts[class_ix] = SecDbCopyStmt(dbconn, sqls[class_ix], NULL, error)));
368 }
369
370 if (result) SOSManifestForEach(manifest, ^(CFDataRef key, bool *stop) {
371 __block SecDbItemRef item = NULL;
372 for (size_t class_ix = 0; result && !item && class_ix < array_size(dsSyncedClasses); ++class_ix) {
373 CFDictionarySetValue(queries[class_ix]->q_item, sha1Attr->name, key);
374 result = (SecDbItemSelectBind(queries[class_ix], stmts[class_ix], error, use_attr_in_where, NULL) && SecDbStep(dbconn, stmts[class_ix], error, ^(bool *unused_stop) {
375 item = SecDbItemCreateWithStatement(kCFAllocatorDefault, queries[class_ix]->q_class, stmts[class_ix], KEYBAG_DEVICE, error, return_attr);
376 })) && SecDbReset(stmts[class_ix], error);
377 }
378 handle_object(key, (SOSObjectRef)item, stop);
379 CFReleaseSafe(item);
380 });
381
382 // Cleanup
383 for (size_t class_ix = 0; class_ix < array_size(dsSyncedClasses); ++class_ix) {
384 result &= SecDbReleaseCachedStmt(dbconn, sqls[class_ix], stmts[class_ix], error);
385 CFReleaseSafe(sqls[class_ix]);
386 result &= query_destroy(queries[class_ix], error);
387 }
388 });
389 return result;
390 }
391
392 static bool dsRelease(SOSDataSourceRef data_source, CFErrorRef *error) {
393 // We never release our dataSource since it's tracking changes
394 // to the keychain for the engine and its peers.
395 return true;
396 }
397
398 static SOSObjectRef objectCreateWithPropertyList(CFDictionaryRef plist, CFErrorRef *error) {
399 SecDbItemRef item = NULL;
400 const SecDbClass *class = NULL;
401 CFTypeRef cname = CFDictionaryGetValue(plist, kSecClass);
402 if (cname) {
403 class = kc_class_with_name(cname);
404 if (class) {
405 item = SecDbItemCreateWithAttributes(kCFAllocatorDefault, class, plist, KEYBAG_DEVICE, error);
406 } else {
407 SecError(errSecNoSuchClass, error, CFSTR("can find class named: %@"), cname);
408 }
409 } else {
410 SecError(errSecItemClassMissing, error, CFSTR("query missing %@ attribute"), kSecClass);
411 }
412 return (SOSObjectRef)item;
413 }
414
415 static CFDataRef copyObjectDigest(SOSObjectRef object, CFErrorRef *error) {
416 SecDbItemRef item = (SecDbItemRef) object;
417 CFDataRef digest = SecDbItemGetSHA1(item, error);
418 CFRetainSafe(digest);
419 return digest;
420 }
421
422 static CFDataRef objectCopyPrimaryKey(SOSObjectRef object, CFErrorRef *error) {
423 SecDbItemRef item = (SecDbItemRef) object;
424 CFDataRef pk = SecDbItemGetPrimaryKey(item, error);
425 CFRetainSafe(pk);
426 return pk;
427 }
428
429 static CFDictionaryRef objectCopyPropertyList(SOSObjectRef object, CFErrorRef *error) {
430 SecDbItemRef item = (SecDbItemRef) object;
431 CFMutableDictionaryRef cryptoDataDict = SecDbItemCopyPListWithMask(item, kSecDbInCryptoDataFlag, error);
432 CFMutableDictionaryRef authDataDict = SecDbItemCopyPListWithMask(item, kSecDbInAuthenticatedDataFlag, error);
433
434 if (cryptoDataDict) {
435 if (authDataDict) {
436 CFDictionaryForEach(authDataDict, ^(const void *key, const void *value) {
437 CFDictionarySetValue(cryptoDataDict, key, value);
438 });
439 }
440 CFDictionaryAddValue(cryptoDataDict, kSecClass, SecDbItemGetClass(item)->name);
441 }
442
443 CFReleaseSafe(authDataDict);
444 return cryptoDataDict;
445 }
446
447 static bool dsWith(SOSDataSourceRef data_source, CFErrorRef *error, SOSDataSourceTransactionSource source, void(^transaction)(SOSTransactionRef txn, bool *commit)) {
448 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
449 __block bool ok = true;
450 ok &= SecDbPerformWrite(ds->db, error, ^(SecDbConnectionRef dbconn) {
451 ok &= SecDbTransaction(dbconn,
452 source == kSOSDataSourceAPITransaction ? kSecDbExclusiveTransactionType : kSecDbExclusiveRemoteTransactionType,
453 error, ^(bool *commit) {
454 transaction((SOSTransactionRef)dbconn, commit);
455 });
456 });
457 return ok;
458 }
459
460 static SOSMergeResult dsMergeObject(SOSTransactionRef txn, SOSObjectRef peersObject, SOSObjectRef *mergedObject, CFErrorRef *error) {
461 SecDbConnectionRef dbconn = (SecDbConnectionRef)txn;
462 SecDbItemRef peersItem = (SecDbItemRef)peersObject;
463 __block SOSMergeResult mr = kSOSMergeFailure;
464 __block SecDbItemRef mergedItem = NULL;
465 __block SecDbItemRef replacedItem = NULL;
466 if (!peersItem || !dbconn || !SecDbItemSetKeybag(peersItem, KEYBAG_DEVICE, error)) return mr;
467 if (SecDbItemInsertOrReplace(peersItem, dbconn, error, ^(SecDbItemRef myItem, SecDbItemRef *replace) {
468 // An item with the same primary key as dbItem already exists in the the database. That item is old_item.
469 // Let the conflict resolver choose which item to keep.
470 mergedItem = SecItemDataSourceCopyMergedItem(peersItem, myItem, error);
471 if (mergedObject) *mergedObject = (SOSObjectRef)CFRetain(mergedItem);
472 if (!mergedItem) return;
473 if (CFEqual(mergedItem, myItem)) {
474 // Conflict resolver choose my (local) item
475 secnotice("ds", "Conflict resolver choose my (local) item: %@", myItem);
476 mr = kSOSMergeLocalObject;
477 } else {
478 CFRetainAssign(replacedItem, myItem);
479 *replace = CFRetainSafe(mergedItem);
480 if (CFEqual(mergedItem, peersItem)) {
481 // Conflict resolver choose peers item
482 secnotice("ds", "Conflict resolver choose peers item: %@", peersItem);
483 mr = kSOSMergePeersObject;
484 } else {
485 // Conflict resolver created a new item; return it to our caller
486 secnotice("ds", "Conflict resolver created a new item; return it to our caller: %@", mergedItem);
487 mr = kSOSMergeCreatedObject;
488 }
489 }
490 })) {
491 if (mr == kSOSMergeFailure)
492 {
493 secnotice("ds", "kSOSMergeFailure => kSOSMergePeersObject");
494 mr = kSOSMergePeersObject;
495 }
496 }
497
498 if (error && *error && mr != kSOSMergeFailure)
499 CFReleaseNull(*error);
500
501 CFReleaseSafe(mergedItem);
502 CFReleaseSafe(replacedItem);
503 return mr;
504 }
505
506 /*
507 Truthy backup format is a dictionary from sha1 => item.
508 Each item has class, hash and item data.
509
510 TODO: sha1 is included as binary blob to avoid parsing key.
511 */
512 enum {
513 kSecBackupIndexHash = 0,
514 kSecBackupIndexClass,
515 kSecBackupIndexData,
516 };
517
518 static const void *kSecBackupKeys[] = {
519 [kSecBackupIndexHash] = kSecItemBackupHashKey,
520 [kSecBackupIndexClass] = kSecItemBackupClassKey,
521 [kSecBackupIndexData] = kSecItemBackupDataKey
522 };
523
524 static CFDictionaryRef objectCopyBackup(SOSObjectRef object, uint64_t handle, CFErrorRef *error) {
525 const void *values[array_size(kSecBackupKeys)];
526 SecDbItemRef item = (SecDbItemRef)object;
527 CFDictionaryRef backup_item = NULL;
528
529 if ((values[kSecBackupIndexHash] = SecDbItemGetSHA1(item, error))) {
530 if ((values[kSecBackupIndexData] = SecDbItemCopyEncryptedDataToBackup(item, handle, error))) {
531 values[kSecBackupIndexClass] = SecDbItemGetClass(item)->name;
532 backup_item = CFDictionaryCreate(kCFAllocatorDefault, kSecBackupKeys, values, array_size(kSecBackupKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
533 CFRelease(values[kSecBackupIndexData]);
534 }
535 }
536
537 return backup_item;
538 }
539
540 static CFDataRef dsCopyStateWithKey(SOSDataSourceRef data_source, CFStringRef key, CFStringRef pdmn, CFErrorRef *error) {
541 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
542 CFStringRef dataSourceID = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("SOSDataSource-%@"), ds->name);
543 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
544 kSecAttrAccessGroup, kSOSInternalAccessGroup,
545 kSecAttrAccount, key,
546 kSecAttrService, dataSourceID,
547 kSecAttrAccessible, pdmn,
548 kSecAttrSynchronizable, kCFBooleanFalse,
549 NULL);
550 CFReleaseSafe(dataSourceID);
551 __block CFDataRef data = NULL;
552 SecDbQueryRef query = query_create(&genp_class, dict, error);
553 if (query) {
554 if (query->q_item) CFReleaseSafe(query->q_item);
555 query->q_item = dict;
556 SecDbPerformRead(ds->db, error, ^(SecDbConnectionRef dbconn) {
557 SecDbItemSelect(query, dbconn, error, NULL, ^bool(const SecDbAttr *attr) {
558 return CFDictionaryContainsKey(dict, attr->name);
559 }, NULL, NULL, ^(SecDbItemRef item, bool *stop) {
560 secnotice("ds", "found item for key %@@%@", key, pdmn);
561 data = CFRetainSafe(SecDbItemGetValue(item, &v6v_Data, error));
562 });
563 });
564 query_destroy(query, error);
565 } else {
566 CFReleaseSafe(dict);
567 }
568 if (!data) secnotice("ds", "failed to load %@@%@ state: %@", key, pdmn, error ? *error : NULL);
569 return data;
570 }
571
572 static CFDataRef dsCopyItemDataWithKeys(SOSDataSourceRef data_source, CFDictionaryRef keys, CFErrorRef *error) {
573 /*
574 Values for V0 are:
575 kSecAttrAccessGroup ==> CFSTR("com.apple.sbd")
576 kSecAttrAccessible ==> kSecAttrAccessibleWhenUnlocked
577 kSecAttrAccount ==> CFSTR("SecureBackupPublicKeybag")
578 kSecAttrService ==> CFSTR("SecureBackupService")
579
580 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
581 kSecAttrAccessGroup, CFSTR("com.apple.sbd"),
582 kSecAttrAccount, account,
583 kSecAttrService, service,
584 kSecAttrAccessible, pdmn,
585 kSecAttrSynchronizable, kCFBooleanTrue,
586 NULL);
587 */
588
589 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
590 CFMutableDictionaryRef dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, keys);
591 __block CFDataRef data = NULL;
592 SecDbQueryRef query = query_create(&genp_class, dict, error);
593 if (query) {
594 if (query->q_item) CFReleaseSafe(query->q_item);
595 query->q_item = dict;
596 SecDbPerformRead(ds->db, error, ^(SecDbConnectionRef dbconn) {
597 SecDbItemSelect(query, dbconn, error, NULL, ^bool(const SecDbAttr *attr) {
598 return CFDictionaryContainsKey(dict, attr->name);
599 }, NULL, NULL, ^(SecDbItemRef item, bool *stop) {
600 secnotice("ds", "found item for keys %@", keys);
601 data = CFRetainSafe(SecDbItemGetValue(item, &v6v_Data, error));
602 });
603 });
604 query_destroy(query, error);
605 } else {
606 CFReleaseSafe(dict);
607 }
608 if (!data) secnotice("ds", "failed to load item %@: %@", keys, error ? *error : NULL);
609 return data;
610 }
611
612 static bool dsSetStateWithKey(SOSDataSourceRef data_source, SOSTransactionRef txn, CFStringRef key, CFStringRef pdmn, CFDataRef state, CFErrorRef *error) {
613 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
614 CFStringRef dataSourceID = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("SOSDataSource-%@"), ds->name);
615 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
616 kSecAttrAccessGroup, kSOSInternalAccessGroup,
617 kSecAttrAccount, key,
618 kSecAttrService, dataSourceID,
619 kSecAttrAccessible, pdmn,
620 kSecAttrSynchronizable, kCFBooleanFalse,
621 kSecValueData, state,
622 NULL);
623 CFReleaseSafe(dataSourceID);
624 SecDbItemRef item = SecDbItemCreateWithAttributes(kCFAllocatorDefault, &genp_class, dict, KEYBAG_DEVICE, error);
625 SOSMergeResult mr = dsMergeObject(txn, (SOSObjectRef)item, NULL, error);
626 if (mr == kSOSMergeFailure) secerror("failed to save %@@%@ state: %@", key, pdmn, error ? *error : NULL);
627 CFReleaseSafe(item);
628 CFReleaseSafe(dict);
629 return mr != kSOSMergeFailure;
630 }
631
632 static bool dsRestoreObject(SOSTransactionRef txn, uint64_t handle, CFDictionaryRef item, CFErrorRef *error) {
633 CFStringRef item_class = CFDictionaryGetValue(item, kSecItemBackupClassKey);
634 CFDataRef data = CFDictionaryGetValue(item, kSecItemBackupDataKey);
635 const SecDbClass *dbclass = NULL;
636
637 if (!item_class || !data)
638 return SecError(errSecDecode, error, CFSTR("no class or data in object"));
639
640 dbclass = kc_class_with_name(item_class);
641 if (!dbclass)
642 return SecError(errSecDecode, error, CFSTR("no such class %@; update kc_class_with_name "), item_class);
643
644 SecDbItemRef dbitem = SecDbItemCreateWithEncryptedData(kCFAllocatorDefault, dbclass, data, (keybag_handle_t)handle, error);
645 bool ok = dbitem && (dsMergeObject(txn, (SOSObjectRef)dbitem, NULL, error) != kSOSMergeFailure);
646 CFReleaseSafe(dbitem);
647 return ok;
648 }
649
650 SOSDataSourceRef SecItemDataSourceCreate(SecDbRef db, CFStringRef name, CFErrorRef *error) {
651 SecItemDataSourceRef ds = calloc(1, sizeof(struct SecItemDataSource));
652 ds->ds.dsGetName = dsGetName;
653 ds->ds.dsSetNotifyPhaseBlock = dsSetNotifyPhaseBlock;
654 ds->ds.dsCopyManifestWithViewNameSet = dsCopyManifestWithViewNameSet;
655 ds->ds.dsCopyStateWithKey = dsCopyStateWithKey;
656 ds->ds.dsCopyItemDataWithKeys = dsCopyItemDataWithKeys;
657
658 ds->ds.dsForEachObject = dsForEachObject;
659 ds->ds.dsWith = dsWith;
660 ds->ds.dsRelease = dsRelease;
661
662 ds->ds.dsMergeObject = dsMergeObject;
663 ds->ds.dsSetStateWithKey = dsSetStateWithKey;
664 ds->ds.dsRestoreObject = dsRestoreObject;
665
666 // Object field accessors
667 ds->ds.objectCopyDigest = copyObjectDigest;
668 ds->ds.objectCopyPrimaryKey = objectCopyPrimaryKey;
669
670 // Object encode and decode.
671 ds->ds.objectCreateWithPropertyList = objectCreateWithPropertyList;
672 ds->ds.objectCopyPropertyList = objectCopyPropertyList;
673 ds->ds.objectCopyBackup = objectCopyBackup;
674
675 ds->db = CFRetainSafe(db);
676 ds->name = CFRetainSafe(name);
677
678 // Do this after the ds is fully setup so the engine can query us right away.
679 ds->ds.engine = SOSEngineCreate(&ds->ds, error);
680 if (!ds->ds.engine) {
681 free(ds);
682 ds = NULL;
683 }
684 return &ds->ds;
685 }
686
687 static CFStringRef SecItemDataSourceFactoryCopyName(SOSDataSourceFactoryRef factory)
688 {
689 // This is the name of the v0 datasource, a.k.a. "ak"
690 return kSecAttrAccessibleWhenUnlocked;
691 }
692
693 struct SecItemDataSourceFactory {
694 struct SOSDataSourceFactory factory;
695 CFMutableDictionaryRef dsCache;
696 dispatch_queue_t queue;
697 SecDbRef db;
698 };
699
700 static SOSDataSourceRef SecItemDataSourceFactoryCopyDataSource(SOSDataSourceFactoryRef factory, CFStringRef dataSourceName, CFErrorRef *error)
701 {
702 struct SecItemDataSourceFactory *f = (struct SecItemDataSourceFactory *)factory;
703 __block SOSDataSourceRef dataSource = NULL;
704 dispatch_sync(f->queue, ^{
705 dataSource = (SOSDataSourceRef)CFDictionaryGetValue(f->dsCache, dataSourceName);
706 if (!dataSource) {
707 dataSource = (SOSDataSourceRef)SecItemDataSourceCreate(f->db, dataSourceName, error);
708 CFDictionarySetValue(f->dsCache, dataSourceName, dataSource);
709 }
710 });
711 return dataSource;
712 }
713
714 static void SecItemDataSourceFactoryDispose(SOSDataSourceFactoryRef factory)
715 {
716 // Nothing to do here.
717 }
718
719 static void SecItemDataSourceFactoryCircleChanged(SOSDataSourceFactoryRef factory, CFStringRef myPeerID, CFArrayRef trustedPeerIDs, CFArrayRef untrustedPeerIDs) {
720 CFStringRef dsName = SOSDataSourceFactoryCopyName(factory);
721 SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(factory, dsName, NULL);
722 if (engine)
723 SOSEngineCircleChanged(engine, myPeerID, trustedPeerIDs, untrustedPeerIDs);
724 CFReleaseSafe(dsName);
725 }
726
727 // Fire up the SOSEngines so they can
728 static bool SOSDataSourceFactoryStartYourEngines(SOSDataSourceFactoryRef factory) {
729 bool ok = true;
730 CFStringRef dsName = SOSDataSourceFactoryCopyName(factory);
731 CFErrorRef localError = NULL;
732 SOSDataSourceRef ds = SOSDataSourceFactoryCreateDataSource(factory, dsName, &localError);
733 if (!ds)
734 secerror("create_datasource %@ failed %@", dsName, localError);
735 CFReleaseNull(localError);
736 SOSDataSourceRelease(ds, &localError);
737 CFReleaseNull(localError);
738 CFReleaseNull(dsName);
739 return ok;
740 }
741
742 static SOSDataSourceFactoryRef SecItemDataSourceFactoryCreate(SecDbRef db) {
743 struct SecItemDataSourceFactory *dsf = calloc(1, sizeof(struct SecItemDataSourceFactory));
744 dsf->factory.copy_name = SecItemDataSourceFactoryCopyName;
745 dsf->factory.create_datasource = SecItemDataSourceFactoryCopyDataSource;
746 dsf->factory.release = SecItemDataSourceFactoryDispose;
747 dsf->factory.circle_changed = SecItemDataSourceFactoryCircleChanged;
748
749 dsf->dsCache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
750 dsf->queue = dispatch_queue_create("dsf queue", DISPATCH_QUEUE_SERIAL);
751 dsf->db = CFRetainSafe(db);
752 if (!SOSDataSourceFactoryStartYourEngines(&dsf->factory))
753 secerror("Failed to start engines, gonna lose the race.");
754 return &dsf->factory;
755 }
756
757 SOSDataSourceFactoryRef SecItemDataSourceFactoryGetShared(SecDbRef db) {
758 static dispatch_once_t sDSFQueueOnce;
759 static dispatch_queue_t sDSFQueue;
760 static CFMutableDictionaryRef sDSTable = NULL;
761
762 dispatch_once(&sDSFQueueOnce, ^{
763 sDSFQueue = dispatch_queue_create("dataSourceFactory queue", DISPATCH_QUEUE_SERIAL);
764 });
765
766 __block SOSDataSourceFactoryRef result = NULL;
767 dispatch_sync(sDSFQueue, ^{
768 CFStringRef dbPath = SecDbGetPath(db);
769 if (sDSTable) {
770 result = (SOSDataSourceFactoryRef) CFDictionaryGetValue(sDSTable, dbPath);
771 } else {
772 sDSTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
773 }
774
775 if (!result) {
776 result = SecItemDataSourceFactoryCreate(db);
777
778 CFDictionaryAddValue(sDSTable, dbPath, result);
779 }
780 });
781
782 return result;
783 }
784
785 // TODO: These should move to SecItemServer.c
786
787 void SecItemServerAppendItemDescription(CFMutableStringRef desc, CFDictionaryRef object) {
788 SOSObjectRef item = objectCreateWithPropertyList(object, NULL);
789 if (item) {
790 CFStringRef itemDesc = CFCopyDescription(item);
791 if (itemDesc) {
792 CFStringAppend(desc, itemDesc);
793 CFReleaseSafe(itemDesc);
794 }
795 CFRelease(item);
796 }
797 }
798
799 SOSManifestRef SOSCreateManifestWithBackup(CFDictionaryRef backup, CFErrorRef *error)
800 {
801 __block struct SOSDigestVector dv = SOSDigestVectorInit;
802 if (backup) {
803 CFDictionaryForEach(backup, ^void (const void * key, const void * value) {
804 if (isDictionary(value)) {
805 /* converting key back to binary blob is horrible */
806 CFDataRef sha1 = CFDictionaryGetValue(value, kSecItemBackupHashKey);
807 if (isData(sha1) && CFDataGetLength(sha1) == CCSHA1_OUTPUT_SIZE)
808 SOSDigestVectorAppend(&dv, CFDataGetBytePtr(sha1));
809 }
810 });
811 }
812 SOSManifestRef manifest = SOSManifestCreateWithDigestVector(&dv, error);
813 SOSDigestVectorFree(&dv);
814 return manifest;
815 }