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