2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecDbItem.c - CoreFoundation-based constants and functions representing
26 * database items (certificates, keys, identities, and passwords.)
29 #include <securityd/SecDbItem.h>
30 #include <securityd/SecDbKeychainItem.h>
31 #include <securityd/SecItemDb.h>
32 #include <utilities/SecCFWrappers.h>
33 #include <utilities/der_date.h>
34 #include <utilities/debugging.h>
36 #include <Security/SecBasePriv.h>
37 #include <Security/SecInternal.h>
38 #include <corecrypto/ccsha1.h>
39 #include <Security/SecItem.h>
40 #include <Security/SecItemPriv.h>
41 #include <Security/SecAccessControl.h>
42 #include <Security/SecAccessControlPriv.h>
44 // MARK: type converters
46 CFStringRef
copyString(CFTypeRef obj
) {
47 CFTypeID tid
= CFGetTypeID(obj
);
48 if (tid
== CFStringGetTypeID())
49 return CFStringCreateCopy(0, obj
);
50 else if (tid
== CFDataGetTypeID())
51 return CFStringCreateFromExternalRepresentation(0, obj
, kCFStringEncodingUTF8
);
56 CFDataRef
copyData(CFTypeRef obj
) {
57 CFTypeID tid
= CFGetTypeID(obj
);
58 if (tid
== CFDataGetTypeID()) {
59 return CFDataCreateCopy(0, obj
);
60 } else if (tid
== CFStringGetTypeID()) {
61 return CFStringCreateExternalRepresentation(0, obj
, kCFStringEncodingUTF8
, 0);
62 } else if (tid
== CFNumberGetTypeID()) {
64 CFNumberGetValue(obj
, kCFNumberSInt32Type
, &value
);
65 return CFDataCreate(0, (const UInt8
*)&value
, sizeof(value
));
71 CFTypeRef
copyBlob(CFTypeRef obj
) {
72 CFTypeID tid
= CFGetTypeID(obj
);
73 if (tid
== CFDataGetTypeID()) {
74 return CFDataCreateCopy(0, obj
);
75 } else if (tid
== CFStringGetTypeID()) {
76 return CFStringCreateCopy(0, obj
);
77 } else if (tid
== CFNumberGetTypeID()) {
85 CFDataRef
copySHA1(CFTypeRef obj
) {
86 CFTypeID tid
= CFGetTypeID(obj
);
87 if (tid
== CFDataGetTypeID() && CFDataGetLength(obj
) == CCSHA1_OUTPUT_SIZE
) {
88 return CFDataCreateCopy(CFGetAllocator(obj
), obj
);
94 CFTypeRef
copyNumber(CFTypeRef obj
) {
95 CFTypeID tid
= CFGetTypeID(obj
);
96 if (tid
== CFNumberGetTypeID()) {
99 } else if (tid
== CFBooleanGetTypeID()) {
100 SInt32 value
= CFBooleanGetValue(obj
);
101 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
102 } else if (tid
== CFStringGetTypeID()) {
103 SInt32 value
= CFStringGetIntValue(obj
);
104 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value
);
105 /* If a string converted to an int isn't equal to the int printed as
106 a string, return a CFStringRef instead. */
107 if (!CFEqual(t
, obj
)) {
109 return CFStringCreateCopy(0, obj
);
112 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
117 CFDateRef
copyDate(CFTypeRef obj
) {
118 CFTypeID tid
= CFGetTypeID(obj
);
119 if (tid
== CFDateGetTypeID()) {
126 // MARK: SecDbColumn accessors, to retrieve values as CF types in SecDbStep.
128 static CFDataRef
SecDbColumnCopyData(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
129 return CFDataCreate(allocator
, sqlite3_column_blob(stmt
, col
),
130 sqlite3_column_bytes(stmt
, col
));
131 //return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col),
132 // sqlite3_column_bytes(stmt, col),
133 // kCFAllocatorNull);
136 static CFDateRef
SecDbColumnCopyDate(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
137 return CFDateCreate(allocator
, sqlite3_column_double(stmt
, col
));
140 static CFNumberRef
SecDbColumnCopyDouble(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
141 double number
= sqlite3_column_double(stmt
, col
);
142 return CFNumberCreate(allocator
, kCFNumberDoubleType
, &number
);
145 static CFNumberRef
SecDbColumnCopyNumber64(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
146 sqlite_int64 number
= sqlite3_column_int64(stmt
, col
);
147 return CFNumberCreate(allocator
, kCFNumberSInt64Type
, &number
);
150 static CFNumberRef
SecDbColumnCopyNumber(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
151 sqlite_int64 number
= sqlite3_column_int64(stmt
, col
);
152 if (INT32_MIN
<= number
&& number
<= INT32_MAX
) {
153 int32_t num32
= (int32_t)number
;
154 return CFNumberCreate(allocator
, kCFNumberSInt32Type
, &num32
);
156 return CFNumberCreate(allocator
, kCFNumberSInt64Type
, &number
);
160 static CFStringRef
SecDbColumnCopyString(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
161 const unsigned char *text
= sqlite3_column_text(stmt
, col
);
162 return CFStringCreateWithBytes(allocator
, text
, strlen((const char *)text
), kCFStringEncodingUTF8
, false);
165 // MARK: SecDbClass helpers
167 const SecDbAttr
*SecDbClassAttrWithKind(const SecDbClass
*class, SecDbAttrKind kind
, CFErrorRef
*error
) {
168 const SecDbAttr
*result
= NULL
;
169 SecDbForEachAttr(class, desc
) {
170 if (desc
->kind
== kind
)
175 SecError(errSecInternal
, error
, CFSTR("Can't find attribute of kind %d in class %@"), kind
, class->name
);
180 // MARK: SecDbAttr helpers
182 static bool SecDbIsTombstoneDbSelectAttr(const SecDbAttr
*attr
) {
183 return attr
->flags
& kSecDbPrimaryKeyFlag
|| attr
->kind
== kSecDbTombAttr
;
187 static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr
*attr
) {
188 return SecDbIsTombstoneDbSelectAttr(attr
) || attr
->kind
== kSecDbAccessAttr
|| attr
->kind
== kSecDbCreationDateAttr
|| attr
->kind
== kSecDbModificationDateAttr
;
192 static bool SecDbIsTombstoneDbUpdateAttr(const SecDbAttr
*attr
) {
193 return SecDbIsTombstoneDbSelectAttr(attr
) || attr
->kind
== kSecDbAccessAttr
|| attr
->kind
== kSecDbCreationDateAttr
|| attr
->kind
== kSecDbRowIdAttr
;
196 static bool SecDbAttrBind(const SecDbAttr
*attr
, sqlite3_stmt
*stmt
, int col
, CFTypeRef value
, CFErrorRef
*error
) {
198 if (value
&& !CFEqual(kCFNull
, value
) && attr
->flags
& kSecDbSHA1ValueInFlag
) {
199 CFDataRef data
= copyData(value
);
201 SecError(errSecInternal
, error
, CFSTR("failed to get attribute %@ data"), attr
->name
);
205 CFMutableDataRef digest
= CFDataCreateMutable(kCFAllocatorDefault
, CCSHA1_OUTPUT_SIZE
);
206 CFDataSetLength(digest
, CCSHA1_OUTPUT_SIZE
);
207 /* 64 bits cast: worst case is we generate the wrong hash */
208 assert((unsigned long)CFDataGetLength(data
)<UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
209 ccdigest(ccsha1_di(), CFDataGetLength(data
), CFDataGetBytePtr(data
), CFDataGetMutableBytePtr(digest
));
211 ok
&= SecDbBindObject(stmt
, col
, digest
, error
);
214 ok
&= SecDbBindObject(stmt
, col
, value
, error
);
221 CFTypeRef
SecDbItemGetCachedValueWithName(SecDbItemRef item
, CFStringRef name
) {
222 return CFDictionaryGetValue(item
->attributes
, name
);
225 static CFTypeRef
SecDbItemGetCachedValue(SecDbItemRef item
, const SecDbAttr
*desc
) {
226 return CFDictionaryGetValue(item
->attributes
, desc
->name
);
229 CFMutableDictionaryRef
SecDbItemCopyPListWithMask(SecDbItemRef item
, CFOptionFlags mask
, CFErrorRef
*error
) {
230 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault
);
231 SecDbForEachAttrWithMask(item
->class, desc
, mask
) {
232 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
234 if (!CFEqual(kCFNull
, value
)) {
235 CFDictionarySetValue(dict
, desc
->name
, value
);
236 } else if (desc
->flags
& kSecDbNotNullFlag
) {
237 SecError(errSecDecode
, error
, CFSTR("attribute %@ has NULL value"), desc
->name
);
238 secerror("%@", error
? *error
: (CFErrorRef
)CFSTR("error == NULL"));
250 static CFDataRef
SecDbItemCopyDERWithMask(SecDbItemRef item
, CFOptionFlags mask
, CFErrorRef
*error
) {
251 CFDataRef der
= NULL
;
252 CFMutableDictionaryRef dict
= SecDbItemCopyPListWithMask(item
, mask
, error
);
254 der
= kc_plist_copy_der(dict
, error
);
260 static CFDataRef
SecDbItemCopyDigestWithMask(SecDbItemRef item
, CFOptionFlags mask
, CFErrorRef
*error
) {
261 CFDataRef digest
= NULL
;
262 CFDataRef der
= SecDbItemCopyDERWithMask(item
, mask
, error
);
264 digest
= kc_copy_sha1(CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
270 static CFDataRef
SecDbItemCopyPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
271 return SecDbItemCopyDigestWithMask(item
, kSecDbPrimaryKeyFlag
, error
);
274 static CFDataRef
SecDbItemCopySHA1(SecDbItemRef item
, CFErrorRef
*error
) {
275 return SecDbItemCopyDigestWithMask(item
, kSecDbInHashFlag
, error
);
278 SecAccessControlRef
SecDbItemCopyAccessControl(SecDbItemRef item
, CFErrorRef
*error
) {
279 SecAccessControlRef accc
= NULL
;
280 SecAccessControlRef pdmn
= NULL
;
281 CFTypeRef acccData
= SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessControlAttr
, error
), error
);
282 CFTypeRef pdmnValue
= SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
), error
);
283 if (!acccData
|| !pdmnValue
)
285 if (!CFEqual(acccData
, kCFNull
)) {
286 accc
= SecAccessControlCreateFromData(CFGetAllocator(item
), acccData
, error
);
290 if (!CFEqual(pdmnValue
, kCFNull
)) {
291 pdmn
= SecAccessControlCreateWithFlags(CFGetAllocator(item
), pdmnValue
, 0, error
);
298 CFTypeRef acccProt
= SecAccessControlGetProtection(accc
);
299 CFTypeRef pdmnProt
= SecAccessControlGetProtection(pdmn
);
300 if (!acccProt
|| !pdmnProt
|| !CFEqual(acccProt
, pdmnProt
)) {
301 secerror("SecDbItemCopyAccessControl accc %@ != pdmn %@, setting pdmn to accc value", acccProt
, pdmnProt
);
302 __security_simulatecrash(CFSTR("Corrupted item on decrypt accc != pdmn"), __sec_exception_code_CorruptItem
);
303 // Setting pdmn to accc prot value.
304 if (!SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessAttr
, error
), acccProt
, error
)) {
305 // Fixing the kSecDbAccessAttr failed, return the error.
316 void SecDbItemSetCredHandle(SecDbItemRef item
, CFTypeRef cred_handle
) {
317 CFRetainAssign(item
->credHandle
, cred_handle
);
320 void SecDbItemSetCallerAccessGroups(SecDbItemRef item
, CFArrayRef caller_access_groups
) {
321 CFRetainAssign(item
->callerAccessGroups
, caller_access_groups
);
324 static CFDataRef
SecDbItemCopyEncryptedData(SecDbItemRef item
, CFErrorRef
*error
) {
325 CFDataRef edata
= NULL
;
326 CFMutableDictionaryRef attributes
= SecDbItemCopyPListWithMask(item
, kSecDbInCryptoDataFlag
, error
);
327 CFMutableDictionaryRef auth_attributes
= SecDbItemCopyPListWithMask(item
, kSecDbInAuthenticatedDataFlag
, error
);
328 if (attributes
|| auth_attributes
) {
329 SecAccessControlRef access_control
= SecDbItemCopyAccessControl(item
, error
);
330 if (access_control
) {
331 if (ks_encrypt_data(item
->keybag
, access_control
, &item
->credHandle
, attributes
, auth_attributes
, &edata
, error
)) {
332 item
->_edataState
= kSecDbItemEncrypting
;
334 seccritical("ks_encrypt_data (db): failed: %@", error
? *error
: (CFErrorRef
)CFSTR(""));
336 CFRelease(access_control
);
338 CFReleaseSafe(attributes
);
339 CFReleaseSafe(auth_attributes
);
345 CFDataRef
SecDbItemCopyEncryptedDataToBackup(SecDbItemRef item
, uint64_t handle
, CFErrorRef
*error
) {
346 CFDataRef edata
= NULL
;
347 keybag_handle_t keybag
= (keybag_handle_t
)handle
;
348 CFMutableDictionaryRef attributes
= SecDbItemCopyPListWithMask(item
, kSecDbInCryptoDataFlag
, error
);
349 CFMutableDictionaryRef auth_attributes
= SecDbItemCopyPListWithMask(item
, kSecDbInAuthenticatedDataFlag
, error
);
350 if (attributes
|| auth_attributes
) {
351 SecAccessControlRef access_control
= SecDbItemCopyAccessControl(item
, error
);
352 if (access_control
) {
353 if (ks_encrypt_data(keybag
, access_control
, &item
->credHandle
, attributes
, auth_attributes
, &edata
, error
)) {
354 item
->_edataState
= kSecDbItemEncrypting
;
356 seccritical("ks_encrypt_data (db): failed: %@", error
? *error
: (CFErrorRef
)CFSTR(""));
358 CFRelease(access_control
);
360 CFReleaseSafe(attributes
);
361 CFReleaseSafe(auth_attributes
);
366 bool SecDbItemEnsureDecrypted(SecDbItemRef item
, CFDataRef
*authNeeded
, CFTypeRef
*credHandle
, CFErrorRef
*error
) {
368 // If we haven't yet decrypted the item, make sure we do so now
370 if (item
->_edataState
== kSecDbItemEncrypted
) {
371 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, error
);
373 CFDataRef edata
= SecDbItemGetCachedValue(item
, attr
);
375 return SecError(errSecInternal
, error
, CFSTR("state= encrypted but edata is NULL"));
376 // Decrypt calls set value a bunch of times which clears our edata and changes our state.
377 item
->_edataState
= kSecDbItemDecrypting
;
379 SecDbItemSetCredHandle(item
, *credHandle
);
380 result
= SecDbItemDecrypt(item
, edata
, authNeeded
, error
);
382 CFRetainAssign(*credHandle
, item
->credHandle
);
384 item
->_edataState
= kSecDbItemClean
;
386 item
->_edataState
= kSecDbItemEncrypted
;
392 // Only called if cached value is not found.
393 static CFTypeRef
SecDbItemCopyValue(SecDbItemRef item
, const SecDbAttr
*attr
, CFErrorRef
*error
) {
394 CFTypeRef value
= NULL
;
395 switch (attr
->kind
) {
397 value
= SecDbItemCopySHA1(item
, error
);
399 case kSecDbEncryptedDataAttr
:
400 value
= SecDbItemCopyEncryptedData(item
, error
);
402 case kSecDbPrimaryKeyAttr
:
403 value
= SecDbItemCopyPrimaryKey(item
, error
);
405 case kSecDbAccessAttr
:
406 case kSecDbStringAttr
:
408 case kSecDbAccessControlAttr
:
409 if (attr
->flags
& kSecDbNotNullFlag
) {
410 if (attr
->flags
& kSecDbDefault0Flag
) {
413 } else if (attr
->kind
!= kSecDbBlobAttr
&& attr
->flags
& kSecDbDefaultEmptyFlag
) {
414 // blob drops through to data everything else is empty string
421 if (attr
->flags
& kSecDbNotNullFlag
&& attr
->flags
& kSecDbDefaultEmptyFlag
) {
422 value
= CFDataCreate(CFGetAllocator(item
), NULL
, 0);
427 case kSecDbNumberAttr
:
430 if (attr
->flags
& kSecDbNotNullFlag
) {
432 value
= CFNumberCreate(CFGetAllocator(item
), kCFNumberSInt32Type
, &zero
);
438 if (attr
->flags
& kSecDbNotNullFlag
&& attr
->flags
& kSecDbDefault0Flag
) {
439 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
444 case kSecDbRowIdAttr
:
445 if (attr
->flags
& kSecDbNotNullFlag
) {
450 case kSecDbCreationDateAttr
:
451 case kSecDbModificationDateAttr
:
452 value
= CFDateCreate(CFGetAllocator(item
), CFAbsoluteTimeGetCurrent());
459 // SecDbItemGetValue will return kCFNull if there is no value for an attribute and this was not
460 // an error. It will return NULL and optionally set *error if there was an error computing an
461 // attribute, or if a required attribute was missing a value and had no known way to compute
463 CFTypeRef
SecDbItemGetValue(SecDbItemRef item
, const SecDbAttr
*desc
, CFErrorRef
*error
) {
464 // Propagate chained errors
468 if (desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
) {
469 if (!SecDbItemEnsureDecrypted(item
, NULL
, NULL
, error
))
473 CFTypeRef value
= SecDbItemGetCachedValue(item
, desc
);
475 value
= SecDbItemCopyValue(item
, desc
, error
);
477 if (CFEqual(kCFNull
, value
)) {
478 CFRelease(value
); // This is redundant but it shuts clang's static analyzer up.
481 SecDbItemSetValue(item
, desc
, value
, error
);
483 value
= SecDbItemGetCachedValue(item
, desc
);
490 static bool SecDbItemGetBoolValue(SecDbItemRef item
, const SecDbAttr
*desc
, bool *bvalue
, CFErrorRef
*error
) {
491 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
495 *bvalue
= (isNumber(value
) && CFNumberGetValue(value
, kCFNumberCharType
, &cvalue
) && cvalue
== 1);
499 static CFStringRef
SecDbItemCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
500 #if 0 //defined(DEBUG) && DEBUG != 0
501 SecDbItemRef item
= (SecDbItemRef
)cf
;
502 CFMutableStringRef desc
= CFStringCreateMutable(CFGetAllocator(cf
), 0);
503 CFStringAppendFormat(desc
, NULL
, CFSTR("<%@"), item
->class->name
);
504 SecDbForEachAttr(item
->class, attr
) {
505 CFTypeRef value
= SecDbItemGetValue(item
, attr
, NULL
);
507 CFStringAppend(desc
, CFSTR(","));
508 CFStringAppend(desc
, attr
->name
);
509 CFStringAppend(desc
, CFSTR("="));
510 if (CFEqual(CFSTR("data"), attr
->name
)) {
511 CFStringAppendEncryptedData(desc
, value
);
512 } else if (CFEqual(CFSTR("v_Data"), attr
->name
)) {
513 CFStringAppend(desc
, CFSTR("<?>"));
514 } else if (isData(value
)) {
515 CFStringAppendHexData(desc
, value
);
517 CFStringAppendFormat(desc
, 0, CFSTR("%@"), value
);
521 CFStringAppend(desc
, CFSTR(">"));
523 SecDbItemRef item
= (SecDbItemRef
)cf
;
524 const UInt8 zero4
[4] = {};
525 const UInt8
*pk
= &zero4
[0], *sha1
= &zero4
[0];
529 CFStringRef access
= NULL
;
530 uint8_t mdatbuf
[32] = {};
531 uint8_t *mdat
= &mdatbuf
[0];
532 CFMutableStringRef attrs
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
533 CFStringRef agrp
= NULL
;
535 SecDbForEachAttr(item
->class, attr
) {
537 switch (attr
->kind
) {
540 case kSecDbStringAttr
:
541 case kSecDbNumberAttr
:
543 case kSecDbEncryptedDataAttr
:
544 if (attr
->flags
& (kSecDbReturnAttrFlag
| kSecDbReturnDataFlag
) && (value
= SecDbItemGetValue(item
, attr
, NULL
)) && !CFEqual(value
, kCFNull
)) {
545 if (isString(value
) && CFEqual(attr
->name
, kSecAttrAccessGroup
)) {
548 // We don't log these, just record that we saw the attribute.
549 CFStringAppend(attrs
, CFSTR(","));
550 CFStringAppend(attrs
, attr
->name
);
554 case kSecDbCreationDateAttr
:
555 // We don't care about this and every object has one.
557 case kSecDbModificationDateAttr
:
558 value
= SecDbItemGetValue(item
, attr
, NULL
);
560 mdat
= der_encode_generalizedtime_body(CFDateGetAbsoluteTime(value
), NULL
, mdat
, &mdatbuf
[31]);
563 value
= SecDbItemGetValue(item
, attr
, NULL
);
565 sha1
= CFDataGetBytePtr(value
);
567 case kSecDbRowIdAttr
:
568 value
= SecDbItemGetValue(item
, attr
, NULL
);
570 CFNumberGetValue(value
, kCFNumberSInt64Type
, &rowid
);
572 case kSecDbPrimaryKeyAttr
:
573 value
= SecDbItemGetValue(item
, attr
, NULL
);
575 pk
= CFDataGetBytePtr(value
);
578 value
= SecDbItemGetValue(item
, attr
, NULL
);
580 CFNumberGetValue(value
, kCFNumberCharType
, &sync
);
583 value
= SecDbItemGetValue(item
, attr
, NULL
);
585 CFNumberGetValue(value
, kCFNumberCharType
, &tomb
);
587 case kSecDbAccessAttr
:
588 value
= SecDbItemGetValue(item
, attr
, NULL
);
592 case kSecDbAccessControlAttr
:
593 /* TODO: Add formatting of ACLs. */
598 CFStringRef desc
= CFStringCreateWithFormat(CFGetAllocator(cf
), NULL
,
612 pk
[0], pk
[1], pk
[2], pk
[3],
619 sha1
[0], sha1
[1], sha1
[2], sha1
[3]);
620 CFReleaseSafe(attrs
);
626 static void SecDbItemDestroy(CFTypeRef cf
) {
627 SecDbItemRef item
= (SecDbItemRef
)cf
;
628 CFReleaseSafe(item
->attributes
);
629 CFReleaseSafe(item
->credHandle
);
630 CFReleaseSafe(item
->callerAccessGroups
);
633 static CFHashCode
SecDbItemHash(CFTypeRef cf
) {
634 SecDbItemRef item
= (SecDbItemRef
)cf
;
635 CFDataRef digest
= SecDbItemGetSHA1(item
, NULL
);
637 const UInt8
*p
= CFDataGetBytePtr(digest
);
638 // Read first 8 bytes of digest in order
639 code
= p
[0] + ((p
[1] + ((p
[2] + ((p
[3] + ((p
[4] + ((p
[5] + ((p
[6] + (p
[7] << 8)) << 8)) << 8)) << 8)) << 8)) << 8)) << 8);
643 static Boolean
SecDbItemCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
644 SecDbItemRef item1
= (SecDbItemRef
)cf1
;
645 SecDbItemRef item2
= (SecDbItemRef
)cf2
;
646 CFDataRef digest1
= NULL
;
647 CFDataRef digest2
= NULL
;
649 digest1
= SecDbItemGetSHA1(item1
, NULL
);
651 digest2
= SecDbItemGetSHA1(item2
, NULL
);
652 Boolean equal
= CFEqual(digest1
, digest2
);
656 CFGiblisWithHashFor(SecDbItem
)
658 static SecDbItemRef
SecDbItemCreate(CFAllocatorRef allocator
, const SecDbClass
*class, keybag_handle_t keybag
) {
659 SecDbItemRef item
= CFTypeAllocate(SecDbItem
, struct SecDbItem
, allocator
);
661 item
->attributes
= CFDictionaryCreateMutableForCFTypes(allocator
);
662 item
->keybag
= keybag
;
663 item
->_edataState
= kSecDbItemDirty
;
664 item
->cryptoOp
= kSecKsUnwrap
;
668 const SecDbClass
*SecDbItemGetClass(SecDbItemRef item
) {
672 keybag_handle_t
SecDbItemGetKeybag(SecDbItemRef item
) {
676 bool SecDbItemSetKeybag(SecDbItemRef item
, keybag_handle_t keybag
, CFErrorRef
*error
) {
677 if (!SecDbItemEnsureDecrypted(item
, NULL
, NULL
, error
))
679 if (item
->keybag
!= keybag
) {
680 item
->keybag
= keybag
;
681 if (item
->_edataState
== kSecDbItemClean
) {
682 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, NULL
), kCFNull
, NULL
);
689 bool SecDbItemSetValue(SecDbItemRef item
, const SecDbAttr
*desc
, CFTypeRef value
, CFErrorRef
*error
) {
690 // Propagate chained errors.
697 bool changed
= false;
698 CFTypeRef attr
= NULL
;
699 if (desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
)
700 if (!SecDbItemEnsureDecrypted(item
, NULL
, NULL
, error
))
703 switch (desc
->kind
) {
704 case kSecDbPrimaryKeyAttr
:
706 attr
= copyData(value
);
708 case kSecDbEncryptedDataAttr
:
709 attr
= copyData(value
);
711 if (item
->_edataState
== kSecDbItemEncrypting
)
712 item
->_edataState
= kSecDbItemClean
;
714 item
->_edataState
= kSecDbItemEncrypted
;
715 } else if (!value
|| CFEqual(kCFNull
, value
)) {
716 item
->_edataState
= kSecDbItemDirty
;
720 case kSecDbAccessControlAttr
:
721 attr
= copyBlob(value
);
724 case kSecDbCreationDateAttr
:
725 case kSecDbModificationDateAttr
:
726 attr
= copyDate(value
);
728 case kSecDbNumberAttr
:
731 case kSecDbRowIdAttr
:
732 attr
= copyNumber(value
);
734 case kSecDbAccessAttr
:
735 case kSecDbStringAttr
:
736 attr
= copyString(value
);
739 attr
= copySHA1(value
);
744 CFTypeRef ovalue
= CFDictionaryGetValue(item
->attributes
, desc
->name
);
745 changed
= (!ovalue
|| !CFEqual(ovalue
, attr
));
746 CFDictionarySetValue(item
->attributes
, desc
->name
, attr
);
749 if (value
&& !CFEqual(kCFNull
, value
)) {
750 SecError(errSecItemInvalidValue
, error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
753 CFTypeRef ovalue
= CFDictionaryGetValue(item
->attributes
, desc
->name
);
754 changed
= (ovalue
&& !CFEqual(ovalue
, kCFNull
));
755 CFDictionaryRemoveValue(item
->attributes
, desc
->name
);
759 if (desc
->flags
& kSecDbInHashFlag
)
760 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, NULL
), kCFNull
, NULL
);
761 if (desc
->flags
& kSecDbPrimaryKeyFlag
)
762 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbPrimaryKeyAttr
, NULL
), kCFNull
, NULL
);
763 if ((desc
->flags
& kSecDbInCryptoDataFlag
|| desc
->flags
& kSecDbInAuthenticatedDataFlag
) && item
->_edataState
== kSecDbItemClean
)
764 SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, NULL
), kCFNull
, NULL
);
770 bool SecDbItemSetValues(SecDbItemRef item
, CFDictionaryRef values
, CFErrorRef
*error
) {
771 SecDbForEachAttr(item
->class, attr
) {
772 CFTypeRef value
= CFDictionaryGetValue(values
, attr
->name
);
773 if (value
&& !SecDbItemSetValue(item
, attr
, value
, error
))
779 bool SecDbItemSetValueWithName(SecDbItemRef item
, CFStringRef name
, CFTypeRef value
, CFErrorRef
*error
) {
780 SecDbForEachAttr(item
->class, attr
) {
781 if (CFEqual(attr
->name
, name
)) {
782 return SecDbItemSetValue(item
, attr
, value
, error
);
788 bool SecDbItemSetAccessControl(SecDbItemRef item
, SecAccessControlRef access_control
, CFErrorRef
*error
) {
789 bool ok
= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbEncryptedDataAttr
, error
), kCFNull
, error
);
791 item
->_edataState
= kSecDbItemDirty
;
792 CFDataRef data
= SecAccessControlCopyData(access_control
);
793 ok
= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbAccessControlAttr
, error
), data
, error
);
799 SecDbItemRef
SecDbItemCreateWithAttributes(CFAllocatorRef allocator
, const SecDbClass
*class, CFDictionaryRef attributes
, keybag_handle_t keybag
, CFErrorRef
*error
) {
800 SecDbItemRef item
= SecDbItemCreate(kCFAllocatorDefault
, class, keybag
);
801 if (item
&& !SecDbItemSetValues(item
, attributes
, error
))
807 SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator
, sqlite3_stmt
*stmt
, const SecDbAttr
*attr
, int col
, CFErrorRef
*error
) {
808 CFTypeRef value
= NULL
;
809 switch (attr
->kind
) {
811 case kSecDbCreationDateAttr
:
812 case kSecDbModificationDateAttr
:
813 value
= SecDbColumnCopyDate(allocator
, stmt
, col
, error
);
816 switch (sqlite3_column_type(stmt
, col
)) {
818 value
= SecDbColumnCopyNumber(allocator
, stmt
, col
, error
);
821 value
= SecDbColumnCopyDouble(allocator
, stmt
, col
, error
);
824 value
= SecDbColumnCopyString(allocator
, stmt
, col
, error
);
827 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
834 case kSecDbAccessAttr
:
835 case kSecDbStringAttr
:
836 value
= SecDbColumnCopyString(allocator
, stmt
, col
, error
);
840 case kSecDbPrimaryKeyAttr
:
841 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
843 case kSecDbEncryptedDataAttr
:
844 value
= SecDbColumnCopyData(allocator
, stmt
, col
, error
);
848 case kSecDbNumberAttr
:
849 value
= SecDbColumnCopyNumber(allocator
, stmt
, col
, error
);
851 case kSecDbRowIdAttr
:
852 value
= SecDbColumnCopyNumber64(allocator
, stmt
, col
, error
);
854 case kSecDbAccessControlAttr
:
855 /* This attributes does not have any database column associated, exists only inside encrypted blob as metadata. */
861 SecDbItemRef
SecDbItemCreateWithStatement(CFAllocatorRef allocator
, const SecDbClass
*class, sqlite3_stmt
*stmt
, keybag_handle_t keybag
, CFErrorRef
*error
, bool (^return_attr
)(const SecDbAttr
*attr
)) {
862 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
864 SecDbForEachAttr(class, attr
) {
865 if (return_attr(attr
)) {
866 CFTypeRef value
= SecDbColumnCopyValueWithAttr(allocator
, stmt
, attr
, col
++, error
);
868 if (attr
->kind
== kSecDbSHA1Attr
) {
869 SecDbItemSetValue(item
, attr
, value
, NULL
);
870 } else if (!SecDbItemSetValue(item
, attr
, value
, error
)) {
881 SecDbItemRef
SecDbItemCreateWithEncryptedData(CFAllocatorRef allocator
, const SecDbClass
*class,
882 CFDataRef edata
, keybag_handle_t keybag
, CFErrorRef
*error
) {
883 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
884 const SecDbAttr
*edata_attr
= SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr
, error
);
886 if (!SecDbItemSetValue(item
, edata_attr
, edata
, error
))
893 SecDbItemRef
SecDbItemCreateWithRowId(CFAllocatorRef allocator
, const SecDbClass
*class, sqlite_int64 row_id
, keybag_handle_t keybag
, CFErrorRef
*error
) {
894 SecDbItemRef item
= SecDbItemCreate(allocator
, class, keybag
);
895 if (!SecDbItemSetRowId(item
, row_id
, error
))
901 SecDbItemRef
SecDbItemCopyWithUpdates(SecDbItemRef item
, CFDictionaryRef updates
, CFErrorRef
*error
) {
902 SecDbItemRef new_item
= SecDbItemCreate(CFGetAllocator(item
), item
->class, item
->keybag
);
903 SecDbForEachAttr(item
->class, attr
) {
904 // Copy each attribute, except the mod date attribute (it will be reset to now when needed),
905 // from the updates dict unless it's not there in which case we copy the attribute from the passed in item.
906 if (attr
->kind
!= kSecDbModificationDateAttr
&& attr
->kind
!= kSecDbEncryptedDataAttr
&& attr
->kind
!= kSecDbSHA1Attr
&& attr
->kind
!= kSecDbPrimaryKeyAttr
) {
907 CFTypeRef value
= NULL
;
908 if (CFDictionaryGetValueIfPresent(updates
, attr
->name
, &value
)) {
910 SecError(errSecParam
, error
, CFSTR("NULL value in dictionary"));
912 value
= SecDbItemGetValue(item
, attr
, error
);
914 if (!value
|| !SecDbItemSetValue(new_item
, attr
, value
, error
)) {
915 CFReleaseNull(new_item
);
923 // Ensure that the date value of attr of new_item is greater than that of old_item.
924 static bool SecDbItemMakeAttrYounger(SecDbItemRef new_item
, SecDbItemRef old_item
, const SecDbAttr
*attr
, CFErrorRef
*error
) {
925 CFDateRef old_date
= SecDbItemGetValue(old_item
, attr
, error
);
928 CFDateRef new_date
= SecDbItemGetValue(new_item
, attr
, error
);
932 if (CFDateCompare(new_date
, old_date
, NULL
) != kCFCompareGreaterThan
) {
933 CFDateRef adjusted_date
= CFDateCreate(kCFAllocatorDefault
, CFDateGetAbsoluteTime(old_date
) + 0.001);
935 ok
= SecDbItemSetValue(new_item
, attr
, adjusted_date
, error
);
936 CFRelease(adjusted_date
);
942 // Ensure that the mod date of new_item is greater than that of old_item.
943 static bool SecDbItemMakeYounger(SecDbItemRef new_item
, SecDbItemRef old_item
, CFErrorRef
*error
) {
944 const SecDbAttr
*attr
= SecDbClassAttrWithKind(new_item
->class, kSecDbModificationDateAttr
, error
);
945 return attr
&& SecDbItemMakeAttrYounger(new_item
, old_item
, attr
, error
);
948 SecDbItemRef
SecDbItemCopyTombstone(SecDbItemRef item
, CFErrorRef
*error
) {
949 SecDbItemRef new_item
= SecDbItemCreate(CFGetAllocator(item
), item
->class, item
->keybag
);
950 SecDbForEachAttr(item
->class, attr
) {
951 if (attr
->kind
== kSecDbTombAttr
) {
952 // Set the tomb attr to true to indicate a tombstone.
953 if (!SecDbItemSetValue(new_item
, attr
, kCFBooleanTrue
, error
)) {
954 CFReleaseNull(new_item
);
957 } else if (SecDbIsTombstoneDbUpdateAttr(attr
)) {
958 // Copy all primary key attributes and creation timestamps from the original item.
959 CFTypeRef value
= SecDbItemGetValue(item
, attr
, error
);
960 if (!value
|| (!CFEqual(kCFNull
, value
) && !SecDbItemSetValue(new_item
, attr
, value
, error
))) {
961 CFReleaseNull(new_item
);
964 } else if (attr
->kind
== kSecDbModificationDateAttr
) {
965 if (!SecDbItemMakeAttrYounger(new_item
, item
, attr
, error
)) {
966 CFReleaseNull(new_item
);
976 // MARK: SQL Construction helpers -- These should become private in the future
978 void SecDbAppendElement(CFMutableStringRef sql
, CFStringRef value
, bool *needComma
) {
981 CFStringAppend(sql
, CFSTR(","));
985 CFStringAppend(sql
, value
);
988 static void SecDbAppendElementEquals(CFMutableStringRef sql
, CFStringRef value
, bool *needComma
) {
989 SecDbAppendElement(sql
, value
, needComma
);
990 CFStringAppend(sql
, CFSTR("=?"));
993 /* Append AND is needWhere is NULL or *needWhere is false. Append WHERE
994 otherwise. Upon return *needWhere will be false. */
996 SecDbAppendWhereOrAnd(CFMutableStringRef sql
, bool *needWhere
) {
997 if (!needWhere
|| !*needWhere
) {
998 CFStringAppend(sql
, CFSTR(" AND "));
1000 CFStringAppend(sql
, CFSTR(" WHERE "));
1006 SecDbAppendWhereOrAndEquals(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
) {
1007 SecDbAppendWhereOrAnd(sql
, needWhere
);
1008 CFStringAppend(sql
, col
);
1009 CFStringAppend(sql
, CFSTR("=?"));
1012 static CFStringRef
SecDbItemCopyInsertSQL(SecDbItemRef item
, bool(^use_attr
)(const SecDbAttr
*attr
)) {
1013 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(item
), 0);
1014 CFStringAppend(sql
, CFSTR("INSERT INTO "));
1015 CFStringAppend(sql
, item
->class->name
);
1016 CFStringAppend(sql
, CFSTR("("));
1017 bool needComma
= false;
1018 CFIndex used_attr
= 0;
1019 SecDbForEachAttr(item
->class, attr
) {
1020 if (use_attr(attr
)) {
1022 SecDbAppendElement(sql
, attr
->name
, &needComma
);
1025 CFStringAppend(sql
, CFSTR(")VALUES(?"));
1026 while (used_attr
-- > 1) {
1027 CFStringAppend(sql
, CFSTR(",?"));
1029 CFStringAppend(sql
, CFSTR(")"));
1034 static bool SecDbItemInsertBind(SecDbItemRef item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr
)(const SecDbAttr
*attr
)) {
1037 SecDbForEachAttr(item
->class, attr
) {
1038 if (use_attr(attr
)) {
1039 CFTypeRef value
= SecDbItemGetValue(item
, attr
, error
);
1040 if (!value
|| !SecDbAttrBind(attr
, stmt
, ++param
, value
, error
)) {
1049 sqlite3_int64
SecDbItemGetRowId(SecDbItemRef item
, CFErrorRef
*error
) {
1050 sqlite3_int64 row_id
= 0;
1051 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1053 CFNumberRef number
= SecDbItemGetValue(item
, attr
, error
);
1054 if (!isNumber(number
)|| !CFNumberGetValue(number
, kCFNumberSInt64Type
, &row_id
))
1055 SecDbError(SQLITE_ERROR
, error
, CFSTR("rowid %@ is not a 64 bit number"), number
);
1061 static CFNumberRef
SecDbItemCreateRowId(SecDbItemRef item
, sqlite3_int64 rowid
, CFErrorRef
*error
) {
1062 return CFNumberCreate(CFGetAllocator(item
), kCFNumberSInt64Type
, &rowid
);
1065 bool SecDbItemSetRowId(SecDbItemRef item
, sqlite3_int64 rowid
, CFErrorRef
*error
) {
1067 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1069 CFNumberRef value
= SecDbItemCreateRowId(item
, rowid
, error
);
1073 ok
= SecDbItemSetValue(item
, attr
, value
, error
);
1079 static bool SecDbItemClearRowId(SecDbItemRef item
, CFErrorRef
*error
) {
1081 const SecDbAttr
*attr
= SecDbClassAttrWithKind(item
->class, kSecDbRowIdAttr
, error
);
1083 CFDictionaryRemoveValue(item
->attributes
, attr
->name
);
1084 //ok = SecDbItemSetValue(item, attr, kCFNull, error);
1089 static bool SecDbItemSetLastInsertRowId(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1090 sqlite3_int64 rowid
= sqlite3_last_insert_rowid(SecDbHandle(dbconn
));
1091 return SecDbItemSetRowId(item
, rowid
, error
);
1094 bool SecDbItemIsSyncableOrCorrupted(SecDbItemRef item
) {
1095 bool is_syncable_or_corrupted
= false;
1096 CFErrorRef localError
= NULL
;
1097 if (!SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, &localError
),
1098 &is_syncable_or_corrupted
, &localError
)) {
1099 is_syncable_or_corrupted
= SecErrorGetOSStatus(localError
) == errSecDecode
;
1101 CFReleaseSafe(localError
);
1102 return is_syncable_or_corrupted
;
1105 bool SecDbItemIsSyncable(SecDbItemRef item
) {
1107 if (SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, NULL
), &is_syncable
, NULL
))
1112 bool SecDbItemSetSyncable(SecDbItemRef item
, bool sync
, CFErrorRef
*error
)
1114 return SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSyncAttr
, error
), sync
? kCFBooleanTrue
: kCFBooleanFalse
, error
);
1117 bool SecDbItemIsTombstone(SecDbItemRef item
) {
1119 if (SecDbItemGetBoolValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbTombAttr
, NULL
), &is_tomb
, NULL
))
1124 CFDataRef
SecDbItemGetPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
1125 return SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbPrimaryKeyAttr
, error
), error
);
1128 CFDataRef
SecDbItemGetSHA1(SecDbItemRef item
, CFErrorRef
*error
) {
1129 return SecDbItemGetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, error
), error
);
1132 static SecDbQueryRef
SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item
, CFErrorRef
*error
) {
1133 CFMutableDictionaryRef dict
= SecDbItemCopyPListWithMask(item
, kSecDbPrimaryKeyFlag
, error
);
1137 SecDbQueryRef query
= query_create(item
->class, NULL
, error
);
1139 CFReleaseSafe(query
->q_item
);
1140 query
->q_item
= dict
;
1148 static bool SecDbItemIsCorrupt(SecDbItemRef item
, bool *is_corrupt
, CFErrorRef
*error
) {
1149 CFErrorRef localError
= NULL
;
1150 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1151 const struct SecDbAttr
*sha1attr
= SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, &localError
);
1152 CFDataRef storedSHA1
= CFRetainSafe(SecDbItemGetValue(item
, sha1attr
, &localError
));
1153 SecDbItemSetValue(item
, sha1attr
, kCFNull
, &localError
);
1154 CFDataRef access_control_data
= NULL
;
1158 if (localError
|| !SecDbItemEnsureDecrypted(item
, &access_control_data
, NULL
, &localError
)) {
1159 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1160 // We failed to decrypt the item
1161 SecAccessControlRef access_control
= NULL
;
1162 CFTypeRef access_class
= NULL
;
1164 if (access_control_data
!= NULL
&&
1166 SecAccessControlCreateFromData(kCFAllocatorDefault
, access_control_data
, NULL
)) != NULL
&&
1167 (access_class
= SecAccessControlGetProtection(access_control
)) != NULL
&&
1168 CFStringCompare(access_class
, kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
, 0) == kCFCompareEqualTo
) {
1170 secwarning("cannot decrypt item %@, item is irrecoverably lost with older passcode (error %@)", item
, localError
);
1172 secerror("error %@ reading item %@ (corrupted)", localError
, item
);
1173 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem
);
1175 CFReleaseSafe(access_control
);
1176 CFReleaseNull(localError
);
1180 CFReleaseNull(access_control_data
);
1182 CFDataRef computedSHA1
= SecDbItemGetValue(item
, sha1attr
, NULL
);
1183 if (storedSHA1
&& computedSHA1
) {
1184 if (!CFEqual(storedSHA1
, computedSHA1
)) {
1185 secerror("error %@ %@ != %@ item %@ (corrupted)", sha1attr
->name
, storedSHA1
, computedSHA1
, item
);
1186 __security_simulatecrash(CFSTR("Corrupted item (sha1 mismatch) found in keychain"), __sec_exception_code_CorruptItem
);
1188 // Restore original (bad) sha1 digest so db notifications will properly update our manifest
1189 SecDbItemSetValue(item
, sha1attr
, storedSHA1
, NULL
);
1190 CFReleaseSafe(storedSHA1
);
1194 CFReleaseSafe(storedSHA1
);
1196 // Sanity check that all attributes that must not be NULL actually aren't
1197 if (!localError
) SecDbForEachAttr(item
->class, attr
) {
1198 if (attr
->flags
& (kSecDbInCryptoDataFlag
| kSecDbInAuthenticatedDataFlag
)) {
1199 CFTypeRef value
= SecDbItemGetValue(item
, attr
, &localError
);
1201 if (CFEqual(kCFNull
, value
) && attr
->flags
& kSecDbNotNullFlag
) {
1202 secerror("error attribute %@ has NULL value in item %@ (corrupted)", attr
->name
, item
);
1203 __security_simulatecrash(CFSTR("Corrupted item (attr NULL) found in keychain"), __sec_exception_code_CorruptItem
);
1208 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
1209 // We failed to decrypt the item
1211 secwarning("attribute %@: %@ item %@ (item lost with older passcode)", attr
->name
, localError
, item
);
1213 secerror("error attribute %@: %@ item %@ (corrupted)", attr
->name
, localError
, item
);
1214 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem
);
1217 CFReleaseNull(localError
);
1223 return CFErrorPropagate(localError
, error
);
1226 static void SecDbItemRecordUpdate(SecDbConnectionRef dbconn
, SecDbItemRef deleted
, SecDbItemRef inserted
) {
1227 CFDataRef deletedDigest
= NULL
;
1228 CFDataRef insertedDigest
= NULL
;
1229 if (deleted
&& SecDbItemIsSyncableOrCorrupted(deleted
))
1230 deletedDigest
= SecDbItemGetSHA1(deleted
, NULL
);
1231 if (inserted
&& SecDbItemIsSyncable(inserted
))
1232 insertedDigest
= SecDbItemGetSHA1(inserted
, NULL
);
1233 if (deletedDigest
|| insertedDigest
)
1234 SecDbRecordChange(dbconn
, deletedDigest
, insertedDigest
);
1237 static bool SecDbItemDoInsert(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1238 bool (^use_attr
)(const SecDbAttr
*attr
) = ^bool(const SecDbAttr
*attr
) {
1239 return (attr
->flags
& kSecDbInFlag
);
1241 CFStringRef sql
= SecDbItemCopyInsertSQL(item
, use_attr
);
1242 __block
bool ok
= sql
;
1244 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1245 ok
= (SecDbItemInsertBind(item
, stmt
, error
, use_attr
) &&
1246 SecDbStep(dbconn
, stmt
, error
, NULL
) &&
1247 SecDbItemSetLastInsertRowId(item
, dbconn
, error
));
1252 secnotice("item", "inserted %@", item
);
1253 SecDbItemRecordUpdate(dbconn
, NULL
, item
);
1259 bool SecDbItemInsertOrReplace(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFMutableArrayRef authlist
, CFErrorRef
*error
, void(^duplicate
)(SecDbItemRef item
, SecDbItemRef
*replace
)) {
1260 __block CFErrorRef localError
= NULL
;
1261 __block
bool ok
= SecDbItemDoInsert(item
, dbconn
, &localError
);
1262 if (!ok
&& localError
&& CFErrorGetCode(localError
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(localError
))) {
1263 SecDbQueryRef query
= SecDbQueryCreateWithItemPrimaryKey(item
, error
);
1265 CFRetainAssign(query
->q_required_access_controls
, authlist
);
1266 CFRetainAssign(query
->q_use_cred_handle
, item
->credHandle
);
1267 SecDbItemSelect(query
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1268 return attr
->flags
& kSecDbPrimaryKeyFlag
;
1269 }, NULL
, NULL
, ^(SecDbItemRef old_item
, bool *stop
) {
1270 bool is_corrupt
= false;
1271 ok
= SecDbItemIsCorrupt(old_item
, &is_corrupt
, error
);
1272 SecDbItemRef replace
= NULL
;
1274 // If old_item is corrupted pretend it's not there and just replace it.
1277 } else if (ok
&& duplicate
) {
1278 duplicate(old_item
, &replace
);
1281 const SecDbAttr
*rowid_attr
= SecDbClassAttrWithKind(old_item
->class, kSecDbRowIdAttr
, error
);
1282 CFNumberRef oldrowid
= SecDbItemGetCachedValue(old_item
, rowid_attr
);
1284 ok
= SecDbItemSetValue(replace
, rowid_attr
, oldrowid
, &localError
);
1285 if (ok
&& !is_corrupt
) {
1286 ok
= SecDbItemMakeYounger(replace
, old_item
, error
);
1288 ok
= ok
&& SecDbItemDoUpdate(old_item
, replace
, dbconn
, &localError
, ^bool (const SecDbAttr
*attr
) {
1289 return attr
->kind
== kSecDbRowIdAttr
;
1292 ok
= SecError(errSecInternal
, &localError
, CFSTR("no rowid for %@"), old_item
);
1296 CFReleaseNull(localError
); // Clear the error, since we replaced the item.
1299 SecDbItemSetCredHandle(item
, query
->q_use_cred_handle
);
1300 ok
&= query_destroy(query
, error
);
1304 return ok
& CFErrorPropagate(localError
, error
); // Don't use && here!
1307 bool SecDbItemInsert(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFMutableArrayRef authlist
, CFErrorRef
*error
) {
1308 return SecDbItemInsertOrReplace(item
, dbconn
, authlist
, error
, ^(SecDbItemRef old_item
, SecDbItemRef
*replace
) {
1309 if (SecDbItemIsTombstone(old_item
)) {
1316 static CFStringRef
SecDbItemCopyUpdateSQL(SecDbItemRef old_item
, SecDbItemRef new_item
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1317 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(new_item
), 0);
1318 CFStringAppend(sql
, CFSTR("UPDATE "));
1319 CFStringAppend(sql
, new_item
->class->name
);
1320 CFStringAppend(sql
, CFSTR(" SET "));
1321 bool needComma
= false;
1322 CFIndex used_attr
= 0;
1323 SecDbForEachAttrWithMask(new_item
->class, attr
, kSecDbInFlag
) {
1325 SecDbAppendElementEquals(sql
, attr
->name
, &needComma
);
1328 bool needWhere
= true;
1329 SecDbForEachAttr(old_item
->class, attr
) {
1330 if (use_attr_in_where(attr
)) {
1331 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1338 static bool SecDbItemUpdateBind(SecDbItemRef old_item
, SecDbItemRef new_item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1341 SecDbForEachAttrWithMask(new_item
->class, attr
, kSecDbInFlag
) {
1342 CFTypeRef value
= SecDbItemGetValue(new_item
, attr
, error
);
1343 ok
&= value
&& SecDbAttrBind(attr
, stmt
, ++param
, value
, error
);
1347 SecDbForEachAttr(old_item
->class, attr
) {
1348 if (use_attr_in_where(attr
)) {
1349 CFTypeRef value
= SecDbItemGetValue(old_item
, attr
, error
);
1350 ok
&= value
&& SecDbAttrBind(attr
, stmt
, ++param
, value
, error
);
1358 // Primary keys are the same -- do an update
1359 bool SecDbItemDoUpdate(SecDbItemRef old_item
, SecDbItemRef new_item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1360 CFStringRef sql
= SecDbItemCopyUpdateSQL(old_item
, new_item
, use_attr_in_where
);
1361 __block
bool ok
= sql
;
1363 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1364 ok
= SecDbItemUpdateBind(old_item
, new_item
, stmt
, error
, use_attr_in_where
) && SecDbStep(dbconn
, stmt
, error
, NULL
);
1369 secnotice("item", "replaced %@ with %@ in %@", old_item
, new_item
, dbconn
);
1370 SecDbItemRecordUpdate(dbconn
, old_item
, new_item
);
1375 static CFStringRef
SecDbItemCopyDeleteSQL(SecDbItemRef item
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1376 CFMutableStringRef sql
= CFStringCreateMutable(CFGetAllocator(item
), 0);
1377 CFStringAppend(sql
, CFSTR("DELETE FROM "));
1378 CFStringAppend(sql
, item
->class->name
);
1379 bool needWhere
= true;
1380 SecDbForEachAttr(item
->class, attr
) {
1381 if (use_attr_in_where(attr
)) {
1382 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1389 static bool SecDbItemDeleteBind(SecDbItemRef item
, sqlite3_stmt
*stmt
, CFErrorRef
*error
, bool(^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1392 SecDbForEachAttr(item
->class, attr
) {
1393 if (use_attr_in_where(attr
)) {
1394 CFTypeRef value
= SecDbItemGetValue(item
, attr
, error
);
1395 ok
&= value
&& SecDbAttrBind(attr
, stmt
, ++param
, value
, error
);
1403 static bool SecDbItemDoDelete(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
, bool (^use_attr_in_where
)(const SecDbAttr
*attr
)) {
1404 CFStringRef sql
= SecDbItemCopyDeleteSQL(item
, use_attr_in_where
);
1405 __block
bool ok
= sql
;
1407 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1408 ok
= SecDbItemDeleteBind(item
, stmt
, error
, use_attr_in_where
) && SecDbStep(dbconn
, stmt
, error
, NULL
);
1413 secnotice("item", "deleted %@ from %@", item
, dbconn
);
1414 SecDbItemRecordUpdate(dbconn
, item
, NULL
);
1420 static bool SecDbItemDeleteTombstone(SecDbItemRef item
, SecDbConnectionRef dbconn
, CFErrorRef
*error
) {
1422 // TODO: Treat non decryptable items like tombstones here too and delete them
1423 SecDbItemRef tombstone
= SecDbItemCopyTombstone(item
, error
);
1426 ok
= SecDbItemClearRowId(tombstone
, error
);
1428 ok
= SecDbItemDoDelete(tombstone
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1429 return SecDbIsTombstoneDbSelectAttr(attr
);
1432 CFRelease(tombstone
);
1438 // Replace old_item with new_item. If primary keys are the same this does an update otherwise it does a delete + add
1439 bool SecDbItemUpdate(SecDbItemRef old_item
, SecDbItemRef new_item
, SecDbConnectionRef dbconn
, bool makeTombstone
, CFErrorRef
*error
) {
1440 __block
bool ok
= true;
1441 __block CFErrorRef localError
= NULL
;
1443 CFDataRef old_pk
= SecDbItemGetPrimaryKey(old_item
, error
);
1444 CFDataRef new_pk
= SecDbItemGetPrimaryKey(new_item
, error
);
1446 ok
= old_pk
&& new_pk
;
1448 bool pk_equal
= ok
&& CFEqual(old_pk
, new_pk
);
1450 ok
= SecDbItemMakeYounger(new_item
, old_item
, error
);
1452 ok
= ok
&& SecDbItemDoUpdate(old_item
, new_item
, dbconn
, &localError
, ^bool(const SecDbAttr
*attr
) {
1453 return attr
->kind
== kSecDbRowIdAttr
;
1457 if(CFErrorGetCode(localError
) == SQLITE_CONSTRAINT
&& CFEqual(kSecDbErrorDomain
, CFErrorGetDomain(localError
))) {
1458 /* Update failed because we changed the PrimaryKey and there was a dup.
1459 Find the dup and see if it is a tombstone or corrupted item. */
1460 SecDbQueryRef query
= SecDbQueryCreateWithItemPrimaryKey(new_item
, error
);
1463 ok
&= SecDbItemSelect(query
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1464 return attr
->flags
& kSecDbPrimaryKeyFlag
;
1465 }, NULL
, NULL
, ^(SecDbItemRef duplicate_item
, bool *stop
) {
1466 bool is_corrupt
= false;
1467 bool is_tomb
= false;
1468 ok
= SecDbItemIsCorrupt(duplicate_item
, &is_corrupt
, error
);
1469 if (ok
&& !is_corrupt
) {
1470 if ((is_tomb
= SecDbItemIsTombstone(duplicate_item
)))
1471 ok
= SecDbItemMakeYounger(new_item
, duplicate_item
, error
);
1473 if (ok
&& (is_corrupt
|| is_tomb
)) {
1474 ok
= SecDbItemDoDelete(old_item
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1475 return attr
->kind
== kSecDbRowIdAttr
;
1477 ok
= ok
&& SecDbItemDoUpdate(duplicate_item
, new_item
, dbconn
, error
, ^bool (const SecDbAttr
*attr
) {
1478 return attr
->kind
== kSecDbRowIdAttr
;
1480 CFReleaseNull(localError
);
1483 ok
&= query_destroy(query
, error
);
1489 if (error
&& *error
== NULL
) {
1490 *error
= localError
;
1493 CFReleaseSafe(localError
);
1497 if (ok
&& !pk_equal
&& makeTombstone
) {
1498 /* The primary key of new_item is different than that of old_item, we
1499 have been asked to make a tombstone so leave one for the old_item. */
1500 SecDbItemRef tombstone
= SecDbItemCopyTombstone(old_item
, error
);
1503 ok
= (SecDbItemClearRowId(tombstone
, error
) &&
1504 SecDbItemDoInsert(tombstone
, dbconn
, error
));
1505 CFRelease(tombstone
);
1512 // Replace the object with a tombstone
1513 bool SecDbItemDelete(SecDbItemRef item
, SecDbConnectionRef dbconn
, bool makeTombstone
, CFErrorRef
*error
) {
1515 if (makeTombstone
) {
1516 SecDbItemRef tombstone
= SecDbItemCopyTombstone(item
, error
);
1518 ok
= SecDbItemDoUpdate(item
, tombstone
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1519 return attr
->kind
== kSecDbRowIdAttr
;
1521 CFRelease(tombstone
);
1524 ok
= SecDbItemDoDelete(item
, dbconn
, error
, ^bool(const SecDbAttr
*attr
) {
1525 return attr
->kind
== kSecDbRowIdAttr
;
1531 CFStringRef
SecDbItemCopySelectSQL(SecDbQueryRef query
,
1532 bool (^return_attr
)(const SecDbAttr
*attr
),
1533 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1534 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
)) {
1535 CFMutableStringRef sql
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
1536 CFStringAppend(sql
, CFSTR("SELECT "));
1537 // What are we selecting?
1538 bool needComma
= false;
1539 SecDbForEachAttr(query
->q_class
, attr
) {
1540 if (return_attr(attr
))
1541 SecDbAppendElement(sql
, attr
->name
, &needComma
);
1544 // From which table?
1545 CFStringAppend(sql
, CFSTR(" FROM "));
1546 CFStringAppend(sql
, query
->q_class
->name
);
1548 // And which elements do we want to select
1549 bool needWhere
= true;
1550 SecDbForEachAttr(query
->q_class
, attr
) {
1551 if (use_attr_in_where(attr
)) {
1552 SecDbAppendWhereOrAndEquals(sql
, attr
->name
, &needWhere
);
1555 // Append SQL for access groups and limits.
1557 add_where_sql(sql
, &needWhere
);
1562 bool SecDbItemSelectBind(SecDbQueryRef query
, sqlite3_stmt
*stmt
, CFErrorRef
*error
,
1563 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1564 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
)) {
1567 SecDbForEachAttr(query
->q_class
, attr
) {
1568 if (use_attr_in_where(attr
)) {
1569 CFTypeRef value
= CFDictionaryGetValue(query
->q_item
, attr
->name
);
1570 ok
&= SecDbAttrBind(attr
, stmt
, ++param
, value
, error
);
1575 // TODO: Bind arguments for access groups and limits.
1576 if (bind_added_where
)
1577 bind_added_where(stmt
, ++param
);
1582 bool SecDbItemSelect(SecDbQueryRef query
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
1583 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
1584 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
),
1585 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
),
1586 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
1587 __block
bool ok
= true;
1588 __block
bool decrypted
= true;
1589 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
1590 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
|| attr
->kind
== kSecDbSHA1Attr
;
1592 CFStringRef sql
= SecDbItemCopySelectSQL(query
, return_attr
, use_attr_in_where
, add_where_sql
);
1594 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
1595 ok
= (SecDbItemSelectBind(query
, stmt
, error
, use_attr_in_where
, bind_added_where
) &&
1596 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
1597 SecDbItemRef item
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
1599 if (query
->q_required_access_controls
) {
1600 CFDataRef authNeeded
= NULL
;
1601 item
->cryptoOp
= query
->q_crypto_op
;
1602 SecDbItemSetCallerAccessGroups(item
, query
->q_caller_access_groups
);
1603 decrypted
= SecDbItemEnsureDecrypted(item
, &authNeeded
, &query
->q_use_cred_handle
, NULL
);
1604 if (decrypted
&& authNeeded
) {
1605 // Do not process the item right now, just remember that it will need
1606 // authentication to properly process it.
1607 CFArrayAppendValue(query
->q_required_access_controls
, authNeeded
);
1608 CFRelease(authNeeded
);
1612 CFReleaseSafe(authNeeded
);
1614 handle_row(item
, stop
);