]> git.saurik.com Git - apple/security.git/blob - Security/sec/securityd/SecItemDataSource.c
Security-57031.10.10.tar.gz
[apple/security.git] / Security / 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 <SecureObjectSync/SOSDigestVector.h>
36 #include <Security/SecBasePriv.h>
37 #include <Security/SecItem.h>
38 #include <Security/SecItemPriv.h>
39 #include <utilities/array_size.h>
40
41 /*
42 *
43 *
44 * SecItemDataSource
45 *
46 *
47 */
48 typedef struct SecItemDataSource *SecItemDataSourceRef;
49
50 struct SecItemDataSource {
51 struct SOSDataSource ds;
52 SecDbRef db; // The database we operate on
53 CFStringRef name; // The name of the slice of the database we represent.
54 };
55
56 static const SecDbClass *dsSyncedClasses[] = {
57 &genp_class,
58 &inet_class,
59 &keys_class,
60 };
61
62 static bool SecDbItemSelectSHA1(SecDbQueryRef query, SecDbConnectionRef dbconn, CFErrorRef *error,
63 bool (^use_attr_in_where)(const SecDbAttr *attr),
64 bool (^add_where_sql)(CFMutableStringRef sql, bool *needWhere),
65 bool (^bind_added_where)(sqlite3_stmt *stmt, int col),
66 void (^row)(sqlite3_stmt *stmt, bool *stop)) {
67 __block bool ok = true;
68 bool (^return_attr)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) {
69 return attr->kind == kSecDbSHA1Attr;
70 };
71 CFStringRef sql = SecDbItemCopySelectSQL(query, return_attr, use_attr_in_where, add_where_sql);
72 if (sql) {
73 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) {
74 ok = (SecDbItemSelectBind(query, stmt, error, use_attr_in_where, bind_added_where) &&
75 SecDbStep(dbconn, stmt, error, ^(bool *stop){ row(stmt, stop); }));
76 });
77 CFRelease(sql);
78 } else {
79 ok = false;
80 }
81 return ok;
82 }
83
84 static SOSManifestRef SecItemDataSourceCopyManifest(SecItemDataSourceRef ds, CFErrorRef *error) {
85 __block SOSManifestRef manifest = NULL;
86 __block CFErrorRef localError = NULL;
87 if (!SecDbPerformRead(ds->db, error, ^(SecDbConnectionRef dbconn) {
88 __block struct SOSDigestVector dv = SOSDigestVectorInit;
89 for (size_t class_ix = 0; class_ix < array_size(dsSyncedClasses);
90 ++class_ix) {
91 Query *q = query_create(dsSyncedClasses[class_ix], NULL, error);
92 if (q) {
93 q->q_return_type = kSecReturnDataMask | kSecReturnAttributesMask;
94 q->q_limit = kSecMatchUnlimited;
95 q->q_keybag = KEYBAG_DEVICE;
96 query_add_attribute(kSecAttrSynchronizable, kCFBooleanTrue, q);
97 //query_add_attribute(kSecAttrAccessible, ds->name, q);
98 // Select everything including tombstones that is synchronizable.
99 if (!SecDbItemSelectSHA1(q, dbconn, &localError, ^bool(const SecDbAttr *attr) {
100 return attr->kind == kSecDbSyncAttr;
101 }, NULL, NULL, ^(sqlite3_stmt *stmt, bool *stop) {
102 const uint8_t *digest = sqlite3_column_blob(stmt, 0);
103 size_t digestLen = sqlite3_column_bytes(stmt, 0);
104 if (digestLen != SOSDigestSize) {
105 secerror("digest %zu bytes", digestLen);
106 } else {
107 SOSDigestVectorAppend(&dv, digest);
108 }
109 })) {
110 secerror("SecDbItemSelect failed: %@", localError);
111 }
112 query_destroy(q, &localError);
113 if (localError) {
114 secerror("query_destroy failed: %@", localError);
115 }
116 } else if (localError) {
117 secerror("query_create failed: %@", localError);
118 }
119 }
120 manifest = SOSManifestCreateWithDigestVector(&dv, &localError);
121 SOSDigestVectorFree(&dv);
122 })) {
123 CFReleaseNull(manifest);
124 };
125
126 if (error && !*error && localError)
127 *error = localError;
128 else
129 CFReleaseSafe(localError);
130
131 return manifest;
132 }
133
134 // Return the newest object (conflict resolver)
135 static SecDbItemRef SecItemDataSourceCopyMergedItem(SecDbItemRef item1, SecDbItemRef item2, CFErrorRef *error) {
136 CFErrorRef localError = NULL;
137 SecDbItemRef result = NULL;
138 CFDateRef m1, m2;
139 const SecDbAttr *desc = SecDbAttrWithKey(SecDbItemGetClass(item1), kSecAttrModificationDate, error);
140 m1 = SecDbItemGetValue(item1, desc, &localError);
141 m2 = SecDbItemGetValue(item2, desc, &localError);
142 if (m1 && m2) switch (CFDateCompare(m1, m2, NULL)) {
143 case kCFCompareGreaterThan:
144 result = item1;
145 break;
146 case kCFCompareLessThan:
147 result = item2;
148 break;
149 case kCFCompareEqualTo:
150 {
151 // Return the item with the smallest digest.
152 CFDataRef digest1 = SecDbItemGetSHA1(item1, &localError);
153 CFDataRef digest2 = SecDbItemGetSHA1(item2, &localError);
154 if (digest1 && digest2) switch (CFDataCompare(digest1, digest2)) {
155 case kCFCompareGreaterThan:
156 case kCFCompareEqualTo:
157 result = item2;
158 break;
159 case kCFCompareLessThan:
160 result = item1;
161 break;
162 } else if (SecErrorGetOSStatus(localError) == errSecDecode) {
163 if (digest1) result = item1;
164 if (digest2) result = item2;
165 }
166 break;
167 }
168 } else if (SecErrorGetOSStatus(localError) == errSecDecode) {
169 // If one of the two objects has an unparsable date,
170 // the object with the parsable date wins.
171 if (m1) result = item1;
172 if (m2) result = item2;
173 }
174
175 if (localError) {
176 if (!result && error && !*error)
177 *error = localError;
178 else
179 CFRelease(localError);
180 }
181 return CFRetainSafe(result);
182 }
183
184 //
185 // MARK: DataSource protocol implementation
186 //
187
188 static CFStringRef dsGetName(SOSDataSourceRef data_source) {
189 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
190 return ds->name;
191 }
192
193 static void dsSetNotifyPhaseBlock(SOSDataSourceRef data_source, SOSDataSourceNotifyBlock notifyBlock) {
194 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
195 SecDbSetNotifyPhaseBlock(ds->db, ^(SecDbConnectionRef dbconn, SecDbTransactionPhase phase, SecDbTransactionSource source, struct SOSDigestVector *removals, struct SOSDigestVector *additions) {
196 if (notifyBlock) {
197 notifyBlock(&ds->ds, (SOSTransactionRef)dbconn, phase, source, removals, additions);
198 } else {
199 secerror("NULL notifyBlock"); // TODO: remove message; see SOSEngineSetTrustedPeers
200 }
201 });
202 }
203
204
205 static SOSManifestRef dsCopyManifest(SOSDataSourceRef data_source, CFErrorRef *error) {
206 struct SecItemDataSource *ds = (struct SecItemDataSource *)data_source;
207 return SecItemDataSourceCopyManifest(ds, error);
208 }
209
210 static bool dsForEachObject(SOSDataSourceRef data_source, SOSManifestRef manifest, CFErrorRef *error, void (^handle_object)(CFDataRef key, SOSObjectRef object, bool *stop)) {
211 struct SecItemDataSource *ds = (struct SecItemDataSource *)data_source;
212 __block bool result = true;
213 const SecDbAttr *sha1Attr = SecDbClassAttrWithKind(&genp_class, kSecDbSHA1Attr, error);
214 if (!sha1Attr) return false;
215 bool (^return_attr)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) {
216 return attr->kind == kSecDbRowIdAttr || attr->kind == kSecDbEncryptedDataAttr;
217 };
218 bool (^use_attr_in_where)(const SecDbAttr *attr) = ^bool (const SecDbAttr * attr) {
219 return attr->kind == kSecDbSHA1Attr;
220 };
221 Query *select_queries[array_size(dsSyncedClasses)] = {};
222 CFStringRef select_sql[array_size(dsSyncedClasses)] = {};
223 sqlite3_stmt *select_stmts[array_size(dsSyncedClasses)] = {};
224
225 __block Query **queries = select_queries;
226 __block CFStringRef *sqls = select_sql;
227 __block sqlite3_stmt **stmts = select_stmts;
228
229 result &= SecDbPerformRead(ds->db, error, ^(SecDbConnectionRef dbconn) {
230 // Setup
231 for (size_t class_ix = 0; class_ix < array_size(dsSyncedClasses); ++class_ix) {
232 result = (result
233 && (queries[class_ix] = query_create(dsSyncedClasses[class_ix], NULL, error))
234 && (sqls[class_ix] = SecDbItemCopySelectSQL(queries[class_ix], return_attr, use_attr_in_where, NULL))
235 && (stmts[class_ix] = SecDbCopyStmt(dbconn, sqls[class_ix], NULL, error)));
236 }
237
238 if (result) SOSManifestForEach(manifest, ^(CFDataRef key, bool *stop) {
239 __block SecDbItemRef item = NULL;
240 for (size_t class_ix = 0; result && !item && class_ix < array_size(dsSyncedClasses); ++class_ix) {
241 CFDictionarySetValue(queries[class_ix]->q_item, sha1Attr->name, key);
242 result = (SecDbItemSelectBind(queries[class_ix], stmts[class_ix], error, use_attr_in_where, NULL) && SecDbStep(dbconn, stmts[class_ix], error, ^(bool *unused_stop) {
243 item = SecDbItemCreateWithStatement(kCFAllocatorDefault, queries[class_ix]->q_class, stmts[class_ix], KEYBAG_DEVICE, error, return_attr);
244 })) && SecDbReset(stmts[class_ix], error);
245 }
246 handle_object(key, (SOSObjectRef)item, stop);
247 CFReleaseSafe(item);
248 });
249
250 // Cleanup
251 for (size_t class_ix = 0; class_ix < array_size(dsSyncedClasses); ++class_ix) {
252 result &= SecDbReleaseCachedStmt(dbconn, sqls[class_ix], stmts[class_ix], error);
253 CFReleaseSafe(sqls[class_ix]);
254 result &= query_destroy(queries[class_ix], error);
255 }
256 });
257 return result;
258 }
259
260 static bool dsRelease(SOSDataSourceRef data_source, CFErrorRef *error) {
261 // We never release our dataSource since it's tracking changes
262 // to the keychain for the engine and its peers.
263 return true;
264 }
265
266 static SOSObjectRef objectCreateWithPropertyList(CFDictionaryRef plist, CFErrorRef *error) {
267 SecDbItemRef item = NULL;
268 const SecDbClass *class = NULL;
269 CFTypeRef cname = CFDictionaryGetValue(plist, kSecClass);
270 if (cname) {
271 class = kc_class_with_name(cname);
272 if (class) {
273 item = SecDbItemCreateWithAttributes(kCFAllocatorDefault, class, plist, KEYBAG_DEVICE, error);
274 } else {
275 SecError(errSecNoSuchClass, error, CFSTR("can find class named: %@"), cname);
276 }
277 } else {
278 SecError(errSecItemClassMissing, error, CFSTR("query missing %@ attribute"), kSecClass);
279 }
280 return (SOSObjectRef)item;
281 }
282
283 static CFDataRef copyObjectDigest(SOSObjectRef object, CFErrorRef *error) {
284 SecDbItemRef item = (SecDbItemRef) object;
285 CFDataRef digest = SecDbItemGetSHA1(item, error);
286 CFRetainSafe(digest);
287 return digest;
288 }
289
290 static CFDataRef objectCopyPrimaryKey(SOSObjectRef object, CFErrorRef *error) {
291 SecDbItemRef item = (SecDbItemRef) object;
292 CFDataRef pk = SecDbItemGetPrimaryKey(item, error);
293 CFRetainSafe(pk);
294 return pk;
295 }
296
297 static CFDictionaryRef objectCopyPropertyList(SOSObjectRef object, CFErrorRef *error) {
298 SecDbItemRef item = (SecDbItemRef) object;
299 CFMutableDictionaryRef cryptoDataDict = SecDbItemCopyPListWithMask(item, kSecDbInCryptoDataFlag, error);
300 CFMutableDictionaryRef authDataDict = SecDbItemCopyPListWithMask(item, kSecDbInAuthenticatedDataFlag, error);
301
302 if (cryptoDataDict) {
303 if (authDataDict) {
304 CFDictionaryForEach(authDataDict, ^(const void *key, const void *value) {
305 CFDictionarySetValue(cryptoDataDict, key, value);
306 });
307 }
308 CFDictionaryAddValue(cryptoDataDict, kSecClass, SecDbItemGetClass(item)->name);
309 }
310
311 CFReleaseSafe(authDataDict);
312 return cryptoDataDict;
313 }
314
315 static bool dsWith(SOSDataSourceRef data_source, CFErrorRef *error, SOSDataSourceTransactionSource source, void(^transaction)(SOSTransactionRef txn, bool *commit)) {
316 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
317 __block bool ok = true;
318 ok &= SecDbPerformWrite(ds->db, error, ^(SecDbConnectionRef dbconn) {
319 ok &= SecDbTransaction(dbconn,
320 source == kSOSDataSourceAPITransaction ? kSecDbExclusiveTransactionType : kSecDbExclusiveRemoteTransactionType,
321 error, ^(bool *commit) {
322 transaction((SOSTransactionRef)dbconn, commit);
323 });
324 });
325 return ok;
326 }
327
328 static SOSMergeResult dsMergeObject(SOSTransactionRef txn, SOSObjectRef peersObject, SOSObjectRef *createdObject, CFErrorRef *error) {
329 SecDbConnectionRef dbconn = (SecDbConnectionRef)txn;
330 SecDbItemRef peersItem = (SecDbItemRef)peersObject;
331 __block SOSMergeResult mr = kSOSMergeFailure;
332 __block SecDbItemRef mergedItem = NULL;
333 __block SecDbItemRef replacedItem = NULL;
334 if (!peersItem || !dbconn || !SecDbItemSetKeybag(peersItem, KEYBAG_DEVICE, error)) return mr;
335 if (SecDbItemInsertOrReplace(peersItem, dbconn, NULL, error, ^(SecDbItemRef myItem, SecDbItemRef *replace) {
336 // An item with the same primary key as dbItem already exists in the the database. That item is old_item.
337 // Let the conflict resolver choose which item to keep.
338 mergedItem = SecItemDataSourceCopyMergedItem(peersItem, myItem, error);
339 if (!mergedItem) return;
340 if (CFEqual(mergedItem, myItem)) {
341 // Conflict resolver choose my (local) item
342 secnotice("ds", "Conflict resolver choose my (local) item: %@", myItem);
343 mr = kSOSMergeLocalObject;
344 } else {
345 CFRetainAssign(replacedItem, myItem);
346 *replace = CFRetainSafe(mergedItem);
347 if (CFEqual(mergedItem, peersItem)) {
348 // Conflict resolver choose peers item
349 secnotice("ds", "Conflict resolver choose peers item: %@", peersItem);
350 mr = kSOSMergePeersObject;
351 } else {
352 // Conflict resolver created a new item; return it to our caller
353 secnotice("ds", "Conflict resolver created a new item; return it to our caller: %@", mergedItem);
354 if (createdObject)
355 *createdObject = (SOSObjectRef)CFRetain(mergedItem);
356 mr = kSOSMergeCreatedObject;
357 }
358 }
359 })) {
360 if (mr == kSOSMergeFailure)
361 {
362 secnotice("ds", "kSOSMergeFailure => kSOSMergePeersObject");
363 mr = kSOSMergePeersObject;
364 }
365 }
366
367 if (error && *error && mr != kSOSMergeFailure)
368 CFReleaseNull(*error);
369
370 CFReleaseSafe(mergedItem);
371 CFReleaseSafe(replacedItem);
372 return mr;
373 }
374
375 /*
376 Truthy backup format is a dictionary from sha1 => item.
377 Each item has class, hash and item data.
378
379 TODO: sha1 is included as binary blob to avoid parsing key.
380 */
381 enum {
382 kSecBackupIndexHash = 0,
383 kSecBackupIndexClass,
384 kSecBackupIndexData,
385 };
386
387 static const void *kSecBackupKeys[] = {
388 [kSecBackupIndexHash] = CFSTR("hash"),
389 [kSecBackupIndexClass] = CFSTR("class"),
390 [kSecBackupIndexData] = CFSTR("data"),
391 };
392
393 #define kSecBackupHash kSecBackupKeys[kSecBackupIndexHash]
394 #define kSecBackupClass kSecBackupKeys[kSecBackupIndexClass]
395 #define kSecBackupData kSecBackupKeys[kSecBackupIndexData]
396
397 static CFDictionaryRef objectCopyBackup(SOSObjectRef object, uint64_t handle, CFErrorRef *error) {
398 const void *values[array_size(kSecBackupKeys)];
399 SecDbItemRef item = (SecDbItemRef)object;
400 CFDictionaryRef backup_item = NULL;
401
402 if ((values[kSecBackupIndexHash] = SecDbItemGetSHA1(item, error))) {
403 if ((values[kSecBackupIndexData] = SecDbItemCopyEncryptedDataToBackup(item, handle, error))) {
404 values[kSecBackupIndexClass] = SecDbItemGetClass(item)->name;
405 backup_item = CFDictionaryCreate(kCFAllocatorDefault, kSecBackupKeys, values, array_size(kSecBackupKeys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
406 CFRelease(values[kSecBackupIndexData]);
407 }
408 }
409
410 return backup_item;
411 }
412
413 static CFDataRef dsCopyStateWithKey(SOSDataSourceRef data_source, CFStringRef key, CFStringRef pdmn, CFErrorRef *error) {
414 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
415 CFStringRef dataSourceID = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("SOSDataSource-%@"), ds->name);
416 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
417 kSecAttrAccessGroup, kSOSInternalAccessGroup,
418 kSecAttrAccount, key,
419 kSecAttrService, dataSourceID,
420 kSecAttrAccessible, pdmn,
421 kSecAttrSynchronizable, kCFBooleanFalse,
422 NULL);
423 CFReleaseSafe(dataSourceID);
424 __block CFDataRef data = NULL;
425 SecDbQueryRef query = query_create(&genp_class, dict, error);
426 if (query) {
427 if (query->q_item) CFReleaseSafe(query->q_item);
428 query->q_item = dict;
429 SecDbPerformRead(ds->db, error, ^(SecDbConnectionRef dbconn) {
430 SecDbItemSelect(query, dbconn, error, ^bool(const SecDbAttr *attr) {
431 return CFDictionaryContainsKey(dict, attr->name);
432 }, NULL, NULL, ^(SecDbItemRef item, bool *stop) {
433 secnotice("ds", "found item for key %@@%@", key, pdmn);
434 data = CFRetainSafe(SecDbItemGetValue(item, &v6v_Data, error));
435 });
436 });
437 query_destroy(query, error);
438 } else {
439 CFReleaseSafe(dict);
440 }
441 if (!data) secnotice("ds", "failed to load %@@%@ state: %@", key, pdmn, error ? *error : NULL);
442 return data;
443 }
444
445 static bool dsSetStateWithKey(SOSDataSourceRef data_source, SOSTransactionRef txn, CFStringRef key, CFStringRef pdmn, CFDataRef state, CFErrorRef *error) {
446 SecItemDataSourceRef ds = (SecItemDataSourceRef)data_source;
447 CFStringRef dataSourceID = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("SOSDataSource-%@"), ds->name);
448 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault,
449 kSecAttrAccessGroup, kSOSInternalAccessGroup,
450 kSecAttrAccount, key,
451 kSecAttrService, dataSourceID,
452 kSecAttrAccessible, pdmn,
453 kSecAttrSynchronizable, kCFBooleanFalse,
454 kSecValueData, state,
455 NULL);
456 CFReleaseSafe(dataSourceID);
457 SecDbItemRef item = SecDbItemCreateWithAttributes(kCFAllocatorDefault, &genp_class, dict, KEYBAG_DEVICE, error);
458 SOSMergeResult mr = dsMergeObject(txn, (SOSObjectRef)item, NULL, error);
459 if (mr == kSOSMergeFailure) secerror("failed to save %@@%@ state: %@", key, pdmn, error ? *error : NULL);
460 CFReleaseSafe(item);
461 CFReleaseSafe(dict);
462 return mr != kSOSMergeFailure;
463 }
464
465 static bool dsRestoreObject(SOSTransactionRef txn, uint64_t handle, CFDictionaryRef item, CFErrorRef *error) {
466 CFStringRef item_class = CFDictionaryGetValue(item, kSecBackupClass);
467 CFDataRef data = CFDictionaryGetValue(item, kSecBackupData);
468 const SecDbClass *dbclass = NULL;
469
470 if (!item_class || !data)
471 return SecError(errSecDecode, error, CFSTR("no class or data in object"));
472
473 dbclass = kc_class_with_name(item_class);
474 if (!dbclass)
475 return SecError(errSecDecode, error, CFSTR("no such class %@; update kc_class_with_name "), item_class);
476
477 SecDbItemRef dbitem = SecDbItemCreateWithEncryptedData(kCFAllocatorDefault, dbclass, data, (keybag_handle_t)handle, error);
478 bool ok = dbitem && (dsMergeObject(txn, (SOSObjectRef)dbitem, NULL, error) != kSOSMergeFailure);
479 CFReleaseSafe(dbitem);
480 return ok;
481 }
482
483 SOSDataSourceRef SecItemDataSourceCreate(SecDbRef db, CFStringRef name, CFErrorRef *error) {
484 SecItemDataSourceRef ds = calloc(1, sizeof(struct SecItemDataSource));
485 ds->ds.dsGetName = dsGetName;
486 ds->ds.dsSetNotifyPhaseBlock = dsSetNotifyPhaseBlock;
487 ds->ds.dsCopyManifest = dsCopyManifest;
488 ds->ds.dsCopyStateWithKey = dsCopyStateWithKey;
489 ds->ds.dsForEachObject = dsForEachObject;
490 ds->ds.dsWith = dsWith;
491 ds->ds.dsRelease = dsRelease;
492
493 ds->ds.dsMergeObject = dsMergeObject;
494 ds->ds.dsSetStateWithKey = dsSetStateWithKey;
495 ds->ds.dsRestoreObject = dsRestoreObject;
496
497 // Object field accessors
498 ds->ds.objectCopyDigest = copyObjectDigest;
499 ds->ds.objectCopyPrimaryKey = objectCopyPrimaryKey;
500
501 // Object encode and decode.
502 ds->ds.objectCreateWithPropertyList = objectCreateWithPropertyList;
503 ds->ds.objectCopyPropertyList = objectCopyPropertyList;
504 ds->ds.objectCopyBackup = objectCopyBackup;
505
506 ds->db = CFRetainSafe(db);
507 ds->name = CFRetainSafe(name);
508
509 // Do this after the ds is fully setup so the engine can query us right away.
510 ds->ds.engine = SOSEngineCreate(&ds->ds, error);
511 if (!ds->ds.engine) {
512 free(ds);
513 ds = NULL;
514 }
515 return &ds->ds;
516 }
517
518 static CFArrayRef SecItemDataSourceFactoryCopyNames(SOSDataSourceFactoryRef factory)
519 {
520 return CFArrayCreateForCFTypes(kCFAllocatorDefault,
521 kSecAttrAccessibleWhenUnlocked,
522 //kSecAttrAccessibleAfterFirstUnlock,
523 //kSecAttrAccessibleAlways,
524 NULL);
525 }
526
527 struct SecItemDataSourceFactory {
528 struct SOSDataSourceFactory factory;
529 CFMutableDictionaryRef dsCache;
530 dispatch_queue_t queue;
531 SecDbRef db;
532 };
533
534 static SOSDataSourceRef SecItemDataSourceFactoryCopyDataSource(SOSDataSourceFactoryRef factory, CFStringRef dataSourceName, CFErrorRef *error)
535 {
536 struct SecItemDataSourceFactory *f = (struct SecItemDataSourceFactory *)factory;
537 __block SOSDataSourceRef dataSource = NULL;
538 dispatch_sync(f->queue, ^{
539 dataSource = (SOSDataSourceRef)CFDictionaryGetValue(f->dsCache, dataSourceName);
540 if (!dataSource) {
541 dataSource = (SOSDataSourceRef)SecItemDataSourceCreate(f->db, dataSourceName, error);
542 CFDictionarySetValue(f->dsCache, dataSourceName, dataSource);
543 }
544 });
545 return dataSource;
546 }
547
548 static void SecItemDataSourceFactoryDispose(SOSDataSourceFactoryRef factory)
549 {
550 // Nothing to do here.
551 }
552
553 // Fire up the SOSEngines so they can
554 static bool SOSDataSourceFactoryStartYourEngines(SOSDataSourceFactoryRef factory) {
555 bool ok = true;
556 CFArrayRef dsNames = factory->copy_names(factory);
557 CFStringRef dsName = NULL;
558 CFArrayForEachC(dsNames, dsName) {
559 CFErrorRef localError = NULL;
560 SOSDataSourceRef ds = factory->create_datasource(factory, dsName, &localError);
561 if (!ds)
562 secerror("create_datasource %@ failed %@", dsName, localError);
563 CFReleaseNull(localError);
564 SOSDataSourceRelease(ds, &localError);
565 CFReleaseNull(localError);
566 }
567 CFReleaseSafe(dsNames);
568 return ok;
569 }
570
571 static SOSDataSourceFactoryRef SecItemDataSourceFactoryCreate(SecDbRef db) {
572 struct SecItemDataSourceFactory *dsf = calloc(1, sizeof(struct SecItemDataSourceFactory));
573 dsf->factory.copy_names = SecItemDataSourceFactoryCopyNames;
574 dsf->factory.create_datasource = SecItemDataSourceFactoryCopyDataSource;
575 dsf->factory.release = SecItemDataSourceFactoryDispose;
576 dsf->dsCache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
577 dsf->queue = dispatch_queue_create("dsf queue", DISPATCH_QUEUE_SERIAL);
578 dsf->db = CFRetainSafe(db);
579 if (!SOSDataSourceFactoryStartYourEngines(&dsf->factory))
580 secerror("Failed to start engines, gonna lose the race.");
581 return &dsf->factory;
582 }
583
584 SOSDataSourceFactoryRef SecItemDataSourceFactoryGetShared(SecDbRef db) {
585 static dispatch_once_t sDSFQueueOnce;
586 static dispatch_queue_t sDSFQueue;
587 static CFMutableDictionaryRef sDSTable = NULL;
588
589 dispatch_once(&sDSFQueueOnce, ^{
590 sDSFQueue = dispatch_queue_create("dataSourceFactory queue", DISPATCH_QUEUE_SERIAL);
591 });
592
593 __block SOSDataSourceFactoryRef result = NULL;
594 dispatch_sync(sDSFQueue, ^{
595 CFStringRef dbPath = SecDbGetPath(db);
596 if (sDSTable) {
597 result = (SOSDataSourceFactoryRef) CFDictionaryGetValue(sDSTable, dbPath);
598 } else {
599 sDSTable = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
600 }
601
602 if (!result) {
603 result = SecItemDataSourceFactoryCreate(db);
604
605 CFDictionaryAddValue(sDSTable, dbPath, result);
606 }
607 });
608
609 return result;
610 }
611
612 void SecItemServerAppendItemDescription(CFMutableStringRef desc, CFDictionaryRef object) {
613 SOSObjectRef item = objectCreateWithPropertyList(object, NULL);
614 if (item) {
615 CFStringRef itemDesc = CFCopyDescription(item);
616 if (itemDesc) {
617 CFStringAppend(desc, itemDesc);
618 CFReleaseSafe(itemDesc);
619 }
620 CFRelease(item);
621 }
622 }
623
624 SOSManifestRef SOSCreateManifestWithBackup(CFDictionaryRef backup, CFErrorRef *error)
625 {
626 __block struct SOSDigestVector dv = SOSDigestVectorInit;
627 if (backup) {
628 CFDictionaryForEach(backup, ^void (const void * key, const void * value) {
629 if (isDictionary(value)) {
630 /* converting key back to binary blob is horrible */
631 CFDataRef sha1 = CFDictionaryGetValue(value, kSecBackupHash);
632 if (isData(sha1) && CFDataGetLength(sha1) == CCSHA1_OUTPUT_SIZE)
633 SOSDigestVectorAppend(&dv, CFDataGetBytePtr(sha1));
634 }
635 });
636 }
637 SOSManifestRef manifest = SOSManifestCreateWithDigestVector(&dv, error);
638 SOSDigestVectorFree(&dv);
639 return manifest;
640 }