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.)
31 #undef SECUREOBJECTSYNC
32 #undef SHAREDWEBCREDENTIALS
35 #include "keychain/securityd/SecDbItem.h"
36 #include "keychain/securityd/SecDbKeychainItem.h"
37 #include "keychain/securityd/SecItemDb.h"
38 #include <utilities/SecCFWrappers.h>
39 #include <utilities/SecCFCCWrappers.h>
40 #include <utilities/der_date.h>
41 #include <utilities/der_plist.h>
42 #include <utilities/debugging.h>
44 #include <Security/SecBasePriv.h>
45 #include <Security/SecInternal.h>
46 #include <corecrypto/ccsha1.h>
47 #include <Security/SecItem.h>
48 #include <Security/SecItemPriv.h>
49 #include <Security/SecAccessControl.h>
50 #include <Security/SecAccessControlPriv.h>
51 #include "keychain/securityd/SecItemSchema.h"
53 #include <keychain/ckks/CKKS.h>
55 // MARK: type converters
57 CFStringRef
copyString(CFTypeRef obj
) {
58 CFTypeID tid
= CFGetTypeID(obj
);
59 if (tid
== CFStringGetTypeID()) {
60 return CFStringCreateCopy(0, obj
);
61 }else if (tid
== CFDataGetTypeID()) {
62 return CFStringCreateFromExternalRepresentation(0, obj
, kCFStringEncodingUTF8
);
63 } else if (tid
== CFUUIDGetTypeID()) {
64 return CFUUIDCreateString(NULL
, obj
);
70 CFDataRef
copyData(CFTypeRef obj
) {
71 CFTypeID tid
= CFGetTypeID(obj
);
72 if (tid
== CFDataGetTypeID()) {
73 return CFDataCreateCopy(0, obj
);
74 } else if (tid
== CFStringGetTypeID()) {
75 return CFStringCreateExternalRepresentation(0, obj
, kCFStringEncodingUTF8
, 0);
76 } else if (tid
== CFNumberGetTypeID()) {
78 CFNumberGetValue(obj
, kCFNumberSInt32Type
, &value
);
79 return CFDataCreate(0, (const UInt8
*)&value
, sizeof(value
));
85 CFTypeRef
copyUUID(CFTypeRef obj
) {
86 CFTypeID tid
= CFGetTypeID(obj
);
87 if (tid
== CFDataGetTypeID()) {
88 CFIndex length
= CFDataGetLength(obj
);
89 if (length
!= 0 && length
!= 16)
91 return CFDataCreateCopy(NULL
, obj
);
92 } else if (tid
== CFNullGetTypeID()) {
93 return CFDataCreate(NULL
, NULL
, 0);
94 } else if (tid
== CFUUIDGetTypeID()) {
95 CFUUIDBytes uuidbytes
= CFUUIDGetUUIDBytes(obj
);
96 CFDataRef uuiddata
= CFDataCreate(NULL
, (void*) &uuidbytes
, sizeof(uuidbytes
));
104 CFTypeRef
copyBlob(CFTypeRef obj
) {
105 CFTypeID tid
= CFGetTypeID(obj
);
106 if (tid
== CFDataGetTypeID()) {
107 return CFDataCreateCopy(0, obj
);
108 } else if (tid
== CFStringGetTypeID()) {
109 return CFStringCreateCopy(0, obj
);
110 } else if (tid
== CFNumberGetTypeID()) {
118 CFDataRef
copySHA1(CFTypeRef obj
) {
119 CFTypeID tid
= CFGetTypeID(obj
);
120 if (tid
== CFDataGetTypeID() && CFDataGetLength(obj
) == CCSHA1_OUTPUT_SIZE
) {
121 return CFDataCreateCopy(CFGetAllocator(obj
), obj
);
127 CFTypeRef
copyNumber(CFTypeRef obj
) {
128 CFTypeID tid
= CFGetTypeID(obj
);
129 if (tid
== CFNumberGetTypeID()) {
132 } else if (tid
== CFBooleanGetTypeID()) {
133 SInt32 value
= CFBooleanGetValue(obj
);
134 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
135 } else if (tid
== CFStringGetTypeID()) {
136 SInt32 value
= CFStringGetIntValue(obj
);
137 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value
);
138 /* If a string converted to an int isn't equal to the int printed as
139 a string, return a CFStringRef instead. */
140 if (!CFEqual(t
, obj
)) {
142 return CFStringCreateCopy(0, obj
);
145 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
150 CFDateRef
copyDate(CFTypeRef obj
) {
151 CFTypeID tid
= CFGetTypeID(obj
);
152 if (tid
== CFDateGetTypeID()) {
159 // MARK: SecDbColumn accessors, to retrieve values as CF types in SecDbStep.
161 static CFDataRef
SecDbColumnCopyData(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
162 return CFDataCreate(allocator
, sqlite3_column_blob(stmt
, col
),
163 sqlite3_column_bytes(stmt
, col
));
164 //return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col),
165 // sqlite3_column_bytes(stmt, col),
166 // kCFAllocatorNull);
169 static CFDateRef
SecDbColumnCopyDate(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
170 return CFDateCreate(allocator
, sqlite3_column_double(stmt
, col
));
173 static CFNumberRef
SecDbColumnCopyDouble(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
174 double number
= sqlite3_column_double(stmt
, col
);
175 return CFNumberCreate(allocator
, kCFNumberDoubleType
, &number
);
178 static CFNumberRef
SecDbColumnCopyNumber64(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
179 sqlite_int64 number
= sqlite3_column_int64(stmt
, col
);
180 return CFNumberCreate(allocator
, kCFNumberSInt64Type
, &number
);
183 static CFNumberRef
SecDbColumnCopyNumber(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
184 sqlite_int64 number
= sqlite3_column_int64(stmt
, col
);
185 if (INT32_MIN
<= number
&& number
<= INT32_MAX
) {
186 int32_t num32
= (int32_t)number
;
187 return CFNumberCreate(allocator
, kCFNumberSInt32Type
, &num32
);
189 return CFNumberCreate(allocator
, kCFNumberSInt64Type
, &number
);
193 static CFTypeRef
SecDbColumnCopyString(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
,
194 CFOptionFlags flags
) {
195 const unsigned char *text
= sqlite3_column_text(stmt
, col
);
196 if (!text
|| 0 == strlen((const char *)text
)) {
197 if (flags
& kSecDbDefaultEmptyFlag
) {
199 } else if (flags
& kSecDbDefault0Flag
) {
205 return CFStringCreateWithBytes(allocator
, text
, strlen((const char *)text
), kCFStringEncodingUTF8
, false);
208 // MARK: SecDbClass helpers
210 const SecDbAttr
*SecDbClassAttrWithKind(const SecDbClass
*class, SecDbAttrKind kind
, CFErrorRef
*error
) {
211 const SecDbAttr
*result
= NULL
;
212 SecDbForEachAttr(class, desc
) {
213 if (desc
->kind
== kind
)
218 SecError(errSecInternal
, error
, CFSTR("Can't find attribute of kind %d in class %@"), kind
, class->name
);
223 // MARK: SecDbAttr helpers
225 static bool SecDbIsTombstoneDbSelectAttr(const SecDbAttr
*attr
) {
226 return attr
->flags
& kSecDbPrimaryKeyFlag
|| attr
->kind
== kSecDbTombAttr
;
230 static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr
*attr
) {
231 return SecDbIsTombstoneDbSelectAttr(attr
) || attr
->kind
== kSecDbAccessAttr
|| attr
->kind
== kSecDbCreationDateAttr
|| attr
->kind
== kSecDbModificationDateAttr
;
235 static bool SecDbIsTombstoneDbUpdateAttr(const SecDbAttr
*attr
) {
236 // We add AuthenticatedData to include UUIDs, which can't be primary keys
237 return SecDbIsTombstoneDbSelectAttr(attr
) || attr
->kind
== kSecDbAccessAttr
|| attr
->kind
== kSecDbCreationDateAttr
|| attr
->kind
== kSecDbRowIdAttr
|| (attr
->flags
& kSecDbInAuthenticatedDataFlag
);
240 CFTypeRef
SecDbAttrCopyDefaultValue(const SecDbAttr
*attr
, CFErrorRef
*error
) {
241 CFTypeRef value
= NULL
;
242 switch (attr
->kind
) {
243 case kSecDbAccessAttr
:
244 case kSecDbStringAttr
:
245 case kSecDbAccessControlAttr
:
250 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
253 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
255 case kSecDbNumberAttr
:
260 value
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &zero
);
264 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
266 case kSecDbCreationDateAttr
:
267 case kSecDbModificationDateAttr
:
268 value
= CFDateCreate(kCFAllocatorDefault
, CFAbsoluteTimeGetCurrent());
271 SecError(errSecInternal
, error
, CFSTR("attr %@ has no default value"), attr
->name
);
278 static CFTypeRef
SecDbAttrCopyValueForDb(const SecDbAttr
*attr
, CFTypeRef value
, CFErrorRef
*error
) {
279 CFDataRef data
= NULL
;
280 CFTypeRef result
= NULL
;
285 if (CFEqual(value
, kCFNull
) && attr
->flags
& kSecDbPrimaryKeyFlag
) {
286 // SQLITE3 doesn't like NULL for primary key attributes, pretend kSecDbDefaultEmptyFlag was specified
287 require_quiet(result
= SecDbAttrCopyDefaultValue(attr
, error
), out
);
289 result
= CFRetain(value
);
292 if (attr
->flags
& kSecDbSHA1ValueInFlag
&& !CFEqual(result
, kCFNull
)) {
293 require_action_quiet(data
= copyData(result
), out
,
294 SecError(errSecInternal
, error
, CFSTR("failed to get attribute %@ data"), attr
->name
);
295 CFReleaseNull(result
));
296 CFAssignRetained(result
, CFDataCopySHA1Digest(data
, error
));
304 static CFStringRef
SecDbAttrGetHashName(const SecDbAttr
*attr
) {
305 if ((attr
->flags
& kSecDbSHA1ValueInFlag
) == 0) {
309 static dispatch_once_t once
;
310 static CFMutableDictionaryRef hash_store
;
311 static dispatch_queue_t queue
;
312 dispatch_once(&once
, ^{
313 queue
= dispatch_queue_create("secd-hash-name", NULL
);
314 hash_store
= CFDictionaryCreateMutableForCFTypes(NULL
);
317 __block CFStringRef name
;
318 dispatch_sync(queue
, ^{
319 name
= CFDictionaryGetValue(hash_store
, attr
->name
);
321 name
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("#%@"), attr
->name
);
322 CFDictionarySetValue(hash_store
, attr
->name
, name
);
331 CFTypeRef
SecDbItemGetCachedValueWithName(SecDbItemRef item
, CFStringRef name
) {
332 return CFDictionaryGetValue(item
->attributes
, name
);
335 static CFTypeRef
SecDbItemGetCachedValue(SecDbItemRef item
, const SecDbAttr
*desc
) {
336 return CFDictionaryGetValue(item
->attributes
, desc
->name
);
339 CFMutableDictionaryRef
SecDbItemCopyPListWithMask(SecDbItemRef item
, CFOptionFlags mask
, CFErrorRef
*error
) {
340 return SecDbItemCopyPListWithFlagAndSkip(item
, mask
, 0, error
);
343 CFMutableDictionaryRef
SecDbItemCopyPListWithFlagAndSkip(SecDbItemRef item
,
345 CFOptionFlags flagsToSkip
,
348 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
349 SecDbForEachAttrWithMask(item
->class, desc
, mask
) {
350 if((desc
->flags
& flagsToSkip
) != 0) {
354 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
356 if (!CFEqual(kCFNull
, value
)) {
357 CFDictionarySetValue(dict
, desc
->name
, value
);
358 } else if (desc
->flags
& kSecDbNotNullFlag
) {
359 SecError(errSecDecode
, error
, CFSTR("attribute %@ has NULL value"), desc
->name
);
360 secerror("%@", error
? *error
: (CFErrorRef
)CFSTR("error == NULL"));
372 void SecDbItemSetCredHandle(SecDbItemRef item
, CFTypeRef cred_handle
) {
373 CFRetainAssign(item
->credHandle
, cred_handle
);
376 void SecDbItemSetCallerAccessGroups(SecDbItemRef item
, CFArrayRef caller_access_groups
) {
377 CFRetainAssign(item
->callerAccessGroups
, caller_access_groups
);
380 CFDataRef
SecDbItemCopyEncryptedDataToBackup(SecDbItemRef item
, uint64_t handle
, CFErrorRef
*error
) {
381 CFDataRef edata
= NULL
;
382 keybag_handle_t keybag
= (keybag_handle_t
)handle
;
383 CFMutableDictionaryRef attributes
= SecDbItemCopyPListWithMask(item
, kSecDbInCryptoDataFlag
, error
);
384 CFMutableDictionaryRef auth_attributes
= SecDbItemCopyPListWithMask(item
, kSecDbInAuthenticatedDataFlag
, error
);
385 if (attributes
|| auth_attributes
) {
386 SecAccessControlRef access_control
= SecDbItemCopyAccessControl(item
, error
);
387 if (access_control
) {
388 if (ks_encrypt_data_legacy(keybag
, access_control
, item
->credHandle
, attributes
, auth_attributes
, &edata
, false, error
)) {
389 item
->_edataState
= kSecDbItemEncrypting
;
391 seccritical("ks_encrypt_data (db): failed: %@", error
? *error
: (CFErrorRef
)CFSTR(""));
393 CFRelease(access_control
);
395 CFReleaseNull(attributes
);
396 CFReleaseNull(auth_attributes
);
402 bool SecDbItemEnsureDecrypted(SecDbItemRef item
, bool decryptSecretData
, CFErrorRef
*error
) {
404 // If we haven't yet decrypted the item, make sure we do so now
406 if (item
->_edataState
== kSecDbItemEncrypted
|| (decryptSecretData
&& item
->_edataState
== kSecDbItemSecretEncrypted
)) {
407 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, error
);
409 CFDataRef edata
= SecDbItemGetCachedValue(item
, attr
);
411 return SecError(errSecInternal
, error
, CFSTR("state= encrypted but edata is NULL"));
412 // Decrypt calls set value a bunch of times which clears our edata and changes our state.
413 item
->_edataState
= kSecDbItemDecrypting
;
414 result
= SecDbItemDecrypt(item
, decryptSecretData
, edata
, error
);
416 item
->_edataState
= decryptSecretData
? kSecDbItemClean
: kSecDbItemSecretEncrypted
;
418 item
->_edataState
= kSecDbItemEncrypted
;
424 // Only called if cached value is not found.
425 static CFTypeRef
SecDbItemCopyValue(SecDbItemRef item
, const SecDbAttr
*attr
, CFErrorRef
*error
) {
426 if (attr
->copyValue
) {
427 return attr
->copyValue(item
, attr
, error
);
430 CFTypeRef value
= NULL
;
431 switch (attr
->kind
) {
432 // These have an explicit copyValue; here to shut up compiler
434 case kSecDbEncryptedDataAttr
:
435 case kSecDbPrimaryKeyAttr
:
438 case kSecDbAccessAttr
:
439 case kSecDbStringAttr
:
441 case kSecDbAccessControlAttr
:
442 if (attr
->flags
& kSecDbNotNullFlag
) {
443 if (attr
->flags
& kSecDbDefault0Flag
) {
446 } else if (attr
->kind
!= kSecDbBlobAttr
&& attr
->flags
& kSecDbDefaultEmptyFlag
) {
447 // blob drops through to data everything else is empty string
454 if (attr
->flags
& kSecDbNotNullFlag
&& attr
->flags
& kSecDbDefaultEmptyFlag
) {
455 value
= CFDataCreate(CFGetAllocator(item
), NULL
, 0);
461 value
= CFDataCreate(CFGetAllocator(item
), NULL
, 0);
463 case kSecDbNumberAttr
:
466 if (attr
->flags
& kSecDbNotNullFlag
) {
468 value
= CFNumberCreate(CFGetAllocator(item
), kCFNumberSInt32Type
, &zero
);
474 if (attr
->flags
& kSecDbNotNullFlag
&& attr
->flags
& kSecDbDefault0Flag
) {
475 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
480 case kSecDbRowIdAttr
:
481 if (attr
->flags
& kSecDbNotNullFlag
) {
486 case kSecDbCreationDateAttr
:
487 case kSecDbModificationDateAttr
:
488 value
= CFDateCreate(CFGetAllocator(item
), CFAbsoluteTimeGetCurrent());
490 case kSecDbUTombAttr
:
498 // SecDbItemGetValue will return kCFNull if there is no value for an attribute and this was not
499 // an error. It will return NULL and optionally set *error if there was an error computing an
500 // attribute, or if a required attribute was missing a value and had no known way to compute
502 CFTypeRef
SecDbItemGetValue(SecDbItemRef item
, const SecDbAttr
*desc
, CFErrorRef
*error
) {
503 // Propagate chained errors
507 if (desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
|| desc
->flags
& kSecDbReturnDataFlag
) {
508 if (!SecDbItemEnsureDecrypted(item
, desc
->flags
& kSecDbReturnDataFlag
, error
))
512 CFTypeRef value
= SecDbItemGetCachedValue(item
, desc
);
514 value
= SecDbItemCopyValue(item
, desc
, error
);
516 if (CFEqual(kCFNull
, value
)) {
517 CFRelease(value
); // This is redundant but it shuts clang's static analyzer up.
520 SecDbItemSetValue(item
, desc
, value
, error
);
522 value
= SecDbItemGetCachedValue(item
, desc
);
529 CFTypeRef
SecDbItemGetValueKind(SecDbItemRef item
, SecDbAttrKind descKind
, CFErrorRef
*error
) {
530 CFTypeRef result
= NULL
;
532 const SecDbClass
* itemClass
= SecDbItemGetClass(item
);
533 const SecDbAttr
* desc
= SecDbClassAttrWithKind(itemClass
, descKind
, error
);
536 result
= SecDbItemGetValue(item
, desc
, error
);
543 // Similar as SecDbItemGetValue, but if attr represents attribute stored into DB field as hash, returns
544 // hashed value for the attribute.
545 static CFTypeRef
SecDbItemCopyValueForDb(SecDbItemRef item
, const SecDbAttr
*desc
, CFErrorRef
*error
) {
546 CFTypeRef value
= NULL
;
547 CFStringRef hash_name
= NULL
;
548 hash_name
= SecDbAttrGetHashName(desc
);
549 if ((desc
->flags
& kSecDbSHA1ValueInFlag
) && (desc
->flags
& kSecDbInFlag
)) {
550 value
= CFRetainSafe(CFDictionaryGetValue(item
->attributes
, hash_name
));
554 require_quiet(value
= SecDbItemGetValue(item
, desc
, error
), out
);
555 require_action_quiet(value
= SecDbAttrCopyValueForDb(desc
, value
, error
), out
, CFReleaseNull(value
));
556 if ((desc
->flags
& kSecDbSHA1ValueInFlag
) != 0) {
557 CFDictionarySetValue(item
->attributes
, hash_name
, value
);
565 static bool SecDbItemGetBoolValue(SecDbItemRef item
, const SecDbAttr
*desc
, bool *bvalue
, CFErrorRef
*error
) {
566 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
570 *bvalue
= (isNumber(value
) && CFNumberGetValue(value
, kCFNumberCharType
, &cvalue
) && cvalue
== 1);
574 static void SecDbItemAppendAttributeToDescription(SecDbItemRef item
, const SecDbAttr
*attr
, CFMutableStringRef mdesc
)
576 // In non-debug builds, the following attributes aren't very useful.
578 if (CFEqual(CFSTR("data"), attr
->name
)||
579 CFEqual(CFSTR("v_pk"), attr
->name
)) {
584 CFTypeRef value
= SecDbItemGetValue(item
, attr
, NULL
);
585 if (value
&& value
!= kCFNull
) {
586 CFStringAppend(mdesc
, CFSTR(","));
587 CFStringAppend(mdesc
, attr
->name
);
588 CFStringAppend(mdesc
, CFSTR("="));
589 if (CFEqual(CFSTR("data"), attr
->name
)) {
590 CFStringAppendEncryptedData(mdesc
, value
);
591 } else if (CFEqual(CFSTR("v_Data"), attr
->name
)) {
592 CFStringAppend(mdesc
, CFSTR("<?>"));
593 } else if (isData(value
)) {
594 CFStringAppendHexData(mdesc
, value
);
596 CFStringAppendFormat(mdesc
, 0, CFSTR("%@"), value
);
601 static CFStringRef
SecDbItemCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
603 if (isDictionary(formatOptions
) && CFDictionaryContainsKey(formatOptions
, kSecDebugFormatOption
)) {
604 SecDbItemRef item
= (SecDbItemRef
)cf
;
605 CFMutableStringRef mdesc
= CFStringCreateMutable(CFGetAllocator(cf
), 0);
606 CFStringAppendFormat(mdesc
, NULL
, CFSTR("<%@"), item
->class->name
);
608 // First, the primary key attributes
609 SecDbForEachAttrWithMask(item
->class, attr
, kSecDbPrimaryKeyFlag
) {
610 SecDbItemAppendAttributeToDescription(item
, attr
, mdesc
);
613 CFStringAppend(mdesc
, CFSTR(", |otherAttr"));
614 // tombstone values are very important, and should print next
615 SecDbForEachAttr(item
->class, attr
) {
616 if(CFEqualSafe(CFSTR("tomb"), attr
->name
)) {
617 SecDbItemAppendAttributeToDescription(item
, attr
, mdesc
);
621 // And finally, everything else
622 SecDbForEachAttr(item
->class, attr
) {
623 if((attr
->flags
& kSecDbPrimaryKeyFlag
) != 0) {
626 if(CFEqualSafe(CFSTR("tomb"), attr
->name
)) {
629 SecDbItemAppendAttributeToDescription(item
, attr
, mdesc
);
631 CFStringAppend(mdesc
, CFSTR(">"));
634 SecDbItemRef item
= (SecDbItemRef
)cf
;
635 const UInt8 zero4
[4] = {};
636 const UInt8
*pk
= &zero4
[0], *sha1
= &zero4
[0];
640 CFStringRef access
= NULL
;
641 uint8_t mdatbuf
[32] = {};
642 uint8_t *mdat
= &mdatbuf
[0];
643 CFMutableStringRef attrs
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
644 CFStringRef agrp
= NULL
;
645 CFBooleanRef utomb
= NULL
;
647 SecDbForEachAttr(item
->class, attr
) {
649 switch (attr
->kind
) {
652 case kSecDbStringAttr
:
653 case kSecDbNumberAttr
:
655 case kSecDbEncryptedDataAttr
:
656 if (attr
->flags
& (kSecDbReturnAttrFlag
| kSecDbReturnDataFlag
) && (value
= SecDbItemGetValue(item
, attr
, NULL
)) && !CFEqual(value
, kCFNull
)) {
657 if (isString(value
) && CFEqual(attr
->name
, kSecAttrAccessGroup
)) {
660 // We don't log these, just record that we saw the attribute.
661 CFStringAppend(attrs
, CFSTR(","));
662 CFStringAppend(attrs
, attr
->name
);
667 if ((value
= SecDbItemGetValue(item
, attr
, NULL
))) {
668 if (CFEqual(attr
->name
, kSecAttrMultiUser
)) {
670 CFStringAppend(attrs
, CFSTR(","));
671 if (CFDataGetLength(value
)) {
672 CFStringAppendHexData(attrs
, value
);
674 CFStringAppend(attrs
, attr
->name
);
680 case kSecDbCreationDateAttr
:
681 // We don't care about this and every object has one.
683 case kSecDbModificationDateAttr
:
684 value
= SecDbItemGetValue(item
, attr
, NULL
);
686 mdat
= der_encode_generalizedtime_body(CFDateGetAbsoluteTime(value
), NULL
, mdat
, &mdatbuf
[31]);
689 value
= SecDbItemGetValue(item
, attr
, NULL
);
690 if (isData(value
) && CFDataGetLength(value
) >= (CFIndex
)sizeof(zero4
))
691 sha1
= CFDataGetBytePtr(value
);
693 case kSecDbRowIdAttr
:
694 value
= SecDbItemGetValue(item
, attr
, NULL
);
696 CFNumberGetValue(value
, kCFNumberSInt64Type
, &rowid
);
698 case kSecDbPrimaryKeyAttr
:
699 value
= SecDbItemGetValue(item
, attr
, NULL
);
701 pk
= CFDataGetBytePtr(value
);
704 value
= SecDbItemGetValue(item
, attr
, NULL
);
706 CFNumberGetValue(value
, kCFNumberCharType
, &sync
);
709 value
= SecDbItemGetValue(item
, attr
, NULL
);
711 CFNumberGetValue(value
, kCFNumberCharType
, &tomb
);
713 case kSecDbAccessAttr
:
714 value
= SecDbItemGetValue(item
, attr
, NULL
);
718 case kSecDbUTombAttr
:
719 value
= SecDbItemGetValue(item
, attr
, NULL
);
720 if (isBoolean(value
))
722 case kSecDbAccessControlAttr
:
723 /* TODO: Add formatting of ACLs. */
728 desc
= CFStringCreateWithFormat(CFGetAllocator(cf
), NULL
,
743 pk
[0], pk
[1], pk
[2], pk
[3],
750 utomb
? (CFEqual(utomb
, kCFBooleanFalse
) ? "F," : "T,") : "",
751 sha1
[0], sha1
[1], sha1
[2], sha1
[3]);
752 CFReleaseSafe(attrs
);
758 static void SecDbItemDestroy(CFTypeRef cf
) {
759 SecDbItemRef item
= (SecDbItemRef
)cf
;
760 CFReleaseSafe(item
->attributes
);
761 CFReleaseSafe(item
->credHandle
);
762 CFReleaseSafe(item
->callerAccessGroups
);
763 CFReleaseSafe(item
->cryptoOp
);
766 static CFHashCode
SecDbItemHash(CFTypeRef cf
) {
767 SecDbItemRef item
= (SecDbItemRef
)cf
;
768 CFDataRef digest
= SecDbItemGetSHA1(item
, NULL
);
770 const UInt8
*p
= CFDataGetBytePtr(digest
);
771 // Read first 8 bytes of digest in order
772 code
= p
[0] + ((p
[1] + ((p
[2] + ((p
[3] + ((p
[4] + ((p
[5] + ((p
[6] + (p
[7] << 8)) << 8)) << 8)) << 8)) << 8)) << 8)) << 8);
776 static Boolean
SecDbItemCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
777 SecDbItemRef item1
= (SecDbItemRef
)cf1
;
778 SecDbItemRef item2
= (SecDbItemRef
)cf2
;
779 CFDataRef digest1
= NULL
;
780 CFDataRef digest2
= NULL
;
782 digest1
= SecDbItemGetSHA1(item1
, NULL
);
784 digest2
= SecDbItemGetSHA1(item2
, NULL
);
785 Boolean equal
= CFEqual(digest1
, digest2
);
789 CFGiblisWithHashFor(SecDbItem
)
791 static SecDbItemRef
SecDbItemCreate(CFAllocatorRef allocator
, const SecDbClass
*class, keybag_handle_t keybag
) {
792 SecDbItemRef item
= CFTypeAllocate(SecDbItem
, struct SecDbItem
, allocator
);
794 item
->attributes
= CFDictionaryCreateMutableForCFTypes(allocator
);
795 item
->keybag
= keybag
;
796 item
->_edataState
= kSecDbItemDirty
;
797 item
->cryptoOp
= kAKSKeyOpDecrypt
;
802 const SecDbClass
*SecDbItemGetClass(SecDbItemRef item
) {
806 keybag_handle_t
SecDbItemGetKeybag(SecDbItemRef item
) {
810 bool SecDbItemSetKeybag(SecDbItemRef item
, keybag_handle_t keybag
, CFErrorRef
*error
) {
811 if (!SecDbItemEnsureDecrypted(item
, true, error
))
813 if (item
->keybag
!= keybag
) {
814 item
->keybag
= keybag
;
815 if (item
->_edataState
== kSecDbItemClean
) {
816 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, NULL
), kCFNull
, NULL
);
823 bool SecDbItemSetValue(SecDbItemRef item
, const SecDbAttr
*desc
, CFTypeRef value
, CFErrorRef
*error
) {
824 // Propagate chained errors.
832 return desc
->setValue(item
, desc
, value
, error
);
834 if (desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
) {
835 if (!SecDbItemEnsureDecrypted(item
, true, error
)) {
840 bool changed
= false;
841 CFTypeRef attr
= NULL
;
842 switch (desc
->kind
) {
843 case kSecDbPrimaryKeyAttr
:
845 attr
= copyData(value
);
847 case kSecDbEncryptedDataAttr
:
848 attr
= copyData(value
);
850 if (item
->_edataState
== kSecDbItemEncrypting
)
851 item
->_edataState
= kSecDbItemClean
;
853 item
->_edataState
= kSecDbItemEncrypted
;
854 } else if (!value
|| CFEqual(kCFNull
, value
)) {
855 item
->_edataState
= kSecDbItemDirty
;
859 case kSecDbAccessControlAttr
:
860 attr
= copyBlob(value
);
863 case kSecDbCreationDateAttr
:
864 case kSecDbModificationDateAttr
:
865 attr
= copyDate(value
);
867 case kSecDbNumberAttr
:
870 case kSecDbRowIdAttr
:
871 attr
= copyNumber(value
);
873 case kSecDbAccessAttr
:
874 case kSecDbStringAttr
:
875 attr
= copyString(value
);
878 attr
= copySHA1(value
);
880 case kSecDbUTombAttr
:
881 attr
= CFRetainSafe(asBoolean(value
, NULL
));
884 attr
= copyUUID(value
);
889 CFTypeRef ovalue
= CFDictionaryGetValue(item
->attributes
, desc
->name
);
890 changed
= (!ovalue
|| !CFEqual(ovalue
, attr
));
891 CFDictionarySetValue(item
->attributes
, desc
->name
, attr
);
894 if (value
&& !CFEqual(kCFNull
, value
)) {
895 SecError(errSecItemInvalidValue
, error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
898 CFTypeRef ovalue
= CFDictionaryGetValue(item
->attributes
, desc
->name
);
899 changed
= (ovalue
&& !CFEqual(ovalue
, kCFNull
));
900 CFDictionaryRemoveValue(item
->attributes
, desc
->name
);
904 if (desc
->flags
& kSecDbInHashFlag
)
905 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
), kCFNull
, NULL
);
906 if (desc
->flags
& kSecDbPrimaryKeyFlag
)
907 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbPrimaryKeyAttr
, NULL
), kCFNull
, NULL
);
908 if ((desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
) && (item
->_edataState
== kSecDbItemClean
|| (item
->_edataState
== kSecDbItemSecretEncrypted
&& (desc
->flags
& kSecDbReturnDataFlag
) == 0)))
909 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, NULL
), kCFNull
, NULL
);
910 if (desc
->flags
& kSecDbSHA1ValueInFlag
)
911 CFDictionaryRemoveValue(item
->attributes
, SecDbAttrGetHashName(desc
));
917 bool SecDbItemSetValues(SecDbItemRef item
, CFDictionaryRef values
, CFErrorRef
*error
) {
918 SecDbForEachAttr(item
->class, attr
) {
919 CFTypeRef value
= CFDictionaryGetValue(values
, attr
->name
);
920 if (value
&& !SecDbItemSetValue(item
, attr
, value
, error
))
926 bool SecDbItemSetValueWithName(SecDbItemRef item
, CFStringRef name
, CFTypeRef value
, CFErrorRef
*error
) {
927 SecDbForEachAttr(item
->class, attr
) {
928 if (CFEqual(attr
->name
, name
)) {
929 return SecDbItemSetValue(item
, attr
, value
, error
);
935 bool SecItemPreserveAttribute(SecDbItemRef target
, SecDbItemRef source
, const SecDbAttr
* attr
) {
936 CFErrorRef cferror
= nil
;
937 CFTypeRef v
= SecDbItemGetValue(source
, attr
, &cferror
);
939 secnotice("secitem", "Merging: unable to get attribute (%@) : %@", attr
->name
, cferror
);
940 CFReleaseNull(cferror
);
943 if(!v
|| CFEqualSafe(v
, kCFNull
)) {
946 secnotice("secitem", "Preserving existing data for %@", attr
->name
);
947 SecDbItemSetValue(target
, attr
, v
, &cferror
);
949 secnotice("secitem", "Unable to set attribute (%@) : %@", attr
->name
, cferror
);
950 CFReleaseNull(cferror
);
957 bool SecDbItemSetAccessControl(SecDbItemRef item
, SecAccessControlRef access_control
, CFErrorRef
*error
) {
959 if (item
->_edataState
== kSecDbItemClean
)
960 ok
= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, error
), kCFNull
, error
);
961 if (ok
&& access_control
) { //added check for access_control because ks_decrypt_data can leave NULL in access_control in case of error
962 item
->_edataState
= kSecDbItemDirty
;
963 CFDataRef data
= SecAccessControlCopyData(access_control
);
964 ok
= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessControlAttr
, error
), data
, error
);
970 SecDbItemRef
SecDbItemCreateWithAttributes(CFAllocatorRef allocator
, const SecDbClass
*class, CFDictionaryRef attributes
, keybag_handle_t keybag
, CFErrorRef
*error
) {
971 SecDbItemRef item
= SecDbItemCreate(kCFAllocatorDefault
, class, keybag
);
972 if (item
&& !SecDbItemSetValues(item
, attributes
, error
))
978 SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, const SecDbAttr
*attr
, int col
, CFErrorRef
*error
) {
979 CFTypeRef value
= NULL
;
980 switch (attr
->kind
) {
982 case kSecDbCreationDateAttr
:
983 case kSecDbModificationDateAttr
:
984 value
= SecDbColumnCopyDate(allocator
, stmt
, col
, error
);
987 case kSecDbNumberAttr
:
988 switch (sqlite3_column_type(stmt
, col
)) {
990 value
= SecDbColumnCopyNumber(allocator
, stmt
, col
, error
);
993 value
= SecDbColumnCopyDouble(allocator
, stmt
, col
, error
);
996 value
= SecDbColumnCopyString(allocator
, stmt
, col
, error
,
1000 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
1007 case kSecDbAccessAttr
:
1008 case kSecDbStringAttr
:
1009 value
= SecDbColumnCopyString(allocator
, stmt
, col
, error
,
1012 case kSecDbDataAttr
:
1013 case kSecDbUUIDAttr
:
1014 case kSecDbSHA1Attr
:
1015 case kSecDbPrimaryKeyAttr
:
1016 case kSecDbEncryptedDataAttr
:
1017 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
1019 case kSecDbSyncAttr
:
1020 case kSecDbTombAttr
:
1021 value
= SecDbColumnCopyNumber(allocator
, stmt
, col
, error
);
1023 case kSecDbRowIdAttr
:
1024 value
= SecDbColumnCopyNumber64(allocator
, stmt
, col
, error
);
1026 case kSecDbAccessControlAttr
:
1027 case kSecDbUTombAttr
:
1028 /* This attributes does not have any database column associated, exists only inside encrypted blob as metadata. */
1034 SecDbItemRef
SecDbItemCreateWithStatement(CFAllocatorRef allocator
, const SecDbClass
*class, sqlite3_stmt
*stmt
, keybag_handle_t keybag
, CFErrorRef
*error
, bool (^return_attr
)(const SecDbAttr
*attr
)) {
1035 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
1037 SecDbForEachAttr(class, attr
) {
1038 if (return_attr(attr
)) {
1039 CFTypeRef value
= SecDbColumnCopyValueWithAttr(allocator
, stmt
, attr
, col
++, error
);
1040 require_action_quiet(value
, errOut
, CFReleaseNull(item
));
1042 CFDictionarySetValue(item
->attributes
, SecDbAttrGetHashName(attr
), value
);
1046 const SecDbAttr
*data_attr
= SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr
, NULL
);
1047 if (data_attr
!= NULL
&& CFDictionaryGetValue(item
->attributes
, data_attr
->name
) != NULL
) {
1048 item
->_edataState
= kSecDbItemEncrypted
;
1056 SecDbItemRef
SecDbItemCreateWithEncryptedData(CFAllocatorRef allocator
, const SecDbClass
*class,
1057 CFDataRef edata
, keybag_handle_t keybag
, CFErrorRef
*error
) {
1058 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
1059 const SecDbAttr
*edata_attr
= SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr
, error
);
1061 if (!SecDbItemSetValue(item
, edata_attr
, edata
, error
))
1062 CFReleaseNull(item
);
1067 // TODO: Hack -- Replace with real filtering
1069 // Return true iff an item for which SecDbItemIsSyncable() already returns true should be part of the v2 view.
1070 bool SecDbItemInV2(SecDbItemRef item
) {
1071 const SecDbClass
*iclass
= SecDbItemGetClass(item
);
1072 return (SecDbItemGetCachedValueWithName(item
, kSecAttrSyncViewHint
) == NULL
&&
1073 (iclass
== genp_class() || iclass
== inet_class() || iclass
== keys_class() || iclass
== cert_class()));
1076 // Return true iff an item for which SecDbItemIsSyncable() and SecDbItemInV2() already return true should be part of the v0 view.
1077 bool SecDbItemInV2AlsoInV0(SecDbItemRef item
) {
1078 return (SecDbItemGetCachedValueWithName(item
, kSecAttrTokenID
) == NULL
&& SecDbItemGetClass(item
) != cert_class());
1081 SecDbItemRef
SecDbItemCopyWithUpdates(SecDbItemRef item
, CFDictionaryRef updates
, CFErrorRef
*error
) {
1082 SecDbItemRef new_item
= SecDbItemCreate(CFGetAllocator(item
), item
->class, item
->keybag
);
1083 SecDbItemSetCredHandle(new_item
, item
->credHandle
);
1084 SecDbForEachAttr(item
->class, attr
) {
1085 // Copy each attribute, except the mod date attribute (it will be reset to now when needed),
1086 // from the updates dict unless it's not there in which case we copy the attribute from the passed in item.
1087 if (attr
->kind
!= kSecDbModificationDateAttr
&& attr
->kind
!= kSecDbEncryptedDataAttr
&& attr
->kind
!= kSecDbSHA1Attr
&& attr
->kind
!= kSecDbPrimaryKeyAttr
) {
1088 CFTypeRef value
= NULL
;
1089 if (CFDictionaryGetValueIfPresent(updates
, attr
->name
, &value
)) {
1091 SecError(errSecParam
, error
, CFSTR("NULL value in dictionary"));
1093 value
= SecDbItemGetValue(item
, attr
, error
);
1095 if (!value
|| !SecDbItemSetValue(new_item
, attr
, value
, error
)) {
1096 CFReleaseNull(new_item
);
1104 // Ensure that the date value of attr of new_item is greater than that of old_item.
1105 static bool SecDbItemMakeAttrYounger(SecDbItemRef new_item
, SecDbItemRef old_item
, const SecDbAttr
*attr
, CFErrorRef
*error
) {
1106 CFDateRef old_date
= SecDbItemGetValue(old_item
, attr
, error
);
1109 CFDateRef new_date
= SecDbItemGetValue(new_item
, attr
, error
);
1113 if (CFDateCompare(new_date
, old_date
, NULL
) != kCFCompareGreaterThan
) {
1114 CFDateRef adjusted_date
= CFDateCreate(kCFAllocatorDefault
, CFDateGetAbsoluteTime(old_date
) + 0.001);
1115 if (adjusted_date
) {
1116 ok
= SecDbItemSetValue(new_item
, attr
, adjusted_date
, error
);
1117 CFRelease(adjusted_date
);
1123 // Ensure that the mod date of new_item is greater than that of old_item.
1124 static bool SecDbItemMakeYounger(SecDbItemRef new_item
, SecDbItemRef old_item
, CFErrorRef
*error
) {
1125 const SecDbAttr
*attr
= SecDbClassAttrWithKind(new_item
->class, kSecDbModificationDateAttr
, error
);
1126 return attr
&& SecDbItemMakeAttrYounger(new_item
, old_item
, attr
, error
);
1129 static SecDbItemRef
SecDbItemCopyTombstone(SecDbItemRef item
, CFBooleanRef makeTombStone
, bool tombstone_time_from_item
, CFErrorRef
*error
) {
1130 SecDbItemRef new_item
= SecDbItemCreate(CFGetAllocator(item
), item
->class, item
->keybag
);
1131 SecDbForEachAttr(item
->class, attr
) {
1132 if (attr
->kind
== kSecDbTombAttr
) {
1133 // Set the tomb attr to true to indicate a tombstone.
1134 if (!SecDbItemSetValue(new_item
, attr
, kCFBooleanTrue
, error
)) {
1135 CFReleaseNull(new_item
);
1138 } else if (SecDbIsTombstoneDbUpdateAttr(attr
)) {
1139 // Copy all primary key attributes and creation timestamps from the original item.
1140 CFTypeRef value
= SecDbItemGetValue(item
, attr
, error
);
1141 if (!value
|| (!CFEqual(kCFNull
, value
) && !SecDbItemSetValue(new_item
, attr
, value
, error
))) {
1142 CFReleaseNull(new_item
);
1145 } else if (attr
->kind
== kSecDbModificationDateAttr
) {
1146 if(tombstone_time_from_item
) {
1147 SecItemPreserveAttribute(new_item
, item
, attr
);
1150 if (!SecDbItemMakeAttrYounger(new_item
, item
, attr
, error
)) {
1151 CFReleaseNull(new_item
);
1154 } else if (makeTombStone
&& attr
->kind
== kSecDbUTombAttr
) {
1156 SecDbItemSetValue(new_item
, attr
, makeTombStone
, error
);
1163 bool SecDbItemIsEngineInternalState(SecDbItemRef itemObject
) {
1164 // Only used for controlling logging
1165 // Use agrp=com.apple.security.sos, since it is not encrypted
1169 const SecDbAttr
*agrp
= SecDbAttrWithKey(SecDbItemGetClass(itemObject
), kSecAttrAccessGroup
, NULL
);
1170 CFTypeRef cfval
= SecDbItemGetValue(itemObject
, agrp
, NULL
);
1171 return cfval
&& CFStringCompareSafe(cfval
, kSOSInternalAccessGroup
, NULL
) == kCFCompareEqualTo
;
1176 // MARK: SQL Construction helpers -- These should become private in the future
1178 void SecDbAppendElement(CFMutableStringRef sql
, CFStringRef value
, bool *needComma
) {
1181 CFStringAppend(sql
, CFSTR(","));
1185 CFStringAppend(sql
, value
);
1188 static void SecDbAppendElementEquals(CFMutableStringRef sql
, CFStringRef value
, bool *needComma
) {
1189 SecDbAppendElement(sql
, value
, needComma
);
1190 CFStringAppend(sql
, CFSTR("=?"));
1193 /* Append AND is needWhere is NULL or *needWhere is false. Append WHERE
1194 otherwise. Upon return *needWhere will be false. */
1196 SecDbAppendWhereOrAnd(CFMutableStringRef sql
, bool *needWhere
) {
1197 if (!needWhere
|| !*needWhere
) {
1198 CFStringAppend(sql
, CFSTR(" AND "));
1200 CFStringAppend(sql
, CFSTR(" WHERE "));
1206 SecDbAppendWhereOrAndEquals(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
) {
1207 SecDbAppendWhereOrAnd(sql
, needWhere
);
1208 CFStringAppend(sql
, col
);
1209 CFStringAppend(sql
, CFSTR("=?"));
1213 SecDbAppendWhereOrAndNotEquals(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
) {
1214 SecDbAppendWhereOrAnd(sql
, needWhere
);
1215 CFStringAppend(sql
, col
);
1216 CFStringAppend(sql
, CFSTR("!=?"));
1219 static void SecDbAppendCountArgsAndCloseParen(CFMutableStringRef sql
, CFIndex count
) {
1220 bool needComma
= false;
1222 SecDbAppendElement(sql
, CFSTR("?"), &needComma
);
1223 CFStringAppend(sql
, CFSTR(")"));
1227 SecDbAppendWhereOrAndIn(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
, CFIndex count
) {
1229 return SecDbAppendWhereOrAndEquals(sql
, col
, needWhere
);
1230 SecDbAppendWhereOrAnd(sql
, needWhere
);
1231 CFStringAppend(sql
, col
);
1232 CFStringAppend(sql
, CFSTR(" IN ("));
1233 SecDbAppendCountArgsAndCloseParen(sql
, count
);
1237 SecDbAppendWhereOrAndNotIn(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
, CFIndex count
) {
1239 return SecDbAppendWhereOrAndNotEquals(sql
, col
, needWhere
);
1240 SecDbAppendWhereOrAnd(sql
, needWhere
);
1241 CFStringAppend(sql
, col
);
1242 CFStringAppend(sql
, CFSTR(" NOT IN ("));
1243 SecDbAppendCountArgsAndCloseParen(sql
, count
);
1246 static CFStringRef
SecDbItemCopyInsertSQL(SecDbItemRef item
, bool(^use_attr
)(const SecDbAttr
*attr
)) {
1247 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(item
), 0);
1248 CFStringAppend(sql
, CFSTR("INSERT INTO "));
1249 CFStringAppend(sql
, item
->class->name
);
1250 CFStringAppend(sql
, CFSTR("("));
1251 bool needComma
= false;
1252 CFIndex used_attr
= 0;
1253 SecDbForEachAttr(item
->class, attr
) {
1254 if (use_attr(attr
)) {
1256 SecDbAppendElement(sql
, attr
->name
, &needComma
);
1259 CFStringAppend(sql
, CFSTR(")VALUES(?"));
1260 while (used_attr
-- > 1) {
1261 CFStringAppend(sql
, CFSTR(",?"));
1263 CFStringAppend(sql
, CFSTR(")"));
1268 static bool SecDbItemInsertBind(SecDbItemRef item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr
)(const SecDbAttr
*attr
)) {
1271 SecDbForEachAttr(item
->class, attr
) {
1272 if (use_attr(attr
)) {
1273 CFTypeRef value
= SecDbItemCopyValueForDb(item
, attr
, error
);
1274 ok
= value
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1275 CFReleaseSafe(value
);
1283 sqlite3_int64
SecDbItemGetRowId(SecDbItemRef item
, CFErrorRef
*error
) {
1284 sqlite3_int64 row_id
= 0;
1285 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1287 CFNumberRef number
= SecDbItemGetValue(item
, attr
, error
);
1288 if (!isNumber(number
)|| !CFNumberGetValue(number
, kCFNumberSInt64Type
, &row_id
))
1289 SecDbError(SQLITE_ERROR
, error
, CFSTR("rowid %@ is not a 64 bit number"), number
);
1295 static CFNumberRef
SecDbItemCreateRowId(SecDbItemRef item
, sqlite3_int64 rowid
, CFErrorRef
*error
) {
1296 return CFNumberCreate(CFGetAllocator(item
), kCFNumberSInt64Type
, &rowid
);
1299 bool SecDbItemSetRowId(SecDbItemRef item
, sqlite3_int64 rowid
, CFErrorRef
*error
) {
1301 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1303 CFNumberRef value
= SecDbItemCreateRowId(item
, rowid
, error
);
1307 ok
= SecDbItemSetValue(item
, attr
, value
, error
);
1313 bool SecDbItemClearRowId(SecDbItemRef item
, CFErrorRef
*error
) {
1315 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1317 CFDictionaryRemoveValue(item
->attributes
, attr
->name
);
1318 //ok = SecDbItemSetValue(item, attr, kCFNull, error);
1323 static bool SecDbItemSetLastInsertRowId(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1324 sqlite3_int64 rowid
= sqlite3_last_insert_rowid(SecDbHandle(dbconn
));
1325 return SecDbItemSetRowId(item
, rowid
, error
);
1328 bool SecDbItemIsSyncableOrCorrupted(SecDbItemRef item
) {
1329 bool is_syncable_or_corrupted
= false;
1330 CFErrorRef localError
= NULL
;
1331 if (!SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, &localError
),
1332 &is_syncable_or_corrupted
, &localError
)) {
1333 is_syncable_or_corrupted
= SecErrorGetOSStatus(localError
) == errSecDecode
;
1335 CFReleaseSafe(localError
);
1336 return is_syncable_or_corrupted
;
1339 bool SecDbItemIsSyncable(SecDbItemRef item
) {
1341 if (SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, NULL
), &is_syncable
, NULL
))
1346 bool SecDbItemSetSyncable(SecDbItemRef item
, bool sync
, CFErrorRef
*error
)
1348 return SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, error
), sync
? kCFBooleanTrue
: kCFBooleanFalse
, error
);
1351 bool SecDbItemIsTombstone(SecDbItemRef item
) {
1353 if (SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbTombAttr
, NULL
), &is_tomb
, NULL
))
1358 CFDataRef
SecDbItemGetPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
1359 return SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbPrimaryKeyAttr
, error
), error
);
1362 CFDataRef
SecDbItemGetSHA1(SecDbItemRef item
, CFErrorRef
*error
) {
1363 return SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, error
), error
);
1366 static SecDbQueryRef
SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
1367 CFMutableDictionaryRef dict
= SecDbItemCopyPListWithMask(item
, kSecDbPrimaryKeyFlag
, error
);
1371 SecDbQueryRef query
= query_create(item
->class, NULL
, NULL
, NULL
, error
);
1373 CFReleaseSafe(query
->q_item
);
1374 query
->q_item
= dict
;
1382 static bool SecDbItemIsCorrupt(SecDbItemRef item
, bool *is_corrupt
, CFErrorRef
*error
) {
1383 CFErrorRef localError
= NULL
;
1384 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1385 const struct SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, &localError
);
1386 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, &localError
));
1389 if (localError
|| !SecDbItemEnsureDecrypted(item
, true, &localError
)) {
1390 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1391 // We failed to decrypt the item
1392 const SecDbAttr
*desc
= SecDbClassAttrWithKind(item
->class, kSecDbAccessControlAttr
, &localError
);
1393 SecAccessControlRef accc
= NULL
;
1394 CFDataRef acccData
= NULL
;
1396 acccData
= (CFDataRef
)SecDbItemGetValue(item
, desc
, &localError
);
1397 if (isData(acccData
)) {
1398 accc
= SecAccessControlCreateFromData(CFGetAllocator(item
), acccData
, &localError
);
1401 if (accc
&& CFEqualSafe(SecAccessControlGetProtection(accc
), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
)) {
1403 secwarning("cannot decrypt item %@, item is irrecoverably lost with older passcode (error %@)", item
, localError
);
1405 secerror("error %@ reading item %@ (corrupted)", localError
, item
);
1406 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem
);
1408 CFReleaseNull(localError
);
1413 // Recompute sha1 hash attribute and compare with the cached one.
1414 CFDataRef computedSHA1
= SecDbItemCopyValue(item
, sha1attr
, &localError
);
1415 if (storedSHA1
&& computedSHA1
&& !CFEqual(storedSHA1
, computedSHA1
)) {
1416 CFStringRef storedHex
= CFDataCopyHexString(storedSHA1
), computedHex
= CFDataCopyHexString(computedSHA1
);
1417 secerror("error %@ %@ != %@ item %@ (corrupted)", sha1attr
->name
, storedHex
, computedHex
, item
);
1418 // Do not simulate crash for this condition.
1419 // The keychain hashes floating point numbers which causes many false positives, this is not fixable except by major surgery
1420 CFReleaseSafe(storedHex
);
1421 CFReleaseSafe(computedHex
);
1425 // Sanity check that all attributes that must not be NULL actually aren't
1426 if (!localError
) SecDbForEachAttr(item
->class, attr
) {
1427 if (attr
->flags
& (kSecDbInCryptoDataFlag
| kSecDbInAuthenticatedDataFlag
)) {
1428 CFTypeRef value
= SecDbItemGetValue(item
, attr
, &localError
);
1430 if (CFEqual(kCFNull
, value
) && attr
->flags
& kSecDbNotNullFlag
) {
1431 secerror("error attribute %@ has NULL value in item %@ (corrupted)", attr
->name
, item
);
1432 __security_simulatecrash(CFSTR("Corrupted item (attr NULL) found in keychain"), __sec_exception_code_CorruptItem
);
1437 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1438 // We failed to decrypt the item
1440 secwarning("attribute %@: %@ item %@ (item lost with older passcode)", attr
->name
, localError
, item
);
1442 secerror("error attribute %@: %@ item %@ (corrupted)", attr
->name
, localError
, item
);
1443 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem
);
1446 CFReleaseNull(localError
);
1453 CFReleaseSafe(computedSHA1
);
1454 CFReleaseSafe(storedSHA1
);
1455 return SecErrorPropagate(localError
, error
);
1458 static void SecDbItemRecordUpdate(SecDbConnectionRef dbconn
, SecDbItemRef deleted
, SecDbItemRef inserted
) {
1459 SecDbRecordChange(dbconn
, deleted
, inserted
);
1462 static bool SecDbItemDoInsert(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1463 bool (^use_attr
)(const SecDbAttr
*attr
) = ^bool(const SecDbAttr
*attr
) {
1464 return (attr
->flags
& kSecDbInFlag
);
1467 if (!SecDbItemEnsureDecrypted(item
, true, error
)) {
1471 CFStringRef sql
= SecDbItemCopyInsertSQL(item
, use_attr
);
1472 __block
bool ok
= sql
;
1474 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1475 ok
= (SecDbItemInsertBind(item
, stmt
, error
, use_attr
) &&
1476 SecDbStep(dbconn
, stmt
, error
, NULL
) &&
1477 SecDbItemSetLastInsertRowId(item
, dbconn
, error
));
1482 secnotice("item", "inserted %@", item
);
1483 SecDbItemRecordUpdate(dbconn
, NULL
, item
);
1485 if (SecDbItemIsEngineInternalState(item
)) {
1486 secdebug ("item", "insert failed for item %@ with %@", item
, error
? *error
: NULL
);
1488 secnotice("item", "insert failed for item %@ with %@", item
, error
? *error
: NULL
);
1495 bool SecErrorIsSqliteDuplicateItemError(CFErrorRef error
) {
1496 return error
&& CFErrorGetCode(error
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(error
));
1499 bool SecDbItemInsertOrReplace(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, void(^duplicate
)(SecDbItemRef item
, SecDbItemRef
*replace
)) {
1500 __block CFErrorRef localError
= NULL
;
1501 __block
bool ok
= SecDbItemDoInsert(item
, dbconn
, &localError
);
1502 if (!ok
&& localError
&& CFErrorGetCode(localError
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(localError
))) {
1503 SecDbQueryRef query
= SecDbQueryCreateWithItemPrimaryKey(item
, error
);
1505 CFRetainAssign(query
->q_use_cred_handle
, item
->credHandle
);
1506 SecDbItemSelect(query
, dbconn
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
1507 return attr
->flags
& kSecDbPrimaryKeyFlag
;
1508 }, NULL
, NULL
, ^(SecDbItemRef old_item
, bool *stop
) {
1509 bool is_corrupt
= false;
1510 ok
= SecDbItemIsCorrupt(old_item
, &is_corrupt
, error
);
1511 SecDbItemRef replace
= NULL
;
1513 // If old_item is corrupted pretend it's not there and just replace it.
1517 CFReleaseNull(*error
); //item is corrupted and will be replaced, so drop the error
1518 } else if (ok
&& duplicate
) {
1519 duplicate(old_item
, &replace
);
1522 const SecDbAttr
*rowid_attr
= SecDbClassAttrWithKind(old_item
->class, kSecDbRowIdAttr
, error
);
1523 CFNumberRef oldrowid
= SecDbItemGetCachedValue(old_item
, rowid_attr
);
1525 ok
= SecDbItemSetValue(replace
, rowid_attr
, oldrowid
, &localError
);
1526 if (ok
&& !is_corrupt
) {
1527 ok
= SecDbItemMakeYounger(replace
, old_item
, error
);
1529 ok
= ok
&& SecDbItemDoUpdate(old_item
, replace
, dbconn
, &localError
, ^bool (const SecDbAttr
*attr
) {
1530 return attr
->kind
== kSecDbRowIdAttr
;
1533 ok
= SecError(errSecInternal
, &localError
, CFSTR("no rowid for %@"), old_item
);
1537 CFReleaseNull(localError
); // Clear the error, since we replaced the item.
1540 SecDbItemSetCredHandle(item
, query
->q_use_cred_handle
);
1541 ok
&= query_destroy(query
, error
);
1545 return ok
& SecErrorPropagate(localError
, error
); // Don't use && here!
1548 bool SecDbItemInsert(SecDbItemRef item
, SecDbConnectionRef dbconn
, bool always_use_uuid_from_new_item
, CFErrorRef
*error
) {
1549 return SecDbItemInsertOrReplace(item
, dbconn
, error
, ^(SecDbItemRef old_item
, SecDbItemRef
*replace
) {
1550 if (SecDbItemIsTombstone(old_item
)) {
1554 // If the caller doesn't care about the UUID, then use old_item's UUID
1555 // Note: this will modify item!
1556 if(!always_use_uuid_from_new_item
) {
1557 SecDbForEachAttr(SecDbItemGetClass(item
), attr
) {
1558 if(CFEqual(attr
->name
, v10itemuuid
.name
)) {
1559 SecItemPreserveAttribute(item
, old_item
, attr
);
1567 static CFStringRef
SecDbItemCopyUpdateSQL(SecDbItemRef old_item
, SecDbItemRef new_item
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1568 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(new_item
), 0);
1569 CFStringAppend(sql
, CFSTR("UPDATE "));
1570 CFStringAppend(sql
, new_item
->class->name
);
1571 CFStringAppend(sql
, CFSTR(" SET "));
1572 bool needComma
= false;
1573 CFIndex used_attr
= 0;
1574 SecDbForEachAttrWithMask(new_item
->class, attr
, kSecDbInFlag
) {
1576 SecDbAppendElementEquals(sql
, attr
->name
, &needComma
);
1579 bool needWhere
= true;
1580 SecDbForEachAttr(old_item
->class, attr
) {
1581 if (use_attr_in_where(attr
)) {
1582 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1589 static bool SecDbItemUpdateBind(SecDbItemRef old_item
, SecDbItemRef new_item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1592 SecDbForEachAttrWithMask(new_item
->class, attr
, kSecDbInFlag
) {
1593 CFTypeRef value
= SecDbItemCopyValueForDb(new_item
, attr
, error
);
1594 ok
&= value
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1595 CFReleaseSafe(value
);
1599 SecDbForEachAttr(old_item
->class, attr
) {
1600 if (use_attr_in_where(attr
)) {
1601 CFTypeRef value
= SecDbItemCopyValueForDb(old_item
, attr
, error
);
1602 ok
&= value
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1603 CFReleaseSafe(value
);
1611 // Primary keys are the same -- do an update
1612 bool SecDbItemDoUpdate(SecDbItemRef old_item
, SecDbItemRef new_item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1613 CFStringRef sql
= SecDbItemCopyUpdateSQL(old_item
, new_item
, use_attr_in_where
);
1614 __block
bool ok
= sql
;
1616 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1617 ok
= SecDbItemUpdateBind(old_item
, new_item
, stmt
, error
, use_attr_in_where
) && SecDbStep(dbconn
, stmt
, error
, NULL
);
1622 if (SecDbItemIsEngineInternalState(old_item
)) {
1623 secdebug ("item", "replaced %@ in %@", old_item
, dbconn
);
1624 secdebug ("item", " with %@ in %@", new_item
, dbconn
);
1626 secnotice("item", "replaced %@ in %@", old_item
, dbconn
);
1627 secnotice("item", " with %@ in %@", new_item
, dbconn
);
1629 SecDbItemRecordUpdate(dbconn
, old_item
, new_item
);
1634 static CFStringRef
SecDbItemCopyDeleteSQL(SecDbItemRef item
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1635 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(item
), 0);
1636 CFStringAppend(sql
, CFSTR("DELETE FROM "));
1637 CFStringAppend(sql
, item
->class->name
);
1638 bool needWhere
= true;
1639 SecDbForEachAttr(item
->class, attr
) {
1640 if (use_attr_in_where(attr
)) {
1641 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1648 static bool SecDbItemDeleteBind(SecDbItemRef item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1651 SecDbForEachAttr(item
->class, attr
) {
1652 if (use_attr_in_where(attr
)) {
1653 CFTypeRef value
= SecDbItemCopyValueForDb(item
, attr
, error
);
1654 ok
&= value
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1655 CFReleaseSafe(value
);
1663 static bool SecDbItemDoDeleteOnly(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1664 CFStringRef sql
= SecDbItemCopyDeleteSQL(item
, use_attr_in_where
);
1665 __block
bool ok
= sql
;
1667 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1668 ok
= SecDbItemDeleteBind(item
, stmt
, error
, use_attr_in_where
) && SecDbStep(dbconn
, stmt
, error
, NULL
);
1675 bool SecDbItemDoDeleteSilently(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1676 return SecDbItemDoDeleteOnly(item
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1677 return attr
->kind
== kSecDbRowIdAttr
;
1681 static bool SecDbItemDoDelete(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1682 bool ok
= SecDbItemDoDeleteOnly(item
, dbconn
, error
, use_attr_in_where
);
1684 secnotice("item", "deleted %@ from %@", item
, dbconn
);
1685 SecDbItemRecordUpdate(dbconn
, item
, NULL
);
1691 static bool SecDbItemDeleteTombstone(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1693 // TODO: Treat non decryptable items like tombstones here too and delete them
1694 SecDbItemRef tombstone
= SecDbItemCopyTombstone(item
, error
);
1697 ok
= SecDbItemClearRowId(tombstone
, error
);
1699 ok
= SecDbItemDoDelete(tombstone
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1700 return SecDbIsTombstoneDbSelectAttr(attr
);
1703 CFRelease(tombstone
);
1713 return SecCKKSIsEnabled();
1719 // Replace old_item with new_item. If primary keys are the same this does an update otherwise it does a delete + add
1720 bool SecDbItemUpdate(SecDbItemRef old_item
, SecDbItemRef new_item
, SecDbConnectionRef dbconn
, CFBooleanRef makeTombstone
, bool uuid_from_primary_key
, CFErrorRef
*error
) {
1721 __block
bool ok
= true;
1722 __block CFErrorRef localError
= NULL
;
1724 CFDataRef old_pk
= SecDbItemGetPrimaryKey(old_item
, error
);
1725 CFDataRef new_pk
= SecDbItemGetPrimaryKey(new_item
, error
);
1727 ok
= old_pk
&& new_pk
;
1729 bool pk_equal
= ok
&& CFEqual(old_pk
, new_pk
);
1731 ok
= SecDbItemMakeYounger(new_item
, old_item
, error
);
1732 } else if(!CFEqualSafe(makeTombstone
, kCFBooleanFalse
) && isCKKSEnabled()) {
1733 // The primary keys aren't equal, and we're going to make a tombstone.
1734 // Help CKKS out: the tombstone should have the existing item's UUID, and the newly updated item should have a new UUID.
1736 s3dl_item_make_new_uuid(new_item
, uuid_from_primary_key
, error
);
1738 ok
= ok
&& SecDbItemDoUpdate(old_item
, new_item
, dbconn
, &localError
, ^bool(const SecDbAttr
*attr
) {
1739 return attr
->kind
== kSecDbRowIdAttr
;
1743 if(CFErrorGetCode(localError
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(localError
))) {
1744 /* Update failed because we changed the PrimaryKey and there was a dup.
1745 Find the dup and see if it is a tombstone or corrupted item. */
1746 SecDbQueryRef query
= SecDbQueryCreateWithItemPrimaryKey(new_item
, error
);
1749 ok
&= SecDbItemSelect(query
, dbconn
, error
, NULL
, ^bool(const SecDbAttr
*attr
) {
1750 return attr
->flags
& kSecDbPrimaryKeyFlag
;
1751 }, NULL
, NULL
, ^(SecDbItemRef duplicate_item
, bool *stop
) {
1752 bool is_corrupt
= false;
1753 bool is_tomb
= false;
1754 ok
= SecDbItemIsCorrupt(duplicate_item
, &is_corrupt
, error
);
1755 if (ok
&& !is_corrupt
) {
1756 if ((is_tomb
= SecDbItemIsTombstone(duplicate_item
)))
1757 ok
= SecDbItemMakeYounger(new_item
, duplicate_item
, error
);
1759 if (ok
&& (is_corrupt
|| is_tomb
)) {
1760 ok
= SecDbItemDoDelete(old_item
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1761 return attr
->kind
== kSecDbRowIdAttr
;
1763 ok
= ok
&& SecDbItemDoUpdate(duplicate_item
, new_item
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1764 return attr
->kind
== kSecDbRowIdAttr
;
1766 CFReleaseNull(localError
);
1769 ok
&= query_destroy(query
, error
);
1775 if (error
&& *error
== NULL
) {
1776 *error
= localError
;
1779 CFReleaseSafe(localError
);
1783 if (ok
&& !pk_equal
&& !CFEqualSafe(makeTombstone
, kCFBooleanFalse
)) {
1784 /* The primary key of new_item is different than that of old_item, we
1785 have been asked to make a tombstone so leave one for the old_item. */
1786 SecDbItemRef tombstone
= SecDbItemCopyTombstone(old_item
, makeTombstone
, false, error
);
1789 ok
= (SecDbItemClearRowId(tombstone
, error
) &&
1790 SecDbItemDoInsert(tombstone
, dbconn
, error
));
1791 CFRelease(tombstone
);
1798 // Replace the object with a tombstone
1799 bool SecDbItemDelete(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFBooleanRef makeTombstone
, bool tombstone_time_from_item
, CFErrorRef
*error
) {
1801 if (!CFEqualSafe(makeTombstone
, kCFBooleanFalse
)) {
1802 SecDbItemRef tombstone
= SecDbItemCopyTombstone(item
, makeTombstone
, tombstone_time_from_item
, error
);
1804 ok
= SecDbItemDoUpdate(item
, tombstone
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1805 return attr
->kind
== kSecDbRowIdAttr
;
1807 CFRelease(tombstone
);
1810 ok
= SecDbItemDoDelete(item
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1811 return attr
->kind
== kSecDbRowIdAttr
;
1817 CFStringRef
SecDbItemCopySelectSQL(SecDbQueryRef query
,
1818 bool (^return_attr
)(const SecDbAttr
*attr
),
1819 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1820 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
)) {
1821 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1822 CFStringAppend(sql
, CFSTR("SELECT "));
1823 // What are we selecting?
1824 bool needComma
= false;
1825 SecDbForEachAttr(query
->q_class
, attr
) {
1826 if (return_attr(attr
))
1827 SecDbAppendElement(sql
, attr
->name
, &needComma
);
1830 // From which table?
1831 CFStringAppend(sql
, CFSTR(" FROM "));
1832 CFStringAppend(sql
, query
->q_class
->name
);
1834 // And which elements do we want to select
1835 bool needWhere
= true;
1836 SecDbForEachAttr(query
->q_class
, attr
) {
1837 if (use_attr_in_where(attr
)) {
1838 CFTypeRef value
= CFDictionaryGetValue(query
->q_item
, attr
->name
);
1839 if (isArray(value
)) {
1840 CFArrayRef array
= (CFArrayRef
)value
;
1841 CFIndex length
= CFArrayGetCount(array
);
1843 CFTypeRef head
= CFArrayGetValueAtIndex(array
, 0);
1844 if (CFEqualSafe(head
, kCFNull
)) {
1845 SecDbAppendWhereOrAndNotIn(sql
, attr
->name
, &needWhere
, length
- 1);
1847 SecDbAppendWhereOrAndIn(sql
, attr
->name
, &needWhere
, length
);
1851 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1855 // Append SQL for access groups and limits.
1857 add_where_sql(sql
, &needWhere
);
1862 static bool SecDbItemSelectBindValue(SecDbQueryRef query
, sqlite3_stmt
*stmt
, int param
, const SecDbAttr
*attr
, CFTypeRef inValue
, CFErrorRef
*error
) {
1864 CFTypeRef value
= NULL
;
1865 if (attr
->kind
== kSecDbRowIdAttr
) {
1866 // TODO: Ignores inValue and uses rowid directly instead HACK should go
1867 value
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, &query
->q_row_id
);
1869 value
= SecDbAttrCopyValueForDb(attr
, inValue
, error
);
1871 ok
= ok
&& value
!= NULL
&& SecDbBindObject(stmt
, ++param
, value
, error
);
1872 CFReleaseSafe(value
);
1876 bool SecDbItemSelectBind(SecDbQueryRef query
, sqlite3_stmt
*stmt
, CFErrorRef
*error
,
1877 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1878 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
)) {
1879 __block
bool ok
= true;
1880 __block
int param
= 0;
1881 SecDbForEachAttr(query
->q_class
, attr
) {
1882 if (use_attr_in_where(attr
)) {
1883 CFTypeRef value
= CFDictionaryGetValue(query
->q_item
, attr
->name
);
1884 if (isArray(value
)) {
1885 CFArrayRef array
= (CFArrayRef
)value
;
1886 CFRange range
= {.location
= 0, .length
= CFArrayGetCount(array
) };
1887 if (range
.length
> 0) {
1888 CFTypeRef head
= CFArrayGetValueAtIndex(array
, 0);
1889 if (CFEqualSafe(head
, kCFNull
)) {
1894 CFArrayApplyFunction(array
, range
, apply_block_1
, (void (^)(const void *value
)) ^(const void *arrayValue
) {
1895 ok
= SecDbItemSelectBindValue(query
, stmt
, param
++, attr
, arrayValue
, error
);
1898 ok
= SecDbItemSelectBindValue(query
, stmt
, param
++, attr
, value
, error
);
1905 // TODO: Bind arguments for access groups and limits.
1906 if (bind_added_where
)
1907 bind_added_where(stmt
, ++param
);
1912 bool SecDbItemSelect(SecDbQueryRef query
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
1913 bool (^return_attr
)(const SecDbAttr
*attr
),
1914 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1915 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
),
1916 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
),
1917 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
1918 __block
bool ok
= true;
1919 if (return_attr
== NULL
) {
1920 return_attr
= ^bool (const SecDbAttr
* attr
) {
1921 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
|| attr
->kind
== kSecDbSHA1Attr
;
1924 if (use_attr_in_where
== NULL
) {
1925 use_attr_in_where
= ^bool (const SecDbAttr
* attr
) { return false; };
1928 CFStringRef sql
= SecDbItemCopySelectSQL(query
, return_attr
, use_attr_in_where
, add_where_sql
);
1930 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1931 ok
= (SecDbItemSelectBind(query
, stmt
, error
, use_attr_in_where
, bind_added_where
) &&
1932 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
1933 SecDbItemRef item
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
1935 CFRetainAssign(item
->credHandle
, query
->q_use_cred_handle
);
1936 handle_row(item
, stop
);