2 * Copyright (c) 2012-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 * SecDbItem.c - CoreFoundation-based constants and functions representing
26 * database items (certificates, keys, identities, and passwords.)
29 #include <securityd/SecDbItem.h>
30 #include <securityd/SecDbKeychainItem.h>
31 #include <securityd/SecItemDb.h>
32 #include <utilities/SecCFWrappers.h>
33 #include <utilities/SecCFCCWrappers.h>
34 #include <utilities/der_date.h>
35 #include <utilities/der_plist.h>
36 #include <utilities/debugging.h>
38 #include <Security/SecBasePriv.h>
39 #include <Security/SecInternal.h>
40 #include <corecrypto/ccsha1.h>
41 #include <Security/SecItem.h>
42 #include <Security/SecItemPriv.h>
43 #include <Security/SecAccessControl.h>
44 #include <Security/SecAccessControlPriv.h>
45 #include <securityd/SecItemSchema.h>
47 // MARK: type converters
49 CFStringRef
copyString(CFTypeRef obj
) {
50 CFTypeID tid
= CFGetTypeID(obj
);
51 if (tid
== CFStringGetTypeID())
52 return CFStringCreateCopy(0, obj
);
53 else if (tid
== CFDataGetTypeID())
54 return CFStringCreateFromExternalRepresentation(0, obj
, kCFStringEncodingUTF8
);
59 CFDataRef
copyData(CFTypeRef obj
) {
60 CFTypeID tid
= CFGetTypeID(obj
);
61 if (tid
== CFDataGetTypeID()) {
62 return CFDataCreateCopy(0, obj
);
63 } else if (tid
== CFStringGetTypeID()) {
64 return CFStringCreateExternalRepresentation(0, obj
, kCFStringEncodingUTF8
, 0);
65 } else if (tid
== CFNumberGetTypeID()) {
67 CFNumberGetValue(obj
, kCFNumberSInt32Type
, &value
);
68 return CFDataCreate(0, (const UInt8
*)&value
, sizeof(value
));
74 CFTypeRef
copyBlob(CFTypeRef obj
) {
75 CFTypeID tid
= CFGetTypeID(obj
);
76 if (tid
== CFDataGetTypeID()) {
77 return CFDataCreateCopy(0, obj
);
78 } else if (tid
== CFStringGetTypeID()) {
79 return CFStringCreateCopy(0, obj
);
80 } else if (tid
== CFNumberGetTypeID()) {
88 CFDataRef
copySHA1(CFTypeRef obj
) {
89 CFTypeID tid
= CFGetTypeID(obj
);
90 if (tid
== CFDataGetTypeID() && CFDataGetLength(obj
) == CCSHA1_OUTPUT_SIZE
) {
91 return CFDataCreateCopy(CFGetAllocator(obj
), obj
);
97 CFTypeRef
copyNumber(CFTypeRef obj
) {
98 CFTypeID tid
= CFGetTypeID(obj
);
99 if (tid
== CFNumberGetTypeID()) {
102 } else if (tid
== CFBooleanGetTypeID()) {
103 SInt32 value
= CFBooleanGetValue(obj
);
104 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
105 } else if (tid
== CFStringGetTypeID()) {
106 SInt32 value
= CFStringGetIntValue(obj
);
107 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value
);
108 /* If a string converted to an int isn't equal to the int printed as
109 a string, return a CFStringRef instead. */
110 if (!CFEqual(t
, obj
)) {
112 return CFStringCreateCopy(0, obj
);
115 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
120 CFDateRef
copyDate(CFTypeRef obj
) {
121 CFTypeID tid
= CFGetTypeID(obj
);
122 if (tid
== CFDateGetTypeID()) {
129 // MARK: SecDbColumn accessors, to retrieve values as CF types in SecDbStep.
131 static CFDataRef
SecDbColumnCopyData(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
132 return CFDataCreate(allocator
, sqlite3_column_blob(stmt
, col
),
133 sqlite3_column_bytes(stmt
, col
));
134 //return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col),
135 // sqlite3_column_bytes(stmt, col),
136 // kCFAllocatorNull);
139 static CFDateRef
SecDbColumnCopyDate(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
140 return CFDateCreate(allocator
, sqlite3_column_double(stmt
, col
));
143 static CFNumberRef
SecDbColumnCopyDouble(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
144 double number
= sqlite3_column_double(stmt
, col
);
145 return CFNumberCreate(allocator
, kCFNumberDoubleType
, &number
);
148 static CFNumberRef
SecDbColumnCopyNumber64(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
149 sqlite_int64 number
= sqlite3_column_int64(stmt
, col
);
150 return CFNumberCreate(allocator
, kCFNumberSInt64Type
, &number
);
153 static CFNumberRef
SecDbColumnCopyNumber(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
154 sqlite_int64 number
= sqlite3_column_int64(stmt
, col
);
155 if (INT32_MIN
<= number
&& number
<= INT32_MAX
) {
156 int32_t num32
= (int32_t)number
;
157 return CFNumberCreate(allocator
, kCFNumberSInt32Type
, &num32
);
159 return CFNumberCreate(allocator
, kCFNumberSInt64Type
, &number
);
163 static CFStringRef
SecDbColumnCopyString(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
164 const unsigned char *text
= sqlite3_column_text(stmt
, col
);
165 return CFStringCreateWithBytes(allocator
, text
, strlen((const char *)text
), kCFStringEncodingUTF8
, false);
168 // MARK: SecDbClass helpers
170 const SecDbAttr
*SecDbClassAttrWithKind(const SecDbClass
*class, SecDbAttrKind kind
, CFErrorRef
*error
) {
171 const SecDbAttr
*result
= NULL
;
172 SecDbForEachAttr(class, desc
) {
173 if (desc
->kind
== kind
)
178 SecError(errSecInternal
, error
, CFSTR("Can't find attribute of kind %d in class %@"), kind
, class->name
);
183 // MARK: SecDbAttr helpers
185 static bool SecDbIsTombstoneDbSelectAttr(const SecDbAttr
*attr
) {
186 return attr
->flags
& kSecDbPrimaryKeyFlag
|| attr
->kind
== kSecDbTombAttr
;
190 static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr
*attr
) {
191 return SecDbIsTombstoneDbSelectAttr(attr
) || attr
->kind
== kSecDbAccessAttr
|| attr
->kind
== kSecDbCreationDateAttr
|| attr
->kind
== kSecDbModificationDateAttr
;
195 static bool SecDbIsTombstoneDbUpdateAttr(const SecDbAttr
*attr
) {
196 return SecDbIsTombstoneDbSelectAttr(attr
) || attr
->kind
== kSecDbAccessAttr
|| attr
->kind
== kSecDbCreationDateAttr
|| attr
->kind
== kSecDbRowIdAttr
;
199 CFTypeRef
SecDbAttrCopyDefaultValue(const SecDbAttr
*attr
, CFErrorRef
*error
) {
200 CFTypeRef value
= NULL
;
201 switch (attr
->kind
) {
202 case kSecDbAccessAttr
:
203 case kSecDbStringAttr
:
204 case kSecDbAccessControlAttr
:
209 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
211 case kSecDbNumberAttr
:
216 value
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &zero
);
220 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
222 case kSecDbCreationDateAttr
:
223 case kSecDbModificationDateAttr
:
224 value
= CFDateCreate(kCFAllocatorDefault
, CFAbsoluteTimeGetCurrent());
227 SecError(errSecInternal
, error
, CFSTR("attr %@ has no default value"), attr
->name
);
234 static CFTypeRef
SecDbAttrCopyValueForDb(const SecDbAttr
*attr
, CFTypeRef value
, CFErrorRef
*error
) {
235 CFDataRef data
= NULL
;
236 CFTypeRef result
= NULL
;
241 if (CFEqual(value
, kCFNull
) && attr
->flags
& kSecDbPrimaryKeyFlag
) {
242 // SQLITE3 doesn't like NULL for primary key attributes, pretend kSecDbDefaultEmptyFlag was specified
243 require_quiet(result
= SecDbAttrCopyDefaultValue(attr
, error
), out
);
245 result
= CFRetain(value
);
248 if (attr
->flags
& kSecDbSHA1ValueInFlag
&& !CFEqual(result
, kCFNull
)) {
249 require_action_quiet(data
= copyData(result
), out
,
250 SecError(errSecInternal
, error
, CFSTR("failed to get attribute %@ data"), attr
->name
);
251 CFReleaseNull(result
));
252 CFAssignRetained(result
, CFDataCopySHA1Digest(data
, error
));
260 static CFStringRef
SecDbAttrGetHashName(const SecDbAttr
*attr
) {
261 if ((attr
->flags
& kSecDbSHA1ValueInFlag
) == 0) {
265 static dispatch_once_t once
;
266 static CFMutableDictionaryRef hash_store
;
267 static dispatch_queue_t queue
;
268 dispatch_once(&once
, ^{
269 queue
= dispatch_queue_create("secd-hash-name", NULL
);
270 hash_store
= CFDictionaryCreateMutableForCFTypes(NULL
);
273 __block CFStringRef name
;
274 dispatch_sync(queue
, ^{
275 name
= CFDictionaryGetValue(hash_store
, attr
->name
);
277 name
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("#%@"), attr
->name
);
278 CFDictionarySetValue(hash_store
, attr
->name
, name
);
287 CFTypeRef
SecDbItemGetCachedValueWithName(SecDbItemRef item
, CFStringRef name
) {
288 return CFDictionaryGetValue(item
->attributes
, name
);
291 static CFTypeRef
SecDbItemGetCachedValue(SecDbItemRef item
, const SecDbAttr
*desc
) {
292 return CFDictionaryGetValue(item
->attributes
, desc
->name
);
295 CFMutableDictionaryRef
SecDbItemCopyPListWithMask(SecDbItemRef item
, CFOptionFlags mask
, CFErrorRef
*error
) {
296 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
297 SecDbForEachAttrWithMask(item
->class, desc
, mask
) {
298 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
300 if (!CFEqual(kCFNull
, value
)) {
301 CFDictionarySetValue(dict
, desc
->name
, value
);
302 } else if (desc
->flags
& kSecDbNotNullFlag
) {
303 SecError(errSecDecode
, error
, CFSTR("attribute %@ has NULL value"), desc
->name
);
304 secerror("%@", error
? *error
: (CFErrorRef
)CFSTR("error == NULL"));
316 void SecDbItemSetCredHandle(SecDbItemRef item
, CFTypeRef cred_handle
) {
317 CFRetainAssign(item
->credHandle
, cred_handle
);
320 void SecDbItemSetCallerAccessGroups(SecDbItemRef item
, CFArrayRef caller_access_groups
) {
321 CFRetainAssign(item
->callerAccessGroups
, caller_access_groups
);
324 CFDataRef
SecDbItemCopyEncryptedDataToBackup(SecDbItemRef item
, uint64_t handle
, CFErrorRef
*error
) {
325 CFDataRef edata
= NULL
;
326 keybag_handle_t keybag
= (keybag_handle_t
)handle
;
327 CFMutableDictionaryRef attributes
= SecDbItemCopyPListWithMask(item
, kSecDbInCryptoDataFlag
, error
);
328 CFMutableDictionaryRef auth_attributes
= SecDbItemCopyPListWithMask(item
, kSecDbInAuthenticatedDataFlag
, error
);
329 if (attributes
|| auth_attributes
) {
330 SecAccessControlRef access_control
= SecDbItemCopyAccessControl(item
, error
);
331 if (access_control
) {
332 if (ks_encrypt_data(keybag
, access_control
, item
->credHandle
, attributes
, auth_attributes
, &edata
, error
)) {
333 item
->_edataState
= kSecDbItemEncrypting
;
335 seccritical("ks_encrypt_data (db): failed: %@", error
? *error
: (CFErrorRef
)CFSTR(""));
337 CFRelease(access_control
);
339 CFReleaseSafe(attributes
);
340 CFReleaseSafe(auth_attributes
);
345 bool SecDbItemEnsureDecrypted(SecDbItemRef item
, CFErrorRef
*error
) {
347 // If we haven't yet decrypted the item, make sure we do so now
349 if (item
->_edataState
== kSecDbItemEncrypted
) {
350 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, error
);
352 CFDataRef edata
= SecDbItemGetCachedValue(item
, attr
);
354 return SecError(errSecInternal
, error
, CFSTR("state= encrypted but edata is NULL"));
355 // Decrypt calls set value a bunch of times which clears our edata and changes our state.
356 item
->_edataState
= kSecDbItemDecrypting
;
357 result
= SecDbItemDecrypt(item
, edata
, error
);
359 item
->_edataState
= kSecDbItemClean
;
361 item
->_edataState
= kSecDbItemEncrypted
;
367 // Only called if cached value is not found.
368 static CFTypeRef
SecDbItemCopyValue(SecDbItemRef item
, const SecDbAttr
*attr
, CFErrorRef
*error
) {
370 return attr
->copyValue(item
, attr
, error
);
372 CFTypeRef value
= NULL
;
373 switch (attr
->kind
) {
374 // These have an explicit copyValue; here to shut up compiler
376 case kSecDbEncryptedDataAttr
:
377 case kSecDbPrimaryKeyAttr
:
380 case kSecDbAccessAttr
:
381 case kSecDbStringAttr
:
383 case kSecDbAccessControlAttr
:
384 if (attr
->flags
& kSecDbNotNullFlag
) {
385 if (attr
->flags
& kSecDbDefault0Flag
) {
388 } else if (attr
->kind
!= kSecDbBlobAttr
&& attr
->flags
& kSecDbDefaultEmptyFlag
) {
389 // blob drops through to data everything else is empty string
396 if (attr
->flags
& kSecDbNotNullFlag
&& attr
->flags
& kSecDbDefaultEmptyFlag
) {
397 value
= CFDataCreate(CFGetAllocator(item
), NULL
, 0);
402 case kSecDbNumberAttr
:
405 if (attr
->flags
& kSecDbNotNullFlag
) {
407 value
= CFNumberCreate(CFGetAllocator(item
), kCFNumberSInt32Type
, &zero
);
413 if (attr
->flags
& kSecDbNotNullFlag
&& attr
->flags
& kSecDbDefault0Flag
) {
414 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
419 case kSecDbRowIdAttr
:
420 if (attr
->flags
& kSecDbNotNullFlag
) {
425 case kSecDbCreationDateAttr
:
426 case kSecDbModificationDateAttr
:
427 value
= CFDateCreate(CFGetAllocator(item
), CFAbsoluteTimeGetCurrent());
429 case kSecDbUTombAttr
:
437 // SecDbItemGetValue will return kCFNull if there is no value for an attribute and this was not
438 // an error. It will return NULL and optionally set *error if there was an error computing an
439 // attribute, or if a required attribute was missing a value and had no known way to compute
441 CFTypeRef
SecDbItemGetValue(SecDbItemRef item
, const SecDbAttr
*desc
, CFErrorRef
*error
) {
442 // Propagate chained errors
446 if (desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
) {
447 if (!SecDbItemEnsureDecrypted(item
, error
))
451 CFTypeRef value
= SecDbItemGetCachedValue(item
, desc
);
453 value
= SecDbItemCopyValue(item
, desc
, error
);
455 if (CFEqual(kCFNull
, value
)) {
456 CFRelease(value
); // This is redundant but it shuts clang's static analyzer up.
459 SecDbItemSetValue(item
, desc
, value
, error
);
461 value
= SecDbItemGetCachedValue(item
, desc
);
468 // Similar as SecDbItemGetValue, but if attr represents attribute stored into DB field as hash, returns
469 // hashed value for the attribute.
470 static CFTypeRef
SecDbItemCopyValueForDb(SecDbItemRef item
, const SecDbAttr
*desc
, CFErrorRef
*error
) {
471 CFTypeRef value
= NULL
;
472 CFStringRef hash_name
= NULL
;
473 hash_name
= SecDbAttrGetHashName(desc
);
474 if ((desc
->flags
& (kSecDbSHA1ValueInFlag
| kSecDbInFlag
)) != 0) {
475 value
= CFRetainSafe(CFDictionaryGetValue(item
->attributes
, hash_name
));
479 require_quiet(value
= SecDbItemGetValue(item
, desc
, error
), out
);
480 require_action_quiet(value
= SecDbAttrCopyValueForDb(desc
, value
, error
), out
, CFReleaseNull(value
));
481 if ((desc
->flags
& kSecDbSHA1ValueInFlag
) != 0) {
482 CFDictionarySetValue(item
->attributes
, hash_name
, value
);
490 static bool SecDbItemGetBoolValue(SecDbItemRef item
, const SecDbAttr
*desc
, bool *bvalue
, CFErrorRef
*error
) {
491 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
495 *bvalue
= (isNumber(value
) && CFNumberGetValue(value
, kCFNumberCharType
, &cvalue
) && cvalue
== 1);
499 static CFStringRef
SecDbItemCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
501 if (isDictionary(formatOptions
) && CFDictionaryContainsKey(formatOptions
, kSecDebugFormatOption
)) {
502 SecDbItemRef item
= (SecDbItemRef
)cf
;
503 CFMutableStringRef mdesc
= CFStringCreateMutable(CFGetAllocator(cf
), 0);
504 CFStringAppendFormat(mdesc
, NULL
, CFSTR("<%@"), item
->class->name
);
505 SecDbForEachAttr(item
->class, attr
) {
506 CFTypeRef value
= SecDbItemGetValue(item
, attr
, NULL
);
508 CFStringAppend(mdesc
, CFSTR(","));
509 CFStringAppend(mdesc
, attr
->name
);
510 CFStringAppend(mdesc
, CFSTR("="));
511 if (CFEqual(CFSTR("data"), attr
->name
)) {
512 CFStringAppendEncryptedData(mdesc
, value
);
513 } else if (CFEqual(CFSTR("v_Data"), attr
->name
)) {
514 CFStringAppend(mdesc
, CFSTR("<?>"));
515 } else if (isData(value
)) {
516 CFStringAppendHexData(mdesc
, value
);
518 CFStringAppendFormat(mdesc
, 0, CFSTR("%@"), value
);
522 CFStringAppend(mdesc
, CFSTR(">"));
525 SecDbItemRef item
= (SecDbItemRef
)cf
;
526 const UInt8 zero4
[4] = {};
527 const UInt8
*pk
= &zero4
[0], *sha1
= &zero4
[0];
531 CFStringRef access
= NULL
;
532 uint8_t mdatbuf
[32] = {};
533 uint8_t *mdat
= &mdatbuf
[0];
534 CFMutableStringRef attrs
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
535 CFStringRef agrp
= NULL
;
536 CFBooleanRef utomb
= NULL
;
538 SecDbForEachAttr(item
->class, attr
) {
540 switch (attr
->kind
) {
543 case kSecDbStringAttr
:
544 case kSecDbNumberAttr
:
546 case kSecDbEncryptedDataAttr
:
547 if (attr
->flags
& (kSecDbReturnAttrFlag
| kSecDbReturnDataFlag
) && (value
= SecDbItemGetValue(item
, attr
, NULL
)) && !CFEqual(value
, kCFNull
)) {
548 if (isString(value
) && CFEqual(attr
->name
, kSecAttrAccessGroup
)) {
551 // We don't log these, just record that we saw the attribute.
552 CFStringAppend(attrs
, CFSTR(","));
553 CFStringAppend(attrs
, attr
->name
);
557 case kSecDbCreationDateAttr
:
558 // We don't care about this and every object has one.
560 case kSecDbModificationDateAttr
:
561 value
= SecDbItemGetValue(item
, attr
, NULL
);
563 mdat
= der_encode_generalizedtime_body(CFDateGetAbsoluteTime(value
), NULL
, mdat
, &mdatbuf
[31]);
566 value
= SecDbItemGetValue(item
, attr
, NULL
);
567 if (isData(value
) && CFDataGetLength(value
) >= (CFIndex
)sizeof(zero4
))
568 sha1
= CFDataGetBytePtr(value
);
570 case kSecDbRowIdAttr
:
571 value
= SecDbItemGetValue(item
, attr
, NULL
);
573 CFNumberGetValue(value
, kCFNumberSInt64Type
, &rowid
);
575 case kSecDbPrimaryKeyAttr
:
576 value
= SecDbItemGetValue(item
, attr
, NULL
);
578 pk
= CFDataGetBytePtr(value
);
581 value
= SecDbItemGetValue(item
, attr
, NULL
);
583 CFNumberGetValue(value
, kCFNumberCharType
, &sync
);
586 value
= SecDbItemGetValue(item
, attr
, NULL
);
588 CFNumberGetValue(value
, kCFNumberCharType
, &tomb
);
590 case kSecDbAccessAttr
:
591 value
= SecDbItemGetValue(item
, attr
, NULL
);
595 case kSecDbUTombAttr
:
596 value
= SecDbItemGetValue(item
, attr
, NULL
);
597 if (isBoolean(value
))
599 case kSecDbAccessControlAttr
:
600 /* TODO: Add formatting of ACLs. */
605 desc
= CFStringCreateWithFormat(CFGetAllocator(cf
), NULL
,
620 pk
[0], pk
[1], pk
[2], pk
[3],
627 utomb
? (CFEqual(utomb
, kCFBooleanFalse
) ? "F," : "T,") : "",
628 sha1
[0], sha1
[1], sha1
[2], sha1
[3]);
629 CFReleaseSafe(attrs
);
635 static void SecDbItemDestroy(CFTypeRef cf
) {
636 SecDbItemRef item
= (SecDbItemRef
)cf
;
637 CFReleaseSafe(item
->attributes
);
638 CFReleaseSafe(item
->credHandle
);
639 CFReleaseSafe(item
->callerAccessGroups
);
640 CFReleaseSafe(item
->cryptoOp
);
643 static CFHashCode
SecDbItemHash(CFTypeRef cf
) {
644 SecDbItemRef item
= (SecDbItemRef
)cf
;
645 CFDataRef digest
= SecDbItemGetSHA1(item
, NULL
);
647 const UInt8
*p
= CFDataGetBytePtr(digest
);
648 // Read first 8 bytes of digest in order
649 code
= p
[0] + ((p
[1] + ((p
[2] + ((p
[3] + ((p
[4] + ((p
[5] + ((p
[6] + (p
[7] << 8)) << 8)) << 8)) << 8)) << 8)) << 8)) << 8);
653 static Boolean
SecDbItemCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
654 SecDbItemRef item1
= (SecDbItemRef
)cf1
;
655 SecDbItemRef item2
= (SecDbItemRef
)cf2
;
656 CFDataRef digest1
= NULL
;
657 CFDataRef digest2
= NULL
;
659 digest1
= SecDbItemGetSHA1(item1
, NULL
);
661 digest2
= SecDbItemGetSHA1(item2
, NULL
);
662 Boolean equal
= CFEqual(digest1
, digest2
);
666 CFGiblisWithHashFor(SecDbItem
)
668 static SecDbItemRef
SecDbItemCreate(CFAllocatorRef allocator
, const SecDbClass
*class, keybag_handle_t keybag
) {
669 SecDbItemRef item
= CFTypeAllocate(SecDbItem
, struct SecDbItem
, allocator
);
671 item
->attributes
= CFDictionaryCreateMutableForCFTypes(allocator
);
672 item
->keybag
= keybag
;
673 item
->_edataState
= kSecDbItemDirty
;
674 item
->cryptoOp
= kAKSKeyOpDecrypt
;
678 const SecDbClass
*SecDbItemGetClass(SecDbItemRef item
) {
682 keybag_handle_t
SecDbItemGetKeybag(SecDbItemRef item
) {
686 bool SecDbItemSetKeybag(SecDbItemRef item
, keybag_handle_t keybag
, CFErrorRef
*error
) {
687 if (!SecDbItemEnsureDecrypted(item
, error
))
689 if (item
->keybag
!= keybag
) {
690 item
->keybag
= keybag
;
691 if (item
->_edataState
== kSecDbItemClean
) {
692 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, NULL
), kCFNull
, NULL
);
699 bool SecDbItemSetValue(SecDbItemRef item
, const SecDbAttr
*desc
, CFTypeRef value
, CFErrorRef
*error
) {
700 // Propagate chained errors.
708 return desc
->setValue(item
, desc
, value
, error
);
710 if (desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
)
711 if (!SecDbItemEnsureDecrypted(item
, error
))
714 bool changed
= false;
715 CFTypeRef attr
= NULL
;
716 switch (desc
->kind
) {
717 case kSecDbPrimaryKeyAttr
:
719 attr
= copyData(value
);
721 case kSecDbEncryptedDataAttr
:
722 attr
= copyData(value
);
724 if (item
->_edataState
== kSecDbItemEncrypting
)
725 item
->_edataState
= kSecDbItemClean
;
727 item
->_edataState
= kSecDbItemEncrypted
;
728 } else if (!value
|| CFEqual(kCFNull
, value
)) {
729 item
->_edataState
= kSecDbItemDirty
;
733 case kSecDbAccessControlAttr
:
734 attr
= copyBlob(value
);
737 case kSecDbCreationDateAttr
:
738 case kSecDbModificationDateAttr
:
739 attr
= copyDate(value
);
741 case kSecDbNumberAttr
:
744 case kSecDbRowIdAttr
:
745 attr
= copyNumber(value
);
747 case kSecDbAccessAttr
:
748 case kSecDbStringAttr
:
749 attr
= copyString(value
);
752 attr
= copySHA1(value
);
754 case kSecDbUTombAttr
:
755 attr
= CFRetainSafe(asBoolean(value
, NULL
));
759 CFTypeRef ovalue
= CFDictionaryGetValue(item
->attributes
, desc
->name
);
760 changed
= (!ovalue
|| !CFEqual(ovalue
, attr
));
761 CFDictionarySetValue(item
->attributes
, desc
->name
, attr
);
764 if (value
&& !CFEqual(kCFNull
, value
)) {
765 SecError(errSecItemInvalidValue
, error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
768 CFTypeRef ovalue
= CFDictionaryGetValue(item
->attributes
, desc
->name
);
769 changed
= (ovalue
&& !CFEqual(ovalue
, kCFNull
));
770 CFDictionaryRemoveValue(item
->attributes
, desc
->name
);
774 if (desc
->flags
& kSecDbInHashFlag
)
775 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
), kCFNull
, NULL
);
776 if (desc
->flags
& kSecDbPrimaryKeyFlag
)
777 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbPrimaryKeyAttr
, NULL
), kCFNull
, NULL
);
778 if ((desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
) && item
->_edataState
== kSecDbItemClean
)
779 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, NULL
), kCFNull
, NULL
);
780 if (desc
->flags
& kSecDbSHA1ValueInFlag
)
781 CFDictionaryRemoveValue(item
->attributes
, SecDbAttrGetHashName(desc
));
787 bool SecDbItemSetValues(SecDbItemRef item
, CFDictionaryRef values
, CFErrorRef
*error
) {
788 SecDbForEachAttr(item
->class, attr
) {
789 CFTypeRef value
= CFDictionaryGetValue(values
, attr
->name
);
790 if (value
&& !SecDbItemSetValue(item
, attr
, value
, error
))
796 bool SecDbItemSetValueWithName(SecDbItemRef item
, CFStringRef name
, CFTypeRef value
, CFErrorRef
*error
) {
797 SecDbForEachAttr(item
->class, attr
) {
798 if (CFEqual(attr
->name
, name
)) {
799 return SecDbItemSetValue(item
, attr
, value
, error
);
805 bool SecDbItemSetAccessControl(SecDbItemRef item
, SecAccessControlRef access_control
, CFErrorRef
*error
) {
807 if (item
->_edataState
== kSecDbItemClean
)
808 ok
= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, error
), kCFNull
, error
);
809 if (ok
&& access_control
) { //added check for access_control because ks_decrypt_data can leave NULL in access_control in case of error
810 item
->_edataState
= kSecDbItemDirty
;
811 CFDataRef data
= SecAccessControlCopyData(access_control
);
812 ok
= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessControlAttr
, error
), data
, error
);
818 SecDbItemRef
SecDbItemCreateWithAttributes(CFAllocatorRef allocator
, const SecDbClass
*class, CFDictionaryRef attributes
, keybag_handle_t keybag
, CFErrorRef
*error
) {
819 SecDbItemRef item
= SecDbItemCreate(kCFAllocatorDefault
, class, keybag
);
820 if (item
&& !SecDbItemSetValues(item
, attributes
, error
))
826 SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, const SecDbAttr
*attr
, int col
, CFErrorRef
*error
) {
827 CFTypeRef value
= NULL
;
828 switch (attr
->kind
) {
830 case kSecDbCreationDateAttr
:
831 case kSecDbModificationDateAttr
:
832 value
= SecDbColumnCopyDate(allocator
, stmt
, col
, error
);
835 case kSecDbNumberAttr
:
836 switch (sqlite3_column_type(stmt
, col
)) {
838 value
= SecDbColumnCopyNumber(allocator
, stmt
, col
, error
);
841 value
= SecDbColumnCopyDouble(allocator
, stmt
, col
, error
);
844 value
= SecDbColumnCopyString(allocator
, stmt
, col
, error
);
847 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
854 case kSecDbAccessAttr
:
855 case kSecDbStringAttr
:
856 value
= SecDbColumnCopyString(allocator
, stmt
, col
, error
);
860 case kSecDbPrimaryKeyAttr
:
861 case kSecDbEncryptedDataAttr
:
862 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
866 value
= SecDbColumnCopyNumber(allocator
, stmt
, col
, error
);
868 case kSecDbRowIdAttr
:
869 value
= SecDbColumnCopyNumber64(allocator
, stmt
, col
, error
);
871 case kSecDbAccessControlAttr
:
872 case kSecDbUTombAttr
:
873 /* This attributes does not have any database column associated, exists only inside encrypted blob as metadata. */
879 SecDbItemRef
SecDbItemCreateWithStatement(CFAllocatorRef allocator
, const SecDbClass
*class, sqlite3_stmt
*stmt
, keybag_handle_t keybag
, CFErrorRef
*error
, bool (^return_attr
)(const SecDbAttr
*attr
)) {
880 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
882 SecDbForEachAttr(class, attr
) {
883 if (return_attr(attr
)) {
884 CFTypeRef value
= SecDbColumnCopyValueWithAttr(allocator
, stmt
, attr
, col
++, error
);
886 CFDictionarySetValue(item
->attributes
, SecDbAttrGetHashName(attr
), value
);
891 const SecDbAttr
*data_attr
= SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr
, error
);
892 if (data_attr
!= NULL
&& CFDictionaryGetValue(item
->attributes
, data_attr
->name
) != NULL
) {
893 item
->_edataState
= kSecDbItemEncrypted
;
900 SecDbItemRef
SecDbItemCreateWithEncryptedData(CFAllocatorRef allocator
, const SecDbClass
*class,
901 CFDataRef edata
, keybag_handle_t keybag
, CFErrorRef
*error
) {
902 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
903 const SecDbAttr
*edata_attr
= SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr
, error
);
905 if (!SecDbItemSetValue(item
, edata_attr
, edata
, error
))
911 // TODO: Hack -- Replace with real filtering
913 // Return true iff an item for which SecDbItemIsSyncable() already returns true should be part of the v2 view.
914 bool SecDbItemInV2(SecDbItemRef item
) {
915 const SecDbClass
*iclass
= SecDbItemGetClass(item
);
916 return (SecDbItemGetCachedValueWithName(item
, kSecAttrSyncViewHint
) == NULL
&&
917 (iclass
== &genp_class
|| iclass
== &inet_class
|| iclass
== &keys_class
|| iclass
== &cert_class
));
920 // Return true iff an item for which SecDbItemIsSyncable() and SecDbItemInV2() already return true should be part of the v0 view.
921 bool SecDbItemInV2AlsoInV0(SecDbItemRef item
) {
922 return (SecDbItemGetCachedValueWithName(item
, kSecAttrTokenID
) == NULL
&& SecDbItemGetClass(item
) != &cert_class
);
925 SecDbItemRef
SecDbItemCopyWithUpdates(SecDbItemRef item
, CFDictionaryRef updates
, CFErrorRef
*error
) {
926 SecDbItemRef new_item
= SecDbItemCreate(CFGetAllocator(item
), item
->class, item
->keybag
);
927 SecDbItemSetCredHandle(new_item
, item
->credHandle
);
928 SecDbForEachAttr(item
->class, attr
) {
929 // Copy each attribute, except the mod date attribute (it will be reset to now when needed),
930 // from the updates dict unless it's not there in which case we copy the attribute from the passed in item.
931 if (attr
->kind
!= kSecDbModificationDateAttr
&& attr
->kind
!= kSecDbEncryptedDataAttr
&& attr
->kind
!= kSecDbSHA1Attr
&& attr
->kind
!= kSecDbPrimaryKeyAttr
) {
932 CFTypeRef value
= NULL
;
933 if (CFDictionaryGetValueIfPresent(updates
, attr
->name
, &value
)) {
935 SecError(errSecParam
, error
, CFSTR("NULL value in dictionary"));
937 value
= SecDbItemGetValue(item
, attr
, error
);
939 if (!value
|| !SecDbItemSetValue(new_item
, attr
, value
, error
)) {
940 CFReleaseNull(new_item
);
948 // Ensure that the date value of attr of new_item is greater than that of old_item.
949 static bool SecDbItemMakeAttrYounger(SecDbItemRef new_item
, SecDbItemRef old_item
, const SecDbAttr
*attr
, CFErrorRef
*error
) {
950 CFDateRef old_date
= SecDbItemGetValue(old_item
, attr
, error
);
953 CFDateRef new_date
= SecDbItemGetValue(new_item
, attr
, error
);
957 if (CFDateCompare(new_date
, old_date
, NULL
) != kCFCompareGreaterThan
) {
958 CFDateRef adjusted_date
= CFDateCreate(kCFAllocatorDefault
, CFDateGetAbsoluteTime(old_date
) + 0.001);
960 ok
= SecDbItemSetValue(new_item
, attr
, adjusted_date
, error
);
961 CFRelease(adjusted_date
);
967 // Ensure that the mod date of new_item is greater than that of old_item.
968 static bool SecDbItemMakeYounger(SecDbItemRef new_item
, SecDbItemRef old_item
, CFErrorRef
*error
) {
969 const SecDbAttr
*attr
= SecDbClassAttrWithKind(new_item
->class, kSecDbModificationDateAttr
, error
);
970 return attr
&& SecDbItemMakeAttrYounger(new_item
, old_item
, attr
, error
);
973 static SecDbItemRef
SecDbItemCopyTombstone(SecDbItemRef item
, CFBooleanRef makeTombStone
, CFErrorRef
*error
) {
974 SecDbItemRef new_item
= SecDbItemCreate(CFGetAllocator(item
), item
->class, item
->keybag
);
975 SecDbForEachAttr(item
->class, attr
) {
976 if (attr
->kind
== kSecDbTombAttr
) {
977 // Set the tomb attr to true to indicate a tombstone.
978 if (!SecDbItemSetValue(new_item
, attr
, kCFBooleanTrue
, error
)) {
979 CFReleaseNull(new_item
);
982 } else if (SecDbIsTombstoneDbUpdateAttr(attr
)) {
983 // Copy all primary key attributes and creation timestamps from the original item.
984 CFTypeRef value
= SecDbItemGetValue(item
, attr
, error
);
985 if (!value
|| (!CFEqual(kCFNull
, value
) && !SecDbItemSetValue(new_item
, attr
, value
, error
))) {
986 CFReleaseNull(new_item
);
989 } else if (attr
->kind
== kSecDbModificationDateAttr
) {
990 if (!SecDbItemMakeAttrYounger(new_item
, item
, attr
, error
)) {
991 CFReleaseNull(new_item
);
994 } else if (makeTombStone
&& attr
->kind
== kSecDbUTombAttr
) {
996 SecDbItemSetValue(new_item
, attr
, makeTombStone
, error
);
1004 // MARK: SQL Construction helpers -- These should become private in the future
1006 void SecDbAppendElement(CFMutableStringRef sql
, CFStringRef value
, bool *needComma
) {
1009 CFStringAppend(sql
, CFSTR(","));
1013 CFStringAppend(sql
, value
);
1016 static void SecDbAppendElementEquals(CFMutableStringRef sql
, CFStringRef value
, bool *needComma
) {
1017 SecDbAppendElement(sql
, value
, needComma
);
1018 CFStringAppend(sql
, CFSTR("=?"));
1021 /* Append AND is needWhere is NULL or *needWhere is false. Append WHERE
1022 otherwise. Upon return *needWhere will be false. */
1024 SecDbAppendWhereOrAnd(CFMutableStringRef sql
, bool *needWhere
) {
1025 if (!needWhere
|| !*needWhere
) {
1026 CFStringAppend(sql
, CFSTR(" AND "));
1028 CFStringAppend(sql
, CFSTR(" WHERE "));
1034 SecDbAppendWhereOrAndEquals(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
) {
1035 SecDbAppendWhereOrAnd(sql
, needWhere
);
1036 CFStringAppend(sql
, col
);
1037 CFStringAppend(sql
, CFSTR("=?"));
1041 SecDbAppendWhereOrAndNotEquals(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
) {
1042 SecDbAppendWhereOrAnd(sql
, needWhere
);
1043 CFStringAppend(sql
, col
);
1044 CFStringAppend(sql
, CFSTR("!=?"));
1047 static void SecDbAppendCountArgsAndCloseParen(CFMutableStringRef sql
, CFIndex count
) {
1048 bool needComma
= false;
1050 SecDbAppendElement(sql
, CFSTR("?"), &needComma
);
1051 CFStringAppend(sql
, CFSTR(")"));
1055 SecDbAppendWhereOrAndIn(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
, CFIndex count
) {
1057 return SecDbAppendWhereOrAndEquals(sql
, col
, needWhere
);
1058 SecDbAppendWhereOrAnd(sql
, needWhere
);
1059 CFStringAppend(sql
, col
);
1060 CFStringAppend(sql
, CFSTR(" in ("));
1061 SecDbAppendCountArgsAndCloseParen(sql
, count
);
1065 SecDbAppendWhereOrAndNotIn(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
, CFIndex count
) {
1067 return SecDbAppendWhereOrAndNotEquals(sql
, col
, needWhere
);
1068 SecDbAppendWhereOrAnd(sql
, needWhere
);
1069 CFStringAppend(sql
, col
);
1070 CFStringAppend(sql
, CFSTR(" not in ("));
1071 SecDbAppendCountArgsAndCloseParen(sql
, count
);
1074 static CFStringRef
SecDbItemCopyInsertSQL(SecDbItemRef item
, bool(^use_attr
)(const SecDbAttr
*attr
)) {
1075 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(item
), 0);
1076 CFStringAppend(sql
, CFSTR("INSERT INTO "));
1077 CFStringAppend(sql
, item
->class->name
);
1078 CFStringAppend(sql
, CFSTR("("));
1079 bool needComma
= false;
1080 CFIndex used_attr
= 0;
1081 SecDbForEachAttr(item
->class, attr
) {
1082 if (use_attr(attr
)) {
1084 SecDbAppendElement(sql
, attr
->name
, &needComma
);
1087 CFStringAppend(sql
, CFSTR(")VALUES(?"));
1088 while (used_attr
-- > 1) {
1089 CFStringAppend(sql
, CFSTR(",?"));
1091 CFStringAppend(sql
, CFSTR(")"));
1096 static bool SecDbItemInsertBind(SecDbItemRef item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr
)(const SecDbAttr
*attr
)) {
1099 SecDbForEachAttr(item
->class, attr
) {
1100 if (use_attr(attr
)) {
1101 CFTypeRef value
= SecDbItemCopyValueForDb(item
, attr
, error
);
1102 ok
= value
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1103 CFReleaseSafe(value
);
1111 sqlite3_int64
SecDbItemGetRowId(SecDbItemRef item
, CFErrorRef
*error
) {
1112 sqlite3_int64 row_id
= 0;
1113 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1115 CFNumberRef number
= SecDbItemGetValue(item
, attr
, error
);
1116 if (!isNumber(number
)|| !CFNumberGetValue(number
, kCFNumberSInt64Type
, &row_id
))
1117 SecDbError(SQLITE_ERROR
, error
, CFSTR("rowid %@ is not a 64 bit number"), number
);
1123 static CFNumberRef
SecDbItemCreateRowId(SecDbItemRef item
, sqlite3_int64 rowid
, CFErrorRef
*error
) {
1124 return CFNumberCreate(CFGetAllocator(item
), kCFNumberSInt64Type
, &rowid
);
1127 bool SecDbItemSetRowId(SecDbItemRef item
, sqlite3_int64 rowid
, CFErrorRef
*error
) {
1129 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1131 CFNumberRef value
= SecDbItemCreateRowId(item
, rowid
, error
);
1135 ok
= SecDbItemSetValue(item
, attr
, value
, error
);
1141 static bool SecDbItemClearRowId(SecDbItemRef item
, CFErrorRef
*error
) {
1143 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1145 CFDictionaryRemoveValue(item
->attributes
, attr
->name
);
1146 //ok = SecDbItemSetValue(item, attr, kCFNull, error);
1151 static bool SecDbItemSetLastInsertRowId(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1152 sqlite3_int64 rowid
= sqlite3_last_insert_rowid(SecDbHandle(dbconn
));
1153 return SecDbItemSetRowId(item
, rowid
, error
);
1156 bool SecDbItemIsSyncableOrCorrupted(SecDbItemRef item
) {
1157 bool is_syncable_or_corrupted
= false;
1158 CFErrorRef localError
= NULL
;
1159 if (!SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, &localError
),
1160 &is_syncable_or_corrupted
, &localError
)) {
1161 is_syncable_or_corrupted
= SecErrorGetOSStatus(localError
) == errSecDecode
;
1163 CFReleaseSafe(localError
);
1164 return is_syncable_or_corrupted
;
1167 bool SecDbItemIsSyncable(SecDbItemRef item
) {
1169 if (SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, NULL
), &is_syncable
, NULL
))
1174 bool SecDbItemSetSyncable(SecDbItemRef item
, bool sync
, CFErrorRef
*error
)
1176 return SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, error
), sync
? kCFBooleanTrue
: kCFBooleanFalse
, error
);
1179 bool SecDbItemIsTombstone(SecDbItemRef item
) {
1181 if (SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbTombAttr
, NULL
), &is_tomb
, NULL
))
1186 CFDataRef
SecDbItemGetPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
1187 return SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbPrimaryKeyAttr
, error
), error
);
1190 CFDataRef
SecDbItemGetSHA1(SecDbItemRef item
, CFErrorRef
*error
) {
1191 return SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, error
), error
);
1194 static SecDbQueryRef
SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
1195 CFMutableDictionaryRef dict
= SecDbItemCopyPListWithMask(item
, kSecDbPrimaryKeyFlag
, error
);
1199 SecDbQueryRef query
= query_create(item
->class, NULL
, error
);
1201 CFReleaseSafe(query
->q_item
);
1202 query
->q_item
= dict
;
1210 static bool SecDbItemIsCorrupt(SecDbItemRef item
, bool *is_corrupt
, CFErrorRef
*error
) {
1211 CFErrorRef localError
= NULL
;
1212 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1213 const struct SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, &localError
);
1214 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, &localError
));
1217 if (localError
|| !SecDbItemEnsureDecrypted(item
, &localError
)) {
1218 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1219 // We failed to decrypt the item
1220 const SecDbAttr
*desc
= SecDbClassAttrWithKind(item
->class, kSecDbAccessControlAttr
, &localError
);
1221 SecAccessControlRef accc
= NULL
;
1222 CFDataRef acccData
= NULL
;
1224 acccData
= (CFDataRef
)SecDbItemGetValue(item
, desc
, &localError
);
1225 if (isData(acccData
)) {
1226 accc
= SecAccessControlCreateFromData(CFGetAllocator(item
), acccData
, &localError
);
1229 if (accc
&& CFEqualSafe(SecAccessControlGetProtection(accc
), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)) {
1231 secwarning("cannot decrypt item %@, item is irrecoverably lost with older passcode (error %@)", item
, localError
);
1233 secerror("error %@ reading item %@ (corrupted)", localError
, item
);
1234 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem
);
1236 CFReleaseNull(localError
);
1241 // Recompute sha1 hash attribute and compare with the cached one.
1242 CFDataRef computedSHA1
= SecDbItemCopyValue(item
, sha1attr
, &localError
);
1243 if (storedSHA1
&& computedSHA1
&& !CFEqual(storedSHA1
, computedSHA1
)) {
1244 CFStringRef storedHex
= CFDataCopyHexString(storedSHA1
), computedHex
= CFDataCopyHexString(computedSHA1
);
1245 secerror("error %@ %@ != %@ item %@ (corrupted)", sha1attr
->name
, storedHex
, computedHex
, item
);
1246 __security_simulatecrash(CFSTR("Corrupted item (sha1 mismatch) found in keychain"), __sec_exception_code_CorruptItem
);
1247 CFReleaseSafe(storedHex
);
1248 CFReleaseSafe(computedHex
);
1252 // Sanity check that all attributes that must not be NULL actually aren't
1253 if (!localError
) SecDbForEachAttr(item
->class, attr
) {
1254 if (attr
->flags
& (kSecDbInCryptoDataFlag
| kSecDbInAuthenticatedDataFlag
)) {
1255 CFTypeRef value
= SecDbItemGetValue(item
, attr
, &localError
);
1257 if (CFEqual(kCFNull
, value
) && attr
->flags
& kSecDbNotNullFlag
) {
1258 secerror("error attribute %@ has NULL value in item %@ (corrupted)", attr
->name
, item
);
1259 __security_simulatecrash(CFSTR("Corrupted item (attr NULL) found in keychain"), __sec_exception_code_CorruptItem
);
1264 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1265 // We failed to decrypt the item
1267 secwarning("attribute %@: %@ item %@ (item lost with older passcode)", attr
->name
, localError
, item
);
1269 secerror("error attribute %@: %@ item %@ (corrupted)", attr
->name
, localError
, item
);
1270 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem
);
1273 CFReleaseNull(localError
);
1280 CFReleaseSafe(computedSHA1
);
1281 CFReleaseSafe(storedSHA1
);
1282 return SecErrorPropagate(localError
, error
);
1285 static void SecDbItemRecordUpdate(SecDbConnectionRef dbconn
, SecDbItemRef deleted
, SecDbItemRef inserted
) {
1286 SecDbRecordChange(dbconn
, deleted
, inserted
);
1289 static bool SecDbItemDoInsert(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1290 bool (^use_attr
)(const SecDbAttr
*attr
) = ^bool(const SecDbAttr
*attr
) {
1291 return (attr
->flags
& kSecDbInFlag
);
1293 CFStringRef sql
= SecDbItemCopyInsertSQL(item
, use_attr
);
1294 __block
bool ok
= sql
;
1296 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1297 ok
= (SecDbItemInsertBind(item
, stmt
, error
, use_attr
) &&
1298 SecDbStep(dbconn
, stmt
, error
, NULL
) &&
1299 SecDbItemSetLastInsertRowId(item
, dbconn
, error
));
1304 secnotice("item", "inserted %@", item
);
1305 SecDbItemRecordUpdate(dbconn
, NULL
, item
);
1311 bool SecDbItemInsertOrReplace(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, void(^duplicate
)(SecDbItemRef item
, SecDbItemRef
*replace
)) {
1312 __block CFErrorRef localError
= NULL
;
1313 __block
bool ok
= SecDbItemDoInsert(item
, dbconn
, &localError
);
1314 if (!ok
&& localError
&& CFErrorGetCode(localError
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(localError
))) {
1315 SecDbQueryRef query
= SecDbQueryCreateWithItemPrimaryKey(item
, error
);
1317 CFRetainAssign(query
->q_use_cred_handle
, item
->credHandle
);
1318 SecDbItemSelect(query
, dbconn
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
1319 return attr
->flags
& kSecDbPrimaryKeyFlag
;
1320 }, NULL
, NULL
, ^(SecDbItemRef old_item
, bool *stop
) {
1321 bool is_corrupt
= false;
1322 ok
= SecDbItemIsCorrupt(old_item
, &is_corrupt
, error
);
1323 SecDbItemRef replace
= NULL
;
1325 // If old_item is corrupted pretend it's not there and just replace it.
1329 CFReleaseNull(*error
); //item is corrupted and will be replaced, so drop the error
1330 } else if (ok
&& duplicate
) {
1331 duplicate(old_item
, &replace
);
1334 const SecDbAttr
*rowid_attr
= SecDbClassAttrWithKind(old_item
->class, kSecDbRowIdAttr
, error
);
1335 CFNumberRef oldrowid
= SecDbItemGetCachedValue(old_item
, rowid_attr
);
1337 ok
= SecDbItemSetValue(replace
, rowid_attr
, oldrowid
, &localError
);
1338 if (ok
&& !is_corrupt
) {
1339 ok
= SecDbItemMakeYounger(replace
, old_item
, error
);
1341 ok
= ok
&& SecDbItemDoUpdate(old_item
, replace
, dbconn
, &localError
, ^bool (const SecDbAttr
*attr
) {
1342 return attr
->kind
== kSecDbRowIdAttr
;
1345 ok
= SecError(errSecInternal
, &localError
, CFSTR("no rowid for %@"), old_item
);
1349 CFReleaseNull(localError
); // Clear the error, since we replaced the item.
1352 SecDbItemSetCredHandle(item
, query
->q_use_cred_handle
);
1353 ok
&= query_destroy(query
, error
);
1357 return ok
& SecErrorPropagate(localError
, error
); // Don't use && here!
1360 bool SecDbItemInsert(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1361 return SecDbItemInsertOrReplace(item
, dbconn
, error
, ^(SecDbItemRef old_item
, SecDbItemRef
*replace
) {
1362 if (SecDbItemIsTombstone(old_item
)) {
1369 static CFStringRef
SecDbItemCopyUpdateSQL(SecDbItemRef old_item
, SecDbItemRef new_item
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1370 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(new_item
), 0);
1371 CFStringAppend(sql
, CFSTR("UPDATE "));
1372 CFStringAppend(sql
, new_item
->class->name
);
1373 CFStringAppend(sql
, CFSTR(" SET "));
1374 bool needComma
= false;
1375 CFIndex used_attr
= 0;
1376 SecDbForEachAttrWithMask(new_item
->class, attr
, kSecDbInFlag
) {
1378 SecDbAppendElementEquals(sql
, attr
->name
, &needComma
);
1381 bool needWhere
= true;
1382 SecDbForEachAttr(old_item
->class, attr
) {
1383 if (use_attr_in_where(attr
)) {
1384 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1391 static bool SecDbItemUpdateBind(SecDbItemRef old_item
, SecDbItemRef new_item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1394 SecDbForEachAttrWithMask(new_item
->class, attr
, kSecDbInFlag
) {
1395 CFTypeRef value
= SecDbItemCopyValueForDb(new_item
, attr
, error
);
1396 ok
&= value
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1397 CFReleaseSafe(value
);
1401 SecDbForEachAttr(old_item
->class, attr
) {
1402 if (use_attr_in_where(attr
)) {
1403 CFTypeRef value
= SecDbItemCopyValueForDb(old_item
, attr
, error
);
1404 ok
&= value
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1405 CFReleaseSafe(value
);
1413 // Primary keys are the same -- do an update
1414 bool SecDbItemDoUpdate(SecDbItemRef old_item
, SecDbItemRef new_item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1415 CFStringRef sql
= SecDbItemCopyUpdateSQL(old_item
, new_item
, use_attr_in_where
);
1416 __block
bool ok
= sql
;
1418 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1419 ok
= SecDbItemUpdateBind(old_item
, new_item
, stmt
, error
, use_attr_in_where
) && SecDbStep(dbconn
, stmt
, error
, NULL
);
1424 secnotice("item", "replaced %@ with %@ in %@", old_item
, new_item
, dbconn
);
1425 SecDbItemRecordUpdate(dbconn
, old_item
, new_item
);
1430 static CFStringRef
SecDbItemCopyDeleteSQL(SecDbItemRef item
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1431 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(item
), 0);
1432 CFStringAppend(sql
, CFSTR("DELETE FROM "));
1433 CFStringAppend(sql
, item
->class->name
);
1434 bool needWhere
= true;
1435 SecDbForEachAttr(item
->class, attr
) {
1436 if (use_attr_in_where(attr
)) {
1437 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1444 static bool SecDbItemDeleteBind(SecDbItemRef item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1447 SecDbForEachAttr(item
->class, attr
) {
1448 if (use_attr_in_where(attr
)) {
1449 CFTypeRef value
= SecDbItemCopyValueForDb(item
, attr
, error
);
1450 ok
&= value
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1451 CFReleaseSafe(value
);
1459 static bool SecDbItemDoDeleteOnly(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1460 CFStringRef sql
= SecDbItemCopyDeleteSQL(item
, use_attr_in_where
);
1461 __block
bool ok
= sql
;
1463 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1464 ok
= SecDbItemDeleteBind(item
, stmt
, error
, use_attr_in_where
) && SecDbStep(dbconn
, stmt
, error
, NULL
);
1471 bool SecDbItemDoDeleteSilently(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1472 return SecDbItemDoDeleteOnly(item
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1473 return attr
->kind
== kSecDbRowIdAttr
;
1477 static bool SecDbItemDoDelete(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1478 bool ok
= SecDbItemDoDeleteOnly(item
, dbconn
, error
, use_attr_in_where
);
1480 secnotice("item", "deleted %@ from %@", item
, dbconn
);
1481 SecDbItemRecordUpdate(dbconn
, item
, NULL
);
1487 static bool SecDbItemDeleteTombstone(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1489 // TODO: Treat non decryptable items like tombstones here too and delete them
1490 SecDbItemRef tombstone
= SecDbItemCopyTombstone(item
, error
);
1493 ok
= SecDbItemClearRowId(tombstone
, error
);
1495 ok
= SecDbItemDoDelete(tombstone
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1496 return SecDbIsTombstoneDbSelectAttr(attr
);
1499 CFRelease(tombstone
);
1505 // Replace old_item with new_item. If primary keys are the same this does an update otherwise it does a delete + add
1506 bool SecDbItemUpdate(SecDbItemRef old_item
, SecDbItemRef new_item
, SecDbConnectionRef dbconn
, CFBooleanRef makeTombstone
, CFErrorRef
*error
) {
1507 __block
bool ok
= true;
1508 __block CFErrorRef localError
= NULL
;
1510 CFDataRef old_pk
= SecDbItemGetPrimaryKey(old_item
, error
);
1511 CFDataRef new_pk
= SecDbItemGetPrimaryKey(new_item
, error
);
1513 ok
= old_pk
&& new_pk
;
1515 bool pk_equal
= ok
&& CFEqual(old_pk
, new_pk
);
1517 ok
= SecDbItemMakeYounger(new_item
, old_item
, error
);
1519 ok
= ok
&& SecDbItemDoUpdate(old_item
, new_item
, dbconn
, &localError
, ^bool(const SecDbAttr
*attr
) {
1520 return attr
->kind
== kSecDbRowIdAttr
;
1524 if(CFErrorGetCode(localError
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(localError
))) {
1525 /* Update failed because we changed the PrimaryKey and there was a dup.
1526 Find the dup and see if it is a tombstone or corrupted item. */
1527 SecDbQueryRef query
= SecDbQueryCreateWithItemPrimaryKey(new_item
, error
);
1530 ok
&= SecDbItemSelect(query
, dbconn
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
1531 return attr
->flags
& kSecDbPrimaryKeyFlag
;
1532 }, NULL
, NULL
, ^(SecDbItemRef duplicate_item
, bool *stop
) {
1533 bool is_corrupt
= false;
1534 bool is_tomb
= false;
1535 ok
= SecDbItemIsCorrupt(duplicate_item
, &is_corrupt
, error
);
1536 if (ok
&& !is_corrupt
) {
1537 if ((is_tomb
= SecDbItemIsTombstone(duplicate_item
)))
1538 ok
= SecDbItemMakeYounger(new_item
, duplicate_item
, error
);
1540 if (ok
&& (is_corrupt
|| is_tomb
)) {
1541 ok
= SecDbItemDoDelete(old_item
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1542 return attr
->kind
== kSecDbRowIdAttr
;
1544 ok
= ok
&& SecDbItemDoUpdate(duplicate_item
, new_item
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1545 return attr
->kind
== kSecDbRowIdAttr
;
1547 CFReleaseNull(localError
);
1550 ok
&= query_destroy(query
, error
);
1556 if (error
&& *error
== NULL
) {
1557 *error
= localError
;
1560 CFReleaseSafe(localError
);
1564 if (ok
&& !pk_equal
&& !CFEqualSafe(makeTombstone
, kCFBooleanFalse
)) {
1565 /* The primary key of new_item is different than that of old_item, we
1566 have been asked to make a tombstone so leave one for the old_item. */
1567 SecDbItemRef tombstone
= SecDbItemCopyTombstone(old_item
, makeTombstone
, error
);
1570 ok
= (SecDbItemClearRowId(tombstone
, error
) &&
1571 SecDbItemDoInsert(tombstone
, dbconn
, error
));
1572 CFRelease(tombstone
);
1579 // Replace the object with a tombstone
1580 bool SecDbItemDelete(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFBooleanRef makeTombstone
, CFErrorRef
*error
) {
1582 if (!CFEqualSafe(makeTombstone
, kCFBooleanFalse
)) {
1583 SecDbItemRef tombstone
= SecDbItemCopyTombstone(item
, makeTombstone
, error
);
1585 ok
= SecDbItemDoUpdate(item
, tombstone
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1586 return attr
->kind
== kSecDbRowIdAttr
;
1588 CFRelease(tombstone
);
1591 ok
= SecDbItemDoDelete(item
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1592 return attr
->kind
== kSecDbRowIdAttr
;
1598 CFStringRef
SecDbItemCopySelectSQL(SecDbQueryRef query
,
1599 bool (^return_attr
)(const SecDbAttr
*attr
),
1600 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1601 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
)) {
1602 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1603 CFStringAppend(sql
, CFSTR("SELECT "));
1604 // What are we selecting?
1605 bool needComma
= false;
1606 SecDbForEachAttr(query
->q_class
, attr
) {
1607 if (return_attr(attr
))
1608 SecDbAppendElement(sql
, attr
->name
, &needComma
);
1611 // From which table?
1612 CFStringAppend(sql
, CFSTR(" FROM "));
1613 CFStringAppend(sql
, query
->q_class
->name
);
1615 // And which elements do we want to select
1616 bool needWhere
= true;
1617 SecDbForEachAttr(query
->q_class
, attr
) {
1618 if (use_attr_in_where(attr
)) {
1619 CFTypeRef value
= CFDictionaryGetValue(query
->q_item
, attr
->name
);
1620 if (isArray(value
)) {
1621 CFArrayRef array
= (CFArrayRef
)value
;
1622 CFIndex length
= CFArrayGetCount(array
);
1624 CFTypeRef head
= CFArrayGetValueAtIndex(array
, 0);
1625 if (CFEqualSafe(head
, kCFNull
)) {
1626 SecDbAppendWhereOrAndNotIn(sql
, attr
->name
, &needWhere
, length
- 1);
1628 SecDbAppendWhereOrAndIn(sql
, attr
->name
, &needWhere
, length
);
1632 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1636 // Append SQL for access groups and limits.
1638 add_where_sql(sql
, &needWhere
);
1643 static bool SecDbItemSelectBindValue(SecDbQueryRef query
, sqlite3_stmt
*stmt
, int param
, const SecDbAttr
*attr
, CFTypeRef inValue
, CFErrorRef
*error
) {
1645 CFTypeRef value
= NULL
;
1646 if (attr
->kind
== kSecDbRowIdAttr
) {
1647 // TODO: Ignores inValue and uses rowid directly instead HACK should go
1648 value
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &query
->q_row_id
);
1650 value
= SecDbAttrCopyValueForDb(attr
, inValue
, error
);
1652 ok
= ok
&& value
!= NULL
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1653 CFReleaseSafe(value
);
1657 bool SecDbItemSelectBind(SecDbQueryRef query
, sqlite3_stmt
*stmt
, CFErrorRef
*error
,
1658 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1659 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
)) {
1660 __block
bool ok
= true;
1661 __block
int param
= 0;
1662 SecDbForEachAttr(query
->q_class
, attr
) {
1663 if (use_attr_in_where(attr
)) {
1664 CFTypeRef value
= CFDictionaryGetValue(query
->q_item
, attr
->name
);
1665 if (isArray(value
)) {
1666 CFArrayRef array
= (CFArrayRef
)value
;
1667 CFRange range
= {.location
= 0, .length
= CFArrayGetCount(array
) };
1668 if (range
.length
> 0) {
1669 CFTypeRef head
= CFArrayGetValueAtIndex(array
, 0);
1670 if (CFEqualSafe(head
, kCFNull
)) {
1675 CFArrayApplyFunction(array
, range
, apply_block_1
, (void (^)(const void *value
)) ^(const void *value
) {
1676 ok
= SecDbItemSelectBindValue(query
, stmt
, param
++, attr
, value
, error
);
1679 ok
= SecDbItemSelectBindValue(query
, stmt
, param
++, attr
, value
, error
);
1686 // TODO: Bind arguments for access groups and limits.
1687 if (bind_added_where
)
1688 bind_added_where(stmt
, ++param
);
1693 bool SecDbItemSelect(SecDbQueryRef query
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
1694 bool (^return_attr
)(const SecDbAttr
*attr
),
1695 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1696 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
),
1697 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
),
1698 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
1699 __block
bool ok
= true;
1700 if (return_attr
== NULL
) {
1701 return_attr
= ^bool (const SecDbAttr
* attr
) {
1702 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
|| attr
->kind
== kSecDbSHA1Attr
;
1706 CFStringRef sql
= SecDbItemCopySelectSQL(query
, return_attr
, use_attr_in_where
, add_where_sql
);
1708 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1709 ok
= (SecDbItemSelectBind(query
, stmt
, error
, use_attr_in_where
, bind_added_where
) &&
1710 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
1711 SecDbItemRef item
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
1713 CFRetainAssign(item
->credHandle
, query
->q_use_cred_handle
);
1714 handle_row(item
, stop
);