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