2 * Copyright (c) 2006-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecItemDataSource.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
30 #include <securityd/SecItemDataSource.h>
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>
48 typedef struct SecItemDataSource
*SecItemDataSourceRef
;
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.
56 static const SecDbClass
*dsSyncedClasses
[] = {
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
;
71 CFStringRef sql
= SecDbItemCopySelectSQL(query
, return_attr
, use_attr_in_where
, add_where_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
); }));
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
);
91 Query
*q
= query_create(dsSyncedClasses
[class_ix
], NULL
, error
);
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
);
107 SOSDigestVectorAppend(&dv
, digest
);
110 secerror("SecDbItemSelect failed: %@", localError
);
112 query_destroy(q
, &localError
);
114 secerror("query_destroy failed: %@", localError
);
116 } else if (localError
) {
117 secerror("query_create failed: %@", localError
);
120 manifest
= SOSManifestCreateWithDigestVector(&dv
, &localError
);
121 SOSDigestVectorFree(&dv
);
123 CFReleaseNull(manifest
);
126 if (error
&& !*error
&& localError
)
129 CFReleaseSafe(localError
);
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
;
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
:
146 case kCFCompareLessThan
:
149 case kCFCompareEqualTo
:
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
:
159 case kCFCompareLessThan
:
162 } else if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
163 if (digest1
) result
= item1
;
164 if (digest2
) result
= item2
;
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
;
176 if (!result
&& error
&& !*error
)
179 CFRelease(localError
);
181 return CFRetainSafe(result
);
185 // MARK: DataSource protocol implementation
188 static CFStringRef
dsGetName(SOSDataSourceRef data_source
) {
189 SecItemDataSourceRef ds
= (SecItemDataSourceRef
)data_source
;
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
) {
197 notifyBlock(&ds
->ds
, (SOSTransactionRef
)dbconn
, phase
, source
, removals
, additions
);
199 secerror("NULL notifyBlock"); // TODO: remove message; see SOSEngineSetTrustedPeers
205 static SOSManifestRef
dsCopyManifest(SOSDataSourceRef data_source
, CFErrorRef
*error
) {
206 struct SecItemDataSource
*ds
= (struct SecItemDataSource
*)data_source
;
207 return SecItemDataSourceCopyManifest(ds
, error
);
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
;
218 bool (^use_attr_in_where
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
219 return attr
->kind
== kSecDbSHA1Attr
;
221 Query
*select_queries
[array_size(dsSyncedClasses
)] = {};
222 CFStringRef select_sql
[array_size(dsSyncedClasses
)] = {};
223 sqlite3_stmt
*select_stmts
[array_size(dsSyncedClasses
)] = {};
225 __block Query
**queries
= select_queries
;
226 __block CFStringRef
*sqls
= select_sql
;
227 __block sqlite3_stmt
**stmts
= select_stmts
;
229 result
&= SecDbPerformRead(ds
->db
, error
, ^(SecDbConnectionRef dbconn
) {
231 for (size_t class_ix
= 0; class_ix
< array_size(dsSyncedClasses
); ++class_ix
) {
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
)));
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
);
246 handle_object(key
, (SOSObjectRef
)item
, stop
);
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
);
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.
266 static SOSObjectRef
objectCreateWithPropertyList(CFDictionaryRef plist
, CFErrorRef
*error
) {
267 SecDbItemRef item
= NULL
;
268 const SecDbClass
*class = NULL
;
269 CFTypeRef cname
= CFDictionaryGetValue(plist
, kSecClass
);
271 class = kc_class_with_name(cname
);
273 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, class, plist
, KEYBAG_DEVICE
, error
);
275 SecError(errSecNoSuchClass
, error
, CFSTR("can find class named: %@"), cname
);
278 SecError(errSecItemClassMissing
, error
, CFSTR("query missing %@ attribute"), kSecClass
);
280 return (SOSObjectRef
)item
;
283 static CFDataRef
copyObjectDigest(SOSObjectRef object
, CFErrorRef
*error
) {
284 SecDbItemRef item
= (SecDbItemRef
) object
;
285 CFDataRef digest
= SecDbItemGetSHA1(item
, error
);
286 CFRetainSafe(digest
);
290 static CFDataRef
objectCopyPrimaryKey(SOSObjectRef object
, CFErrorRef
*error
) {
291 SecDbItemRef item
= (SecDbItemRef
) object
;
292 CFDataRef pk
= SecDbItemGetPrimaryKey(item
, error
);
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
);
302 if (cryptoDataDict
) {
304 CFDictionaryForEach(authDataDict
, ^(const void *key
, const void *value
) {
305 CFDictionarySetValue(cryptoDataDict
, key
, value
);
308 CFDictionaryAddValue(cryptoDataDict
, kSecClass
, SecDbItemGetClass(item
)->name
);
311 CFReleaseSafe(authDataDict
);
312 return cryptoDataDict
;
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
);
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
;
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
;
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
);
355 *createdObject
= (SOSObjectRef
)CFRetain(mergedItem
);
356 mr
= kSOSMergeCreatedObject
;
360 if (mr
== kSOSMergeFailure
)
362 secnotice("ds", "kSOSMergeFailure => kSOSMergePeersObject");
363 mr
= kSOSMergePeersObject
;
367 if (error
&& *error
&& mr
!= kSOSMergeFailure
)
368 CFReleaseNull(*error
);
370 CFReleaseSafe(mergedItem
);
371 CFReleaseSafe(replacedItem
);
376 Truthy backup format is a dictionary from sha1 => item.
377 Each item has class, hash and item data.
379 TODO: sha1 is included as binary blob to avoid parsing key.
382 kSecBackupIndexHash
= 0,
383 kSecBackupIndexClass
,
387 static const void *kSecBackupKeys
[] = {
388 [kSecBackupIndexHash
] = CFSTR("hash"),
389 [kSecBackupIndexClass
] = CFSTR("class"),
390 [kSecBackupIndexData
] = CFSTR("data"),
393 #define kSecBackupHash kSecBackupKeys[kSecBackupIndexHash]
394 #define kSecBackupClass kSecBackupKeys[kSecBackupIndexClass]
395 #define kSecBackupData kSecBackupKeys[kSecBackupIndexData]
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
;
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
]);
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
,
423 CFReleaseSafe(dataSourceID
);
424 __block CFDataRef data
= NULL
;
425 SecDbQueryRef query
= query_create(&genp_class
, dict
, error
);
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
));
437 query_destroy(query
, error
);
441 if (!data
) secnotice("ds", "failed to load %@@%@ state: %@", key
, pdmn
, error
? *error
: NULL
);
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
,
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
);
462 return mr
!= kSOSMergeFailure
;
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
;
470 if (!item_class
|| !data
)
471 return SecError(errSecDecode
, error
, CFSTR("no class or data in object"));
473 dbclass
= kc_class_with_name(item_class
);
475 return SecError(errSecDecode
, error
, CFSTR("no such class %@; update kc_class_with_name "), item_class
);
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
);
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
;
493 ds
->ds
.dsMergeObject
= dsMergeObject
;
494 ds
->ds
.dsSetStateWithKey
= dsSetStateWithKey
;
495 ds
->ds
.dsRestoreObject
= dsRestoreObject
;
497 // Object field accessors
498 ds
->ds
.objectCopyDigest
= copyObjectDigest
;
499 ds
->ds
.objectCopyPrimaryKey
= objectCopyPrimaryKey
;
501 // Object encode and decode.
502 ds
->ds
.objectCreateWithPropertyList
= objectCreateWithPropertyList
;
503 ds
->ds
.objectCopyPropertyList
= objectCopyPropertyList
;
504 ds
->ds
.objectCopyBackup
= objectCopyBackup
;
506 ds
->db
= CFRetainSafe(db
);
507 ds
->name
= CFRetainSafe(name
);
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
) {
518 static CFArrayRef
SecItemDataSourceFactoryCopyNames(SOSDataSourceFactoryRef factory
)
520 return CFArrayCreateForCFTypes(kCFAllocatorDefault
,
521 kSecAttrAccessibleWhenUnlocked
,
522 //kSecAttrAccessibleAfterFirstUnlock,
523 //kSecAttrAccessibleAlways,
527 struct SecItemDataSourceFactory
{
528 struct SOSDataSourceFactory factory
;
529 CFMutableDictionaryRef dsCache
;
530 dispatch_queue_t queue
;
534 static SOSDataSourceRef
SecItemDataSourceFactoryCopyDataSource(SOSDataSourceFactoryRef factory
, CFStringRef dataSourceName
, CFErrorRef
*error
)
536 struct SecItemDataSourceFactory
*f
= (struct SecItemDataSourceFactory
*)factory
;
537 __block SOSDataSourceRef dataSource
= NULL
;
538 dispatch_sync(f
->queue
, ^{
539 dataSource
= (SOSDataSourceRef
)CFDictionaryGetValue(f
->dsCache
, dataSourceName
);
541 dataSource
= (SOSDataSourceRef
)SecItemDataSourceCreate(f
->db
, dataSourceName
, error
);
542 CFDictionarySetValue(f
->dsCache
, dataSourceName
, dataSource
);
548 static void SecItemDataSourceFactoryDispose(SOSDataSourceFactoryRef factory
)
550 // Nothing to do here.
553 // Fire up the SOSEngines so they can
554 static bool SOSDataSourceFactoryStartYourEngines(SOSDataSourceFactoryRef factory
) {
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
);
562 secerror("create_datasource %@ failed %@", dsName
, localError
);
563 CFReleaseNull(localError
);
564 SOSDataSourceRelease(ds
, &localError
);
565 CFReleaseNull(localError
);
567 CFReleaseSafe(dsNames
);
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
;
584 SOSDataSourceFactoryRef
SecItemDataSourceFactoryGetShared(SecDbRef db
) {
585 static dispatch_once_t sDSFQueueOnce
;
586 static dispatch_queue_t sDSFQueue
;
587 static CFMutableDictionaryRef sDSTable
= NULL
;
589 dispatch_once(&sDSFQueueOnce
, ^{
590 sDSFQueue
= dispatch_queue_create("dataSourceFactory queue", DISPATCH_QUEUE_SERIAL
);
593 __block SOSDataSourceFactoryRef result
= NULL
;
594 dispatch_sync(sDSFQueue
, ^{
595 CFStringRef dbPath
= SecDbGetPath(db
);
597 result
= (SOSDataSourceFactoryRef
) CFDictionaryGetValue(sDSTable
, dbPath
);
599 sDSTable
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
603 result
= SecItemDataSourceFactoryCreate(db
);
605 CFDictionaryAddValue(sDSTable
, dbPath
, result
);
612 void SecItemServerAppendItemDescription(CFMutableStringRef desc
, CFDictionaryRef object
) {
613 SOSObjectRef item
= objectCreateWithPropertyList(object
, NULL
);
615 CFStringRef itemDesc
= CFCopyDescription(item
);
617 CFStringAppend(desc
, itemDesc
);
618 CFReleaseSafe(itemDesc
);
624 SOSManifestRef
SOSCreateManifestWithBackup(CFDictionaryRef backup
, CFErrorRef
*error
)
626 __block
struct SOSDigestVector dv
= SOSDigestVectorInit
;
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
));
637 SOSManifestRef manifest
= SOSManifestCreateWithDigestVector(&dv
, error
);
638 SOSDigestVectorFree(&dv
);