2 * Copyright (c) 2012 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.)
27 * Created by Michael Brouwer on 11/15/12.
30 #include <securityd/SecDbItem.h>
31 #include <utilities/SecCFWrappers.h>
32 #include <utilities/der_date.h>
33 #include <utilities/debugging.h>
35 #include <Security/SecBasePriv.h>
36 #include <Security/SecInternal.h>
37 #include <corecrypto/ccsha1.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecItemPriv.h>
41 // MARK: type converters
43 CFStringRef
copyString(CFTypeRef obj
) {
44 CFTypeID tid
= CFGetTypeID(obj
);
45 if (tid
== CFStringGetTypeID())
46 return CFStringCreateCopy(0, obj
);
47 else if (tid
== CFDataGetTypeID())
48 return CFStringCreateFromExternalRepresentation(0, obj
, kCFStringEncodingUTF8
);
53 CFDataRef
copyData(CFTypeRef obj
) {
54 CFTypeID tid
= CFGetTypeID(obj
);
55 if (tid
== CFDataGetTypeID()) {
56 return CFDataCreateCopy(0, obj
);
57 } else if (tid
== CFStringGetTypeID()) {
58 return CFStringCreateExternalRepresentation(0, obj
, kCFStringEncodingUTF8
, 0);
59 } else if (tid
== CFNumberGetTypeID()) {
61 CFNumberGetValue(obj
, kCFNumberSInt32Type
, &value
);
62 return CFDataCreate(0, (const UInt8
*)&value
, sizeof(value
));
68 CFTypeRef
copyBlob(CFTypeRef obj
) {
69 CFTypeID tid
= CFGetTypeID(obj
);
70 if (tid
== CFDataGetTypeID()) {
71 return CFDataCreateCopy(0, obj
);
72 } else if (tid
== CFStringGetTypeID()) {
73 return CFStringCreateCopy(0, obj
);
74 } else if (tid
== CFNumberGetTypeID()) {
82 CFDataRef
copySHA1(CFTypeRef obj
) {
83 CFTypeID tid
= CFGetTypeID(obj
);
84 if (tid
== CFDataGetTypeID() && CFDataGetLength(obj
) == CCSHA1_OUTPUT_SIZE
) {
85 return CFDataCreateCopy(CFGetAllocator(obj
), obj
);
91 CFTypeRef
copyNumber(CFTypeRef obj
) {
92 CFTypeID tid
= CFGetTypeID(obj
);
93 if (tid
== CFNumberGetTypeID()) {
96 } else if (tid
== CFBooleanGetTypeID()) {
97 SInt32 value
= CFBooleanGetValue(obj
);
98 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
99 } else if (tid
== CFStringGetTypeID()) {
100 SInt32 value
= CFStringGetIntValue(obj
);
101 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value
);
102 /* If a string converted to an int isn't equal to the int printed as
103 a string, return a CFStringRef instead. */
104 if (!CFEqual(t
, obj
)) {
106 return CFStringCreateCopy(0, obj
);
109 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
114 CFDateRef
copyDate(CFTypeRef obj
) {
115 CFTypeID tid
= CFGetTypeID(obj
);
116 if (tid
== CFDateGetTypeID()) {
123 // MARK: SecDbColumn accessors, to retrieve values as CF types in SecDbStep.
125 static CFDataRef
SecDbColumnCopyData(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
126 return CFDataCreate(allocator
, sqlite3_column_blob(stmt
, col
),
127 sqlite3_column_bytes(stmt
, col
));
128 //return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col),
129 // sqlite3_column_bytes(stmt, col),
130 // kCFAllocatorNull);
133 static CFDateRef
SecDbColumnCopyDate(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
134 return CFDateCreate(allocator
, sqlite3_column_double(stmt
, col
));
137 static CFNumberRef
SecDbColumnCopyDouble(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
138 double number
= sqlite3_column_double(stmt
, col
);
139 return CFNumberCreate(allocator
, kCFNumberDoubleType
, &number
);
142 static CFNumberRef
SecDbColumnCopyNumber64(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
143 sqlite_int64 number
= sqlite3_column_int64(stmt
, col
);
144 return CFNumberCreate(allocator
, kCFNumberSInt64Type
, &number
);
147 static CFNumberRef
SecDbColumnCopyNumber(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
148 sqlite_int64 number
= sqlite3_column_int64(stmt
, col
);
149 if (INT32_MIN
<= number
&& number
<= INT32_MAX
) {
150 int32_t num32
= (int32_t)number
;
151 return CFNumberCreate(allocator
, kCFNumberSInt32Type
, &num32
);
153 return CFNumberCreate(allocator
, kCFNumberSInt64Type
, &number
);
157 static CFStringRef
SecDbColumnCopyString(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
158 const unsigned char *text
= sqlite3_column_text(stmt
, col
);
159 return CFStringCreateWithBytes(allocator
, text
, strlen((const char *)text
), kCFStringEncodingUTF8
, false);
162 // MARK: SecDbClass helpers
164 const SecDbAttr
*SecDbClassAttrWithKind(const SecDbClass
*class, SecDbAttrKind kind
, CFErrorRef
*error
) {
165 const SecDbAttr
*result
= NULL
;
166 SecDbForEachAttr(class, desc
) {
167 if (desc
->kind
== kind
)
172 SecError(errSecInternal
, error
, CFSTR("Can't find attribute of kind %d in class %@"), kind
, class->name
);
177 // MARK: SecDbAttr helpers
179 static bool SecDbIsTombstoneDbSelectAttr(const SecDbAttr
*attr
) {
180 return attr
->flags
& kSecDbPrimaryKeyFlag
|| attr
->kind
== kSecDbTombAttr
;
184 static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr
*attr
) {
185 return SecDbIsTombstoneDbSelectAttr(attr
) || attr
->kind
== kSecDbAccessAttr
|| attr
->kind
== kSecDbCreationDateAttr
|| attr
->kind
== kSecDbModificationDateAttr
;
189 static bool SecDbIsTombstoneDbUpdateAttr(const SecDbAttr
*attr
) {
190 return SecDbIsTombstoneDbSelectAttr(attr
) || attr
->kind
== kSecDbAccessAttr
|| attr
->kind
== kSecDbCreationDateAttr
|| attr
->kind
== kSecDbRowIdAttr
;
193 static bool SecDbAttrBind(const SecDbAttr
*attr
, sqlite3_stmt
*stmt
, int col
, CFTypeRef value
, CFErrorRef
*error
) {
195 if (value
&& !CFEqual(kCFNull
, value
) && attr
->flags
& kSecDbSHA1ValueInFlag
) {
196 CFDataRef data
= copyData(value
);
198 SecError(errSecInternal
, error
, CFSTR("failed to get attribute %@ data"), attr
->name
);
202 CFMutableDataRef digest
= CFDataCreateMutable(kCFAllocatorDefault
, CCSHA1_OUTPUT_SIZE
);
203 CFDataSetLength(digest
, CCSHA1_OUTPUT_SIZE
);
204 /* 64 bits cast: worst case is we generate the wrong hash */
205 assert((unsigned long)CFDataGetLength(data
)<UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
206 ccdigest(ccsha1_di(), CFDataGetLength(data
), CFDataGetBytePtr(data
), CFDataGetMutableBytePtr(digest
));
208 ok
&= SecDbBindObject(stmt
, col
, digest
, error
);
211 ok
&= SecDbBindObject(stmt
, col
, value
, error
);
218 CFTypeRef
SecDbItemGetCachedValueWithName(SecDbItemRef item
, CFStringRef name
) {
219 return CFDictionaryGetValue(item
->attributes
, name
);
222 static CFTypeRef
SecDbItemGetCachedValue(SecDbItemRef item
, const SecDbAttr
*desc
) {
223 return CFDictionaryGetValue(item
->attributes
, desc
->name
);
226 CFMutableDictionaryRef
SecDbItemCopyPListWithMask(SecDbItemRef item
, CFOptionFlags mask
, CFErrorRef
*error
) {
227 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
228 SecDbForEachAttrWithMask(item
->class, desc
, mask
) {
229 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
231 if (!CFEqual(kCFNull
, value
)) {
232 CFDictionarySetValue(dict
, desc
->name
, value
);
233 } else if (desc
->flags
& kSecDbNotNullFlag
) {
234 SecError(errSecInternal
, error
, CFSTR("attribute %@ has NULL value"), desc
->name
);
235 secerror("%@", error
? *error
: (CFErrorRef
)CFSTR("error == NULL"));
247 static CFDataRef
SecDbItemCopyDERWithMask(SecDbItemRef item
, CFOptionFlags mask
, CFErrorRef
*error
) {
248 CFDataRef der
= NULL
;
249 CFMutableDictionaryRef dict
= SecDbItemCopyPListWithMask(item
, mask
, error
);
251 der
= kc_plist_copy_der(dict
, error
);
257 static CFDataRef
SecDbItemCopyDigestWithMask(SecDbItemRef item
, CFOptionFlags mask
, CFErrorRef
*error
) {
258 CFDataRef digest
= NULL
;
259 CFDataRef der
= SecDbItemCopyDERWithMask(item
, mask
, error
);
261 digest
= kc_copy_sha1(CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
267 static CFDataRef
SecDbItemCopyPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
268 return SecDbItemCopyDigestWithMask(item
, kSecDbPrimaryKeyFlag
, error
);
271 static CFDataRef
SecDbItemCopySHA1(SecDbItemRef item
, CFErrorRef
*error
) {
272 return SecDbItemCopyDigestWithMask(item
, kSecDbInHashFlag
, error
);
275 static CFDataRef
SecDbItemCopyUnencryptedData(SecDbItemRef item
, CFErrorRef
*error
) {
276 return SecDbItemCopyDERWithMask(item
, kSecDbInCryptoDataFlag
, error
);
279 static keyclass_t
SecDbItemParseKeyclass(CFTypeRef value
, CFErrorRef
*error
) {
280 if (!isString(value
)) {
281 SecError(errSecParam
, error
, CFSTR("accessible attribute %@ not a string"), value
);
282 } else if (CFEqual(value
, kSecAttrAccessibleWhenUnlocked
)) {
284 } else if (CFEqual(value
, kSecAttrAccessibleAfterFirstUnlock
)) {
286 } else if (CFEqual(value
, kSecAttrAccessibleAlways
)) {
288 } else if (CFEqual(value
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
)) {
289 return key_class_aku
;
290 } else if (CFEqual(value
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
)) {
291 return key_class_cku
;
292 } else if (CFEqual(value
, kSecAttrAccessibleAlwaysThisDeviceOnly
)) {
293 return key_class_dku
;
295 SecError(errSecParam
, error
, CFSTR("accessible attribute %@ unknown"), value
);
300 keyclass_t
SecDbItemGetKeyclass(SecDbItemRef item
, CFErrorRef
*error
) {
301 if (!item
->keyclass
) {
302 const SecDbAttr
*desc
= SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
);
304 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
306 item
->keyclass
= SecDbItemParseKeyclass(value
, error
);
310 return item
->keyclass
;
313 static CFDataRef
SecDbItemCopyEncryptedData(SecDbItemRef item
, CFErrorRef
*error
) {
314 CFDataRef edata
= NULL
;
315 CFDataRef plain
= SecDbItemCopyUnencryptedData(item
, error
);
317 keyclass_t keyclass
= SecDbItemGetKeyclass(item
, error
);
319 if (ks_encrypt_data(item
->keybag
, keyclass
, plain
, &edata
, error
)) {
320 item
->_edataState
= kSecDbItemEncrypting
;
322 seccritical("ks_encrypt_data (db): failed: %@", error
? *error
: (CFErrorRef
)CFSTR(""));
331 CFDataRef
SecDbItemCopyEncryptedDataToBackup(SecDbItemRef item
, uint64_t handle
, CFErrorRef
*error
) {
332 CFDataRef edata
= NULL
;
333 keybag_handle_t keybag
= (keybag_handle_t
)handle
;
334 CFDataRef plain
= SecDbItemCopyUnencryptedData(item
, error
);
336 keyclass_t keyclass
= SecDbItemGetKeyclass(item
, error
);
338 if (!ks_encrypt_data(keybag
, keyclass
, plain
, &edata
, error
))
339 seccritical("ks_encrypt_data (db): failed: %@", error
? *error
: (CFErrorRef
)CFSTR(""));
346 static bool SecDbItemEnsureDecrypted(SecDbItemRef item
, CFErrorRef
*error
) {
348 // If we haven't yet decrypted the item, make sure we do so now
350 if (item
->_edataState
== kSecDbItemEncrypted
) {
351 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, error
);
353 CFDataRef edata
= SecDbItemGetCachedValue(item
, attr
);
355 return SecError(errSecInternal
, error
, CFSTR("state= encrypted but edata is NULL"));
356 // Decrypt calls set value a bunch of times which clears our edata and changes our state.
357 item
->_edataState
= kSecDbItemDecrypting
;
358 result
= SecDbItemDecrypt(item
, edata
, error
);
360 item
->_edataState
= kSecDbItemClean
;
362 item
->_edataState
= kSecDbItemEncrypted
;
368 // Only called if cached value is not found.
369 static CFTypeRef
SecDbItemCopyValue(SecDbItemRef item
, const SecDbAttr
*attr
, CFErrorRef
*error
) {
370 CFTypeRef value
= NULL
;
371 switch (attr
->kind
) {
373 value
= SecDbItemCopySHA1(item
, error
);
375 case kSecDbEncryptedDataAttr
:
376 value
= SecDbItemCopyEncryptedData(item
, error
);
378 case kSecDbPrimaryKeyAttr
:
379 value
= SecDbItemCopyPrimaryKey(item
, error
);
381 case kSecDbAccessAttr
:
382 case kSecDbStringAttr
:
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());
434 // SecDbItemGetValue will return kCFNull if there is no value for an attribute and this was not
435 // an error. It will return NULL and optionally set *error if there was an error computing an
436 // attribute, or if a required attribute was missing a value and had no known way to compute
438 CF_RETURNS_NOT_RETAINED CFTypeRef
SecDbItemGetValue(SecDbItemRef item
, const SecDbAttr
*desc
, CFErrorRef
*error
) {
439 // Propagate chained errors
443 if (desc
->flags
& kSecDbInCryptoDataFlag
) {
444 if (!SecDbItemEnsureDecrypted(item
, error
))
448 CFTypeRef value
= SecDbItemGetCachedValue(item
, desc
);
450 value
= SecDbItemCopyValue(item
, desc
, error
);
452 if (!CFEqual(kCFNull
, value
)) {
453 SecDbItemSetValue(item
, desc
, value
, error
);
455 value
= SecDbItemGetCachedValue(item
, desc
);
462 static bool SecDbItemGetBoolValue(SecDbItemRef item
, const SecDbAttr
*desc
, bool *bvalue
, CFErrorRef
*error
) {
463 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
467 *bvalue
= (isNumber(value
) && CFNumberGetValue(value
, kCFNumberCharType
, &cvalue
) && cvalue
== 1);
471 static CFStringRef
SecDbItemCopyDescription(CFTypeRef cf
) {
472 #if 0 //defined(DEBUG) && DEBUG != 0
473 SecDbItemRef item
= (SecDbItemRef
)cf
;
474 CFMutableStringRef desc
= CFStringCreateMutable(CFGetAllocator(cf
), 0);
475 CFStringAppendFormat(desc
, NULL
, CFSTR("<%@"), item
->class->name
);
476 SecDbForEachAttr(item
->class, attr
) {
477 CFTypeRef value
= SecDbItemGetValue(item
, attr
, NULL
);
479 CFStringAppend(desc
, CFSTR(","));
480 CFStringAppend(desc
, attr
->name
);
481 CFStringAppend(desc
, CFSTR("="));
482 if (CFEqual(CFSTR("data"), attr
->name
)) {
483 CFStringAppendEncryptedData(desc
, value
);
484 } else if (CFEqual(CFSTR("v_Data"), attr
->name
)) {
485 CFStringAppend(desc
, CFSTR("<?>"));
486 } else if (isData(value
)) {
487 CFStringAppendHexData(desc
, value
);
489 CFStringAppendFormat(desc
, 0, CFSTR("%@"), value
);
493 CFStringAppend(desc
, CFSTR(">"));
495 SecDbItemRef item
= (SecDbItemRef
)cf
;
496 const UInt8 zero4
[4] = {};
497 const UInt8
*pk
= &zero4
[0], *sha1
= &zero4
[0];
501 CFStringRef access
= NULL
;
502 uint8_t mdatbuf
[32] = {};
503 uint8_t *mdat
= &mdatbuf
[0];
504 CFMutableStringRef attrs
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
505 CFStringRef agrp
= NULL
;
507 SecDbForEachAttr(item
->class, attr
) {
509 switch (attr
->kind
) {
512 case kSecDbStringAttr
:
513 case kSecDbNumberAttr
:
515 case kSecDbEncryptedDataAttr
:
516 if (attr
->flags
& (kSecDbReturnAttrFlag
| kSecDbReturnDataFlag
) && (value
= SecDbItemGetValue(item
, attr
, NULL
)) && !CFEqual(value
, kCFNull
)) {
517 if (isString(value
) && CFEqual(attr
->name
, kSecAttrAccessGroup
)) {
520 // We don't log these, just record that we saw the attribute.
521 CFStringAppend(attrs
, CFSTR(","));
522 CFStringAppend(attrs
, attr
->name
);
526 case kSecDbCreationDateAttr
:
527 // We don't care about this and every object has one.
529 case kSecDbModificationDateAttr
:
530 value
= SecDbItemGetValue(item
, attr
, NULL
);
532 mdat
= der_encode_generalizedtime_body(CFDateGetAbsoluteTime(value
), NULL
, mdat
, &mdatbuf
[31]);
535 value
= SecDbItemGetValue(item
, attr
, NULL
);
537 sha1
= CFDataGetBytePtr(value
);
539 case kSecDbRowIdAttr
:
540 value
= SecDbItemGetValue(item
, attr
, NULL
);
542 CFNumberGetValue(value
, kCFNumberSInt64Type
, &rowid
);
544 case kSecDbPrimaryKeyAttr
:
545 value
= SecDbItemGetValue(item
, attr
, NULL
);
547 pk
= CFDataGetBytePtr(value
);
550 value
= SecDbItemGetValue(item
, attr
, NULL
);
552 CFNumberGetValue(value
, kCFNumberCharType
, &sync
);
555 value
= SecDbItemGetValue(item
, attr
, NULL
);
557 CFNumberGetValue(value
, kCFNumberCharType
, &tomb
);
559 case kSecDbAccessAttr
:
560 value
= SecDbItemGetValue(item
, attr
, NULL
);
567 CFStringRef desc
= CFStringCreateWithFormat(CFGetAllocator(cf
), NULL
,
581 pk
[0], pk
[1], pk
[2], pk
[3],
588 sha1
[0], sha1
[1], sha1
[2], sha1
[3]);
589 CFReleaseSafe(attrs
);
595 static void SecDbItemDestroy(CFTypeRef cf
) {
596 SecDbItemRef item
= (SecDbItemRef
)cf
;
597 CFReleaseSafe(item
->attributes
);
600 static CFHashCode
SecDbItemHash(CFTypeRef cf
) {
601 SecDbItemRef item
= (SecDbItemRef
)cf
;
602 CFDataRef digest
= SecDbItemGetSHA1(item
, NULL
);
604 const UInt8
*p
= CFDataGetBytePtr(digest
);
605 // Read first 8 bytes of digest in order
606 code
= p
[0] + ((p
[1] + ((p
[2] + ((p
[3] + ((p
[4] + ((p
[5] + ((p
[6] + (p
[7] << 8)) << 8)) << 8)) << 8)) << 8)) << 8)) << 8);
610 static Boolean
SecDbItemCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
611 SecDbItemRef item1
= (SecDbItemRef
)cf1
;
612 SecDbItemRef item2
= (SecDbItemRef
)cf2
;
613 CFDataRef digest1
= NULL
;
614 CFDataRef digest2
= NULL
;
616 digest1
= SecDbItemGetSHA1(item1
, NULL
);
618 digest2
= SecDbItemGetSHA1(item2
, NULL
);
619 Boolean equal
= CFEqual(digest1
, digest2
);
623 CFGiblisWithHashFor(SecDbItem
)
625 static SecDbItemRef
SecDbItemCreate(CFAllocatorRef allocator
, const SecDbClass
*class, keybag_handle_t keybag
) {
626 SecDbItemRef item
= CFTypeAllocate(SecDbItem
, struct SecDbItem
, allocator
);
628 item
->attributes
= CFDictionaryCreateMutableForCFTypes(allocator
);
629 item
->keybag
= keybag
;
630 item
->_edataState
= kSecDbItemDirty
;
634 const SecDbClass
*SecDbItemGetClass(SecDbItemRef item
) {
638 const keybag_handle_t
SecDbItemGetKeybag(SecDbItemRef item
) {
642 bool SecDbItemSetKeybag(SecDbItemRef item
, keybag_handle_t keybag
, CFErrorRef
*error
) {
643 if (!SecDbItemEnsureDecrypted(item
, error
))
645 if (item
->keybag
!= keybag
) {
646 item
->keybag
= keybag
;
647 if (item
->_edataState
== kSecDbItemClean
) {
648 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, NULL
), kCFNull
, NULL
);
655 bool SecDbItemSetValue(SecDbItemRef item
, const SecDbAttr
*desc
, CFTypeRef value
, CFErrorRef
*error
) {
656 // Propagate chained errors.
660 bool changed
= false;
661 CFTypeRef attr
= NULL
;
662 if (desc
->flags
& kSecDbInCryptoDataFlag
)
663 if (!SecDbItemEnsureDecrypted(item
, error
))
666 switch (desc
->kind
) {
667 case kSecDbPrimaryKeyAttr
:
669 attr
= copyData(value
);
671 case kSecDbEncryptedDataAttr
:
672 attr
= copyData(value
);
674 if (item
->_edataState
== kSecDbItemEncrypting
)
675 item
->_edataState
= kSecDbItemClean
;
677 item
->_edataState
= kSecDbItemEncrypted
;
678 } else if (!value
|| CFEqual(kCFNull
, value
)) {
679 item
->_edataState
= kSecDbItemDirty
;
683 attr
= copyBlob(value
);
686 case kSecDbCreationDateAttr
:
687 case kSecDbModificationDateAttr
:
688 attr
= copyDate(value
);
690 case kSecDbNumberAttr
:
693 case kSecDbRowIdAttr
:
694 attr
= copyNumber(value
);
696 case kSecDbAccessAttr
:
697 case kSecDbStringAttr
:
698 attr
= copyString(value
);
701 attr
= copySHA1(value
);
706 CFTypeRef ovalue
= CFDictionaryGetValue(item
->attributes
, desc
->name
);
707 changed
= (!ovalue
|| !CFEqual(ovalue
, attr
));
708 CFDictionarySetValue(item
->attributes
, desc
->name
, attr
);
711 if (value
&& !CFEqual(kCFNull
, value
)) {
712 SecError(errSecItemInvalidValue
, error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
715 CFTypeRef ovalue
= CFDictionaryGetValue(item
->attributes
, desc
->name
);
716 changed
= (ovalue
&& !CFEqual(ovalue
, kCFNull
));
717 CFDictionaryRemoveValue(item
->attributes
, desc
->name
);
721 if (desc
->flags
& kSecDbInHashFlag
)
722 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
), kCFNull
, NULL
);
723 if (desc
->flags
& kSecDbPrimaryKeyFlag
)
724 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbPrimaryKeyAttr
, NULL
), kCFNull
, NULL
);
725 if (desc
->flags
& kSecDbInCryptoDataFlag
&& item
->_edataState
== kSecDbItemClean
)
726 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, NULL
), kCFNull
, NULL
);
732 bool SecDbItemSetValues(SecDbItemRef item
, CFDictionaryRef values
, CFErrorRef
*error
) {
733 SecDbForEachAttr(item
->class, attr
) {
734 CFTypeRef value
= CFDictionaryGetValue(values
, attr
->name
);
735 if (value
&& !SecDbItemSetValue(item
, attr
, value
, error
))
741 bool SecDbItemSetValueWithName(SecDbItemRef item
, CFStringRef name
, CFTypeRef value
, CFErrorRef
*error
) {
742 SecDbForEachAttr(item
->class, attr
) {
743 if (CFEqual(attr
->name
, name
)) {
744 return SecDbItemSetValue(item
, attr
, value
, error
);
750 static bool SecDbItemSetNumber(SecDbItemRef item
, const SecDbAttr
*desc
, int32_t number
, CFErrorRef
*error
) {
752 CFNumberRef value
= CFNumberCreate(CFGetAllocator(item
), kCFNumberSInt32Type
, &number
);
754 ok
= SecDbItemSetValue(item
, desc
, value
, error
);
757 ok
= SecError(errSecInternal
, error
, CFSTR("Failed to create CFNumber for %" PRId32
), number
);
762 bool SecDbItemSetKeyclass(SecDbItemRef item
, keyclass_t keyclass
, CFErrorRef
*error
) {
763 bool ok
= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, error
), kCFNull
, error
);
765 item
->_edataState
= kSecDbItemDirty
;
766 ok
= SecDbItemSetNumber(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
), keyclass
, error
);
771 SecDbItemRef
SecDbItemCreateWithAttributes(CFAllocatorRef allocator
, const SecDbClass
*class, CFDictionaryRef attributes
, keybag_handle_t keybag
, CFErrorRef
*error
) {
772 SecDbItemRef item
= SecDbItemCreate(kCFAllocatorDefault
, class, keybag
);
773 if (item
&& !SecDbItemSetValues(item
, attributes
, error
))
779 SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, const SecDbAttr
*attr
, int col
, CFErrorRef
*error
) {
780 CFTypeRef value
= NULL
;
781 switch (attr
->kind
) {
783 case kSecDbCreationDateAttr
:
784 case kSecDbModificationDateAttr
:
785 value
= SecDbColumnCopyDate(allocator
, stmt
, col
, error
);
788 switch (sqlite3_column_type(stmt
, col
)) {
790 value
= SecDbColumnCopyNumber(allocator
, stmt
, col
, error
);
793 value
= SecDbColumnCopyDouble(allocator
, stmt
, col
, error
);
796 value
= SecDbColumnCopyString(allocator
, stmt
, col
, error
);
799 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
806 case kSecDbAccessAttr
:
807 case kSecDbStringAttr
:
808 value
= SecDbColumnCopyString(allocator
, stmt
, col
, error
);
812 case kSecDbPrimaryKeyAttr
:
813 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
815 case kSecDbEncryptedDataAttr
:
816 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
820 case kSecDbNumberAttr
:
821 value
= SecDbColumnCopyNumber(allocator
, stmt
, col
, error
);
823 case kSecDbRowIdAttr
:
824 value
= SecDbColumnCopyNumber64(allocator
, stmt
, col
, error
);
830 SecDbItemRef
SecDbItemCreateWithStatement(CFAllocatorRef allocator
, const SecDbClass
*class, sqlite3_stmt
*stmt
, keybag_handle_t keybag
, CFErrorRef
*error
, bool (^return_attr
)(const SecDbAttr
*attr
)) {
831 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
833 SecDbForEachAttr(class, attr
) {
834 if (return_attr(attr
)) {
835 CFTypeRef value
= SecDbColumnCopyValueWithAttr(allocator
, stmt
, attr
, col
++, error
);
837 SecDbItemSetValue(item
, attr
, value
, error
);
846 SecDbItemRef
SecDbItemCreateWithEncryptedData(CFAllocatorRef allocator
, const SecDbClass
*class,
847 CFDataRef edata
, keybag_handle_t keybag
, CFErrorRef
*error
) {
848 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
849 const SecDbAttr
*edata_attr
= SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr
, error
);
851 if (!SecDbItemSetValue(item
, edata_attr
, edata
, error
))
858 SecDbItemRef
SecDbItemCreateWithRowId(CFAllocatorRef allocator
, const SecDbClass
*class, sqlite_int64 row_id
, keybag_handle_t keybag
, CFErrorRef
*error
) {
859 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
860 if (!SecDbItemSetRowId(item
, row_id
, error
))
866 SecDbItemRef
SecDbItemCopyWithUpdates(SecDbItemRef item
, CFDictionaryRef updates
, CFErrorRef
*error
) {
867 SecDbItemRef new_item
= SecDbItemCreate(CFGetAllocator(item
), item
->class, item
->keybag
);
868 SecDbForEachAttr(item
->class, attr
) {
869 // Copy each attribute, except the mod date attribute (it will be reset to now when needed),
870 // from the updates dict unless it's not there in which case we copy the attribute from the passed in item.
871 if (attr
->kind
!= kSecDbModificationDateAttr
&& attr
->kind
!= kSecDbEncryptedDataAttr
&& attr
->kind
!= kSecDbSHA1Attr
&& attr
->kind
!= kSecDbPrimaryKeyAttr
) {
872 CFTypeRef value
= NULL
;
873 if (CFDictionaryGetValueIfPresent(updates
, attr
->name
, &value
)) {
875 SecError(errSecParam
, error
, CFSTR("NULL value in dictionary"));
877 value
= SecDbItemGetValue(item
, attr
, error
);
879 if (!value
|| !SecDbItemSetValue(new_item
, attr
, value
, error
)) {
880 CFReleaseNull(new_item
);
888 // Ensure that the date value of attr of new_item is greater than that of old_item.
889 static bool SecDbItemMakeAttrYounger(SecDbItemRef new_item
, SecDbItemRef old_item
, const SecDbAttr
*attr
, CFErrorRef
*error
) {
890 CFDateRef old_date
= SecDbItemGetValue(old_item
, attr
, error
);
893 CFDateRef new_date
= SecDbItemGetValue(new_item
, attr
, error
);
897 if (CFDateCompare(new_date
, old_date
, NULL
) != kCFCompareGreaterThan
) {
898 CFDateRef adjusted_date
= CFDateCreate(kCFAllocatorDefault
, CFDateGetAbsoluteTime(old_date
) + 0.001);
900 ok
= SecDbItemSetValue(new_item
, attr
, adjusted_date
, error
);
901 CFRelease(adjusted_date
);
907 // Ensure that the mod date of new_item is greater than that of old_item.
908 static bool SecDbItemMakeYounger(SecDbItemRef new_item
, SecDbItemRef old_item
, CFErrorRef
*error
) {
909 const SecDbAttr
*attr
= SecDbClassAttrWithKind(new_item
->class, kSecDbModificationDateAttr
, error
);
910 return attr
&& SecDbItemMakeAttrYounger(new_item
, old_item
, attr
, error
);
913 SecDbItemRef
SecDbItemCopyTombstone(SecDbItemRef item
, CFErrorRef
*error
) {
914 SecDbItemRef new_item
= SecDbItemCreate(CFGetAllocator(item
), item
->class, item
->keybag
);
915 SecDbForEachAttr(item
->class, attr
) {
916 if (attr
->kind
== kSecDbTombAttr
) {
917 // Set the tomb attr to true to indicate a tombstone.
918 if (!SecDbItemSetValue(new_item
, attr
, kCFBooleanTrue
, error
)) {
919 CFReleaseNull(new_item
);
922 } else if (SecDbIsTombstoneDbUpdateAttr(attr
)) {
923 // Copy all primary key attributes and creation timestamps from the original item.
924 CFTypeRef value
= SecDbItemGetValue(item
, attr
, error
);
925 if (!value
|| (!CFEqual(kCFNull
, value
) && !SecDbItemSetValue(new_item
, attr
, value
, error
))) {
926 CFReleaseNull(new_item
);
929 } else if (attr
->kind
== kSecDbModificationDateAttr
) {
930 if (!SecDbItemMakeAttrYounger(new_item
, item
, attr
, error
)) {
931 CFReleaseNull(new_item
);
941 // MARK: SQL Construction helpers -- These should become private in the future
943 void SecDbAppendElement(CFMutableStringRef sql
, CFStringRef value
, bool *needComma
) {
946 CFStringAppend(sql
, CFSTR(","));
950 CFStringAppend(sql
, value
);
953 static void SecDbAppendElementEquals(CFMutableStringRef sql
, CFStringRef value
, bool *needComma
) {
954 SecDbAppendElement(sql
, value
, needComma
);
955 CFStringAppend(sql
, CFSTR("=?"));
958 /* Append AND is needWhere is NULL or *needWhere is false. Append WHERE
959 otherwise. Upon return *needWhere will be false. */
961 SecDbAppendWhereOrAnd(CFMutableStringRef sql
, bool *needWhere
) {
962 if (!needWhere
|| !*needWhere
) {
963 CFStringAppend(sql
, CFSTR(" AND "));
965 CFStringAppend(sql
, CFSTR(" WHERE "));
971 SecDbAppendWhereOrAndEquals(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
) {
972 SecDbAppendWhereOrAnd(sql
, needWhere
);
973 CFStringAppend(sql
, col
);
974 CFStringAppend(sql
, CFSTR("=?"));
977 static CFStringRef
SecDbItemCopyInsertSQL(SecDbItemRef item
, bool(^use_attr
)(const SecDbAttr
*attr
)) {
978 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(item
), 0);
979 CFStringAppend(sql
, CFSTR("INSERT INTO "));
980 CFStringAppend(sql
, item
->class->name
);
981 CFStringAppend(sql
, CFSTR("("));
982 bool needComma
= false;
983 CFIndex used_attr
= 0;
984 SecDbForEachAttr(item
->class, attr
) {
985 if (use_attr(attr
)) {
987 SecDbAppendElement(sql
, attr
->name
, &needComma
);
990 CFStringAppend(sql
, CFSTR(")VALUES(?"));
991 while (used_attr
-- > 1) {
992 CFStringAppend(sql
, CFSTR(",?"));
994 CFStringAppend(sql
, CFSTR(")"));
999 static bool SecDbItemInsertBind(SecDbItemRef item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr
)(const SecDbAttr
*attr
)) {
1002 SecDbForEachAttr(item
->class, attr
) {
1003 if (use_attr(attr
)) {
1004 CFTypeRef value
= SecDbItemGetValue(item
, attr
, error
);
1005 if (!value
|| !SecDbAttrBind(attr
, stmt
, ++param
, value
, error
)) {
1014 sqlite3_int64
SecDbItemGetRowId(SecDbItemRef item
, CFErrorRef
*error
) {
1015 sqlite3_int64 row_id
= 0;
1016 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1018 CFNumberRef number
= SecDbItemGetValue(item
, attr
, error
);
1019 if (!isNumber(number
)|| !CFNumberGetValue(number
, kCFNumberSInt64Type
, &row_id
))
1020 SecDbError(SQLITE_ERROR
, error
, CFSTR("rowid %@ is not a 64 bit number"), number
);
1026 static CFNumberRef
SecDbItemCreateRowId(SecDbItemRef item
, sqlite3_int64 rowid
, CFErrorRef
*error
) {
1027 return CFNumberCreate(CFGetAllocator(item
), kCFNumberSInt64Type
, &rowid
);
1030 bool SecDbItemSetRowId(SecDbItemRef item
, sqlite3_int64 rowid
, CFErrorRef
*error
) {
1032 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1034 CFNumberRef value
= SecDbItemCreateRowId(item
, rowid
, error
);
1038 ok
= SecDbItemSetValue(item
, attr
, value
, error
);
1044 static bool SecDbItemClearRowId(SecDbItemRef item
, CFErrorRef
*error
) {
1046 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1048 CFDictionaryRemoveValue(item
->attributes
, attr
->name
);
1049 //ok = SecDbItemSetValue(item, attr, kCFNull, error);
1054 static bool SecDbItemSetLastInsertRowId(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1055 sqlite3_int64 rowid
= sqlite3_last_insert_rowid(SecDbHandle(dbconn
));
1056 return SecDbItemSetRowId(item
, rowid
, error
);
1059 bool SecDbItemIsSyncable(SecDbItemRef item
) {
1061 if (SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, NULL
), &is_syncable
, NULL
))
1066 bool SecDbItemSetSyncable(SecDbItemRef item
, bool sync
, CFErrorRef
*error
)
1068 return SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, error
), sync
? kCFBooleanTrue
: kCFBooleanFalse
, error
);
1071 bool SecDbItemIsTombstone(SecDbItemRef item
) {
1073 if (SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbTombAttr
, NULL
), &is_tomb
, NULL
))
1078 CFDataRef
SecDbItemGetPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
1079 return SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbPrimaryKeyAttr
, error
), error
);
1082 CFDataRef
SecDbItemGetSHA1(SecDbItemRef item
, CFErrorRef
*error
) {
1083 return SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, error
), error
);
1086 static SecDbQueryRef
SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
1087 CFMutableDictionaryRef dict
= SecDbItemCopyPListWithMask(item
, kSecDbPrimaryKeyFlag
, error
);
1091 SecDbQueryRef query
= query_create(item
->class, NULL
, error
);
1093 query
->q_item
= dict
;
1100 static bool SecDbItemIsCorrupt(SecDbItemRef item
, bool *is_corrupt
, CFErrorRef
*error
) {
1101 CFErrorRef localError
= NULL
;
1102 bool ok
= SecDbItemEnsureDecrypted(item
, &localError
);
1104 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1105 // We failed to decrypt the item
1106 secerror("error %@ reading item %@ (corrupted)", localError
, item
);
1107 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem
);
1110 } else if (error
&& *error
== NULL
) {
1111 *error
= localError
;
1114 CFReleaseSafe(localError
);
1119 static bool SecDbItemDoInsert(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1120 bool (^use_attr
)(const SecDbAttr
*attr
) = ^bool(const SecDbAttr
*attr
) {
1121 return (attr
->flags
& kSecDbInFlag
);
1123 CFStringRef sql
= SecDbItemCopyInsertSQL(item
, use_attr
);
1124 __block
bool ok
= sql
;
1126 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1127 ok
= (SecDbItemInsertBind(item
, stmt
, error
, use_attr
) &&
1128 SecDbStep(dbconn
, stmt
, error
, NULL
) &&
1129 SecDbItemSetLastInsertRowId(item
, dbconn
, error
));
1134 secnotice("item", "inserted %@", item
);
1139 bool SecDbItemInsertOrReplace(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, void(^duplicate
)(SecDbItemRef item
, SecDbItemRef
*replace
)) {
1140 __block CFErrorRef localError
= NULL
;
1141 __block
bool ok
= SecDbItemDoInsert(item
, dbconn
, &localError
);
1142 if (!ok
&& localError
&& CFErrorGetCode(localError
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(localError
))) {
1143 SecDbQueryRef query
= SecDbQueryCreateWithItemPrimaryKey(item
, error
);
1145 SecDbItemSelect(query
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1146 return attr
->flags
& kSecDbPrimaryKeyFlag
;
1147 }, NULL
, NULL
, ^(SecDbItemRef old_item
, bool *stop
) {
1148 bool is_corrupt
= false;
1149 ok
= SecDbItemIsCorrupt(old_item
, &is_corrupt
, error
);
1150 SecDbItemRef replace
= NULL
;
1152 // If old_item is corrupted pretend it's not there and just replace it.
1155 } else if (ok
&& duplicate
) {
1156 duplicate(old_item
, &replace
);
1159 const SecDbAttr
*rowid_attr
= SecDbClassAttrWithKind(old_item
->class, kSecDbRowIdAttr
, error
);
1160 CFNumberRef oldrowid
= SecDbItemGetCachedValue(old_item
, rowid_attr
);
1162 ok
= SecDbItemSetValue(replace
, rowid_attr
, oldrowid
, &localError
);
1163 if (ok
&& !is_corrupt
) {
1164 ok
= SecDbItemMakeYounger(replace
, old_item
, error
);
1166 ok
= ok
&& SecDbItemDoUpdate(old_item
, replace
, dbconn
, &localError
, ^bool (const SecDbAttr
*attr
) {
1167 return attr
->kind
== kSecDbRowIdAttr
;
1170 ok
= SecError(errSecInternal
, &localError
, CFSTR("no rowid for %@"), old_item
);
1174 CFReleaseNull(localError
); // Clear the error, since we replaced the item.
1177 ok
&= query_destroy(query
, error
);
1180 ok
= false; // We didn't clear the error so we're failing again.
1185 CFErrorRef e = localError ? localError : error ? *error : NULL;
1186 secerror("INSERT %@ failed: %@%s", item, e, (e && CFErrorGetCode(e) == SQLITE_CONSTRAINT) ?
1187 " and SELECT failed to find tombstone" : "");
1189 secerror("INSERT failed: %@", localError
);
1191 if (error
&& *error
== NULL
) {
1192 *error
= localError
;
1195 CFReleaseNull(localError
);
1203 bool SecDbItemInsert(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1204 return SecDbItemInsertOrReplace(item
, dbconn
, error
, ^(SecDbItemRef old_item
, SecDbItemRef
*replace
) {
1205 if (SecDbItemIsTombstone(old_item
)) {
1212 static CFStringRef
SecDbItemCopyUpdateSQL(SecDbItemRef old_item
, SecDbItemRef new_item
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1213 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(new_item
), 0);
1214 CFStringAppend(sql
, CFSTR("UPDATE "));
1215 CFStringAppend(sql
, new_item
->class->name
);
1216 CFStringAppend(sql
, CFSTR(" SET "));
1217 bool needComma
= false;
1218 CFIndex used_attr
= 0;
1219 SecDbForEachAttrWithMask(new_item
->class, attr
, kSecDbInFlag
) {
1221 SecDbAppendElementEquals(sql
, attr
->name
, &needComma
);
1224 bool needWhere
= true;
1225 SecDbForEachAttr(old_item
->class, attr
) {
1226 if (use_attr_in_where(attr
)) {
1227 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1234 static bool SecDbItemUpdateBind(SecDbItemRef old_item
, SecDbItemRef new_item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1237 SecDbForEachAttrWithMask(new_item
->class, attr
, kSecDbInFlag
) {
1238 CFTypeRef value
= SecDbItemGetValue(new_item
, attr
, error
);
1239 ok
&= value
&& SecDbAttrBind(attr
, stmt
, ++param
, value
, error
);
1243 SecDbForEachAttr(old_item
->class, attr
) {
1244 if (use_attr_in_where(attr
)) {
1245 CFTypeRef value
= SecDbItemGetValue(old_item
, attr
, error
);
1246 ok
&= value
&& SecDbAttrBind(attr
, stmt
, ++param
, value
, error
);
1254 // Primary keys are the same -- do an update
1255 bool SecDbItemDoUpdate(SecDbItemRef old_item
, SecDbItemRef new_item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1256 CFStringRef sql
= SecDbItemCopyUpdateSQL(old_item
, new_item
, use_attr_in_where
);
1257 __block
bool ok
= sql
;
1259 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1260 ok
= SecDbItemUpdateBind(old_item
, new_item
, stmt
, error
, use_attr_in_where
) && SecDbStep(dbconn
, stmt
, error
, NULL
);
1265 secnotice("item", "replaced %@ with %@ in %@", old_item
, new_item
, dbconn
);
1269 static CFStringRef
SecDbItemCopyDeleteSQL(SecDbItemRef item
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1270 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(item
), 0);
1271 CFStringAppend(sql
, CFSTR("DELETE FROM "));
1272 CFStringAppend(sql
, item
->class->name
);
1273 bool needWhere
= true;
1274 SecDbForEachAttr(item
->class, attr
) {
1275 if (use_attr_in_where(attr
)) {
1276 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1283 static bool SecDbItemDeleteBind(SecDbItemRef item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1286 SecDbForEachAttr(item
->class, attr
) {
1287 if (use_attr_in_where(attr
)) {
1288 CFTypeRef value
= SecDbItemGetValue(item
, attr
, error
);
1289 ok
&= value
&& SecDbAttrBind(attr
, stmt
, ++param
, value
, error
);
1297 static bool SecDbItemDoDelete(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1298 CFStringRef sql
= SecDbItemCopyDeleteSQL(item
, use_attr_in_where
);
1299 __block
bool ok
= sql
;
1301 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1302 ok
= SecDbItemDeleteBind(item
, stmt
, error
, use_attr_in_where
) && SecDbStep(dbconn
, stmt
, error
, NULL
);
1307 secnotice("item", "deleted %@ from %@", item
, dbconn
);
1312 static bool SecDbItemDeleteTombstone(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1314 // TODO: Treat non decryptable items like tombstones here too and delete them
1315 SecDbItemRef tombstone
= SecDbItemCopyTombstone(item
, error
);
1318 ok
= SecDbItemClearRowId(tombstone
, error
);
1320 ok
= SecDbItemDoDelete(tombstone
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1321 return SecDbIsTombstoneDbSelectAttr(attr
);
1324 CFRelease(tombstone
);
1330 // Replace old_item with new_item. If primary keys are the same this does an update otherwise it does a delete + add
1331 bool SecDbItemUpdate(SecDbItemRef old_item
, SecDbItemRef new_item
, SecDbConnectionRef dbconn
, bool makeTombstone
, CFErrorRef
*error
) {
1332 __block
bool ok
= true;
1333 __block CFErrorRef localError
= NULL
;
1335 CFDataRef old_pk
= SecDbItemGetPrimaryKey(old_item
, error
);
1336 CFDataRef new_pk
= SecDbItemGetPrimaryKey(new_item
, error
);
1338 ok
= old_pk
&& new_pk
;
1340 bool pk_equal
= ok
&& CFEqual(old_pk
, new_pk
);
1342 ok
= SecDbItemMakeYounger(new_item
, old_item
, error
);
1344 ok
= ok
&& SecDbItemDoUpdate(old_item
, new_item
, dbconn
, &localError
, ^bool(const SecDbAttr
*attr
) {
1345 return attr
->kind
== kSecDbRowIdAttr
;
1349 if(CFErrorGetCode(localError
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(localError
))) {
1350 /* Update failed because we changed the PrimaryKey and there was a dup.
1351 Find the dup and see if it is a tombstone or corrupted item. */
1352 SecDbQueryRef query
= SecDbQueryCreateWithItemPrimaryKey(new_item
, error
);
1355 ok
&= SecDbItemSelect(query
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1356 return attr
->flags
& kSecDbPrimaryKeyFlag
;
1357 }, NULL
, NULL
, ^(SecDbItemRef duplicate_item
, bool *stop
) {
1358 bool is_corrupt
= false;
1359 bool is_tomb
= false;
1360 ok
= SecDbItemIsCorrupt(duplicate_item
, &is_corrupt
, error
);
1361 if (ok
&& !is_corrupt
) {
1362 if ((is_tomb
= SecDbItemIsTombstone(duplicate_item
)))
1363 ok
= SecDbItemMakeYounger(new_item
, duplicate_item
, error
);
1365 if (ok
&& (is_corrupt
|| is_tomb
)) {
1366 ok
= SecDbItemDoDelete(old_item
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1367 return attr
->kind
== kSecDbRowIdAttr
;
1369 ok
= ok
&& SecDbItemDoUpdate(duplicate_item
, new_item
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1370 return attr
->kind
== kSecDbRowIdAttr
;
1372 CFReleaseNull(localError
);
1375 ok
&= query_destroy(query
, error
);
1381 if (error
&& *error
== NULL
) {
1382 *error
= localError
;
1385 CFReleaseSafe(localError
);
1389 if (ok
&& !pk_equal
&& makeTombstone
) {
1390 /* The primary key of new_item is different than that of old_item, we
1391 have been asked to make a tombstone so leave one for the old_item. */
1392 SecDbItemRef tombstone
= SecDbItemCopyTombstone(old_item
, error
);
1395 ok
= (SecDbItemClearRowId(tombstone
, error
) &&
1396 SecDbItemDoInsert(tombstone
, dbconn
, error
));
1397 CFRelease(tombstone
);
1404 // Replace the object with a tombstone
1405 bool SecDbItemDelete(SecDbItemRef item
, SecDbConnectionRef dbconn
, bool makeTombstone
, CFErrorRef
*error
) {
1407 if (makeTombstone
) {
1408 SecDbItemRef tombstone
= SecDbItemCopyTombstone(item
, error
);
1410 ok
= SecDbItemDoUpdate(item
, tombstone
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1411 return attr
->kind
== kSecDbRowIdAttr
;
1413 CFRelease(tombstone
);
1416 ok
= SecDbItemDoDelete(item
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1417 return attr
->kind
== kSecDbRowIdAttr
;
1423 CFStringRef
SecDbItemCopySelectSQL(SecDbQueryRef query
,
1424 bool (^return_attr
)(const SecDbAttr
*attr
),
1425 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1426 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
)) {
1427 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1428 CFStringAppend(sql
, CFSTR("SELECT "));
1429 // What are we selecting?
1430 bool needComma
= false;
1431 SecDbForEachAttr(query
->q_class
, attr
) {
1432 if (return_attr(attr
))
1433 SecDbAppendElement(sql
, attr
->name
, &needComma
);
1436 // From which table?
1437 CFStringAppend(sql
, CFSTR(" FROM "));
1438 CFStringAppend(sql
, query
->q_class
->name
);
1440 // And which elements do we want to select
1441 bool needWhere
= true;
1442 SecDbForEachAttr(query
->q_class
, attr
) {
1443 if (use_attr_in_where(attr
)) {
1444 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1447 // Append SQL for access groups and limits.
1449 add_where_sql(sql
, &needWhere
);
1454 bool SecDbItemSelectBind(SecDbQueryRef query
, sqlite3_stmt
*stmt
, CFErrorRef
*error
,
1455 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1456 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
)) {
1459 SecDbForEachAttr(query
->q_class
, attr
) {
1460 if (use_attr_in_where(attr
)) {
1461 CFTypeRef value
= CFDictionaryGetValue(query
->q_item
, attr
->name
);
1462 ok
&= SecDbAttrBind(attr
, stmt
, ++param
, value
, error
);
1467 // TODO: Bind arguments for access groups and limits.
1468 if (bind_added_where
)
1469 bind_added_where(stmt
, ++param
);
1474 bool SecDbItemSelect(SecDbQueryRef query
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
1475 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1476 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
),
1477 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
),
1478 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
1479 __block
bool ok
= true;
1480 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
1481 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
;
1483 CFStringRef sql
= SecDbItemCopySelectSQL(query
, return_attr
, use_attr_in_where
, add_where_sql
);
1485 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1486 ok
= (SecDbItemSelectBind(query
, stmt
, error
, use_attr_in_where
, bind_added_where
) &&
1487 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
1488 SecDbItemRef item
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
1490 handle_row(item
, stop
);