]> git.saurik.com Git - apple/security.git/blob - keychain/securityd/SecDbItem.c
Security-59754.60.13.tar.gz
[apple/security.git] / keychain / securityd / SecDbItem.c
1 /*
2 * Copyright (c) 2012-2014 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecDbItem.c - CoreFoundation-based constants and functions representing
26 * database items (certificates, keys, identities, and passwords.)
27 */
28
29 #if TARGET_DARWINOS
30 #undef OCTAGON
31 #undef SECUREOBJECTSYNC
32 #undef SHAREDWEBCREDENTIALS
33 #endif
34
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>
43
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"
52
53 #include <keychain/ckks/CKKS.h>
54
55 // MARK: type converters
56
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);
65 } else {
66 return NULL;
67 }
68 }
69
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()) {
77 SInt32 value;
78 CFNumberGetValue(obj, kCFNumberSInt32Type, &value);
79 return CFDataCreate(0, (const UInt8 *)&value, sizeof(value));
80 } else {
81 return NULL;
82 }
83 }
84
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)
90 return NULL;
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));
97 return uuiddata;
98 } else {
99 return NULL;
100 }
101 }
102
103
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()) {
111 CFRetain(obj);
112 return obj;
113 } else {
114 return NULL;
115 }
116 }
117
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);
122 } else {
123 return NULL;
124 }
125 }
126
127 CFTypeRef copyNumber(CFTypeRef obj) {
128 CFTypeID tid = CFGetTypeID(obj);
129 if (tid == CFNumberGetTypeID()) {
130 CFRetain(obj);
131 return obj;
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)) {
141 CFRelease(t);
142 return CFStringCreateCopy(0, obj);
143 }
144 CFRelease(t);
145 return CFNumberCreate(0, kCFNumberSInt32Type, &value);
146 } else
147 return NULL;
148 }
149
150 CFDateRef copyDate(CFTypeRef obj) {
151 CFTypeID tid = CFGetTypeID(obj);
152 if (tid == CFDateGetTypeID()) {
153 CFRetain(obj);
154 return obj;
155 } else
156 return NULL;
157 }
158
159 // MARK: SecDbColumn accessors, to retrieve values as CF types in SecDbStep.
160
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);
167 }
168
169 static CFDateRef SecDbColumnCopyDate(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) {
170 return CFDateCreate(allocator, sqlite3_column_double(stmt, col));
171 }
172
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);
176 }
177
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);
181 }
182
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);
188 } else {
189 return CFNumberCreate(allocator, kCFNumberSInt64Type, &number);
190 }
191 }
192
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) {
198 return CFSTR("");
199 } else if (flags & kSecDbDefault0Flag) {
200 return CFSTR("0");
201 } else {
202 return kCFNull;
203 }
204 }
205 return CFStringCreateWithBytes(allocator, text, strlen((const char *)text), kCFStringEncodingUTF8, false);
206 }
207
208 // MARK: SecDbClass helpers
209
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)
214 result = desc;
215 }
216
217 if (!result)
218 SecError(errSecInternal, error, CFSTR("Can't find attribute of kind %d in class %@"), kind, class->name);
219
220 return result;
221 }
222
223 // MARK: SecDbAttr helpers
224
225 static bool SecDbIsTombstoneDbSelectAttr(const SecDbAttr *attr) {
226 return attr->flags & kSecDbPrimaryKeyFlag || attr->kind == kSecDbTombAttr;
227 }
228
229 #if 0
230 static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr *attr) {
231 return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbModificationDateAttr;
232 }
233 #endif
234
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);
238 }
239
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:
246 value = CFSTR("");
247 break;
248 case kSecDbBlobAttr:
249 case kSecDbDataAttr:
250 value = CFDataCreate(kCFAllocatorDefault, NULL, 0);
251 break;
252 case kSecDbUUIDAttr:
253 value = CFDataCreate(kCFAllocatorDefault, NULL, 0);
254 break;
255 case kSecDbNumberAttr:
256 case kSecDbSyncAttr:
257 case kSecDbTombAttr:
258 {
259 int32_t zero = 0;
260 value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &zero);
261 break;
262 }
263 case kSecDbDateAttr:
264 value = CFDateCreate(kCFAllocatorDefault, 0.0);
265 break;
266 case kSecDbCreationDateAttr:
267 case kSecDbModificationDateAttr:
268 value = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
269 break;
270 default:
271 SecError(errSecInternal, error, CFSTR("attr %@ has no default value"), attr->name);
272 value = NULL;
273 }
274
275 return value;
276 }
277
278 static CFTypeRef SecDbAttrCopyValueForDb(const SecDbAttr *attr, CFTypeRef value, CFErrorRef *error) {
279 CFDataRef data = NULL;
280 CFTypeRef result = NULL;
281
282 if (value == NULL)
283 value = kCFNull;
284
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);
288 } else {
289 result = CFRetain(value);
290 }
291
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));
297 }
298
299 out:
300 CFReleaseSafe(data);
301 return result;
302 }
303
304 static CFStringRef SecDbAttrGetHashName(const SecDbAttr *attr) {
305 if ((attr->flags & kSecDbSHA1ValueInFlag) == 0) {
306 return attr->name;
307 }
308
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);
315 });
316
317 __block CFStringRef name;
318 dispatch_sync(queue, ^{
319 name = CFDictionaryGetValue(hash_store, attr->name);
320 if (name == NULL) {
321 name = CFStringCreateWithFormat(NULL, NULL, CFSTR("#%@"), attr->name);
322 CFDictionarySetValue(hash_store, attr->name, name);
323 CFRelease(name);
324 }
325 });
326 return name;
327 }
328
329 // MARK: SecDbItem
330
331 CFTypeRef SecDbItemGetCachedValueWithName(SecDbItemRef item, CFStringRef name) {
332 return CFDictionaryGetValue(item->attributes, name);
333 }
334
335 static CFTypeRef SecDbItemGetCachedValue(SecDbItemRef item, const SecDbAttr *desc) {
336 return CFDictionaryGetValue(item->attributes, desc->name);
337 }
338
339 CFMutableDictionaryRef SecDbItemCopyPListWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) {
340 return SecDbItemCopyPListWithFlagAndSkip(item, mask, 0, error);
341 }
342
343 CFMutableDictionaryRef SecDbItemCopyPListWithFlagAndSkip(SecDbItemRef item,
344 CFOptionFlags mask,
345 CFOptionFlags flagsToSkip,
346 CFErrorRef *error)
347 {
348 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
349 SecDbForEachAttrWithMask(item->class, desc, mask) {
350 if((desc->flags & flagsToSkip) != 0) {
351 break;
352 }
353
354 CFTypeRef value = SecDbItemGetValue(item, desc, error);
355 if (value) {
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"));
361 CFReleaseNull(dict);
362 break;
363 }
364 } else {
365 CFReleaseNull(dict);
366 break;
367 }
368 }
369 return dict;
370 }
371
372 void SecDbItemSetCredHandle(SecDbItemRef item, CFTypeRef cred_handle) {
373 CFRetainAssign(item->credHandle, cred_handle);
374 }
375
376 void SecDbItemSetCallerAccessGroups(SecDbItemRef item, CFArrayRef caller_access_groups) {
377 CFRetainAssign(item->callerAccessGroups, caller_access_groups);
378 }
379
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;
390 } else {
391 seccritical("ks_encrypt_data (db): failed: %@", error ? *error : (CFErrorRef)CFSTR(""));
392 }
393 CFRelease(access_control);
394 }
395 CFReleaseNull(attributes);
396 CFReleaseNull(auth_attributes);
397 }
398
399 return edata;
400 }
401
402 bool SecDbItemEnsureDecrypted(SecDbItemRef item, bool decryptSecretData, CFErrorRef *error) {
403
404 // If we haven't yet decrypted the item, make sure we do so now
405 bool result = true;
406 if (item->_edataState == kSecDbItemEncrypted || (decryptSecretData && item->_edataState == kSecDbItemSecretEncrypted)) {
407 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, error);
408 if (attr) {
409 CFDataRef edata = SecDbItemGetCachedValue(item, attr);
410 if (!edata)
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);
415 if (result)
416 item->_edataState = decryptSecretData ? kSecDbItemClean : kSecDbItemSecretEncrypted;
417 else
418 item->_edataState = kSecDbItemEncrypted;
419 }
420 }
421 return result;
422 }
423
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);
428 }
429
430 CFTypeRef value = NULL;
431 switch (attr->kind) {
432 // These have an explicit copyValue; here to shut up compiler
433 case kSecDbSHA1Attr:
434 case kSecDbEncryptedDataAttr:
435 case kSecDbPrimaryKeyAttr:
436 value = NULL;
437 break;
438 case kSecDbAccessAttr:
439 case kSecDbStringAttr:
440 case kSecDbBlobAttr:
441 case kSecDbAccessControlAttr:
442 if (attr->flags & kSecDbNotNullFlag) {
443 if (attr->flags & kSecDbDefault0Flag) {
444 value = CFSTR("0");
445 break;
446 } else if (attr->kind != kSecDbBlobAttr && attr->flags & kSecDbDefaultEmptyFlag) {
447 // blob drops through to data everything else is empty string
448 value = CFSTR("");
449 break;
450 }
451 }
452 //DROPTHROUGH
453 case kSecDbDataAttr:
454 if (attr->flags & kSecDbNotNullFlag && attr->flags & kSecDbDefaultEmptyFlag) {
455 value = CFDataCreate(CFGetAllocator(item), NULL, 0);
456 } else {
457 value = kCFNull;
458 }
459 break;
460 case kSecDbUUIDAttr:
461 value = CFDataCreate(CFGetAllocator(item), NULL, 0);
462 break;
463 case kSecDbNumberAttr:
464 case kSecDbSyncAttr:
465 case kSecDbTombAttr:
466 if (attr->flags & kSecDbNotNullFlag) {
467 int32_t zero = 0;
468 value = CFNumberCreate(CFGetAllocator(item), kCFNumberSInt32Type, &zero);
469 } else {
470 value = kCFNull;
471 }
472 break;
473 case kSecDbDateAttr:
474 if (attr->flags & kSecDbNotNullFlag && attr->flags & kSecDbDefault0Flag) {
475 value = CFDateCreate(kCFAllocatorDefault, 0.0);
476 } else {
477 value = kCFNull;
478 }
479 break;
480 case kSecDbRowIdAttr:
481 if (attr->flags & kSecDbNotNullFlag) {
482 // No can do, error?
483 }
484 value = kCFNull;
485 break;
486 case kSecDbCreationDateAttr:
487 case kSecDbModificationDateAttr:
488 value = CFDateCreate(CFGetAllocator(item), CFAbsoluteTimeGetCurrent());
489 break;
490 case kSecDbUTombAttr:
491 value = kCFNull;
492 break;
493 }
494
495 return value;
496 }
497
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
501 // it's value.
502 CFTypeRef SecDbItemGetValue(SecDbItemRef item, const SecDbAttr *desc, CFErrorRef *error) {
503 // Propagate chained errors
504 if (!desc)
505 return NULL;
506
507 if (desc->flags & kSecDbInCryptoDataFlag || desc->flags & kSecDbInAuthenticatedDataFlag || desc->flags & kSecDbReturnDataFlag) {
508 if (!SecDbItemEnsureDecrypted(item, desc->flags & kSecDbReturnDataFlag, error))
509 return NULL;
510 }
511
512 CFTypeRef value = SecDbItemGetCachedValue(item, desc);
513 if (!value) {
514 value = SecDbItemCopyValue(item, desc, error);
515 if (value) {
516 if (CFEqual(kCFNull, value)) {
517 CFRelease(value); // This is redundant but it shuts clang's static analyzer up.
518 value = kCFNull;
519 } else {
520 SecDbItemSetValue(item, desc, value, error);
521 CFRelease(value);
522 value = SecDbItemGetCachedValue(item, desc);
523 }
524 }
525 }
526 return value;
527 }
528
529 CFTypeRef SecDbItemGetValueKind(SecDbItemRef item, SecDbAttrKind descKind, CFErrorRef *error) {
530 CFTypeRef result = NULL;
531
532 const SecDbClass * itemClass = SecDbItemGetClass(item);
533 const SecDbAttr * desc = SecDbClassAttrWithKind(itemClass, descKind, error);
534
535 if (desc) {
536 result = SecDbItemGetValue(item, desc, error);
537 }
538
539 return result;
540 }
541
542
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));
551 }
552
553 if (value == NULL) {
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);
558 }
559 }
560
561 out:
562 return value;
563 }
564
565 static bool SecDbItemGetBoolValue(SecDbItemRef item, const SecDbAttr *desc, bool *bvalue, CFErrorRef *error) {
566 CFTypeRef value = SecDbItemGetValue(item, desc, error);
567 if (!value)
568 return false;
569 char cvalue;
570 *bvalue = (isNumber(value) && CFNumberGetValue(value, kCFNumberCharType, &cvalue) && cvalue == 1);
571 return true;
572 }
573
574 static void SecDbItemAppendAttributeToDescription(SecDbItemRef item, const SecDbAttr *attr, CFMutableStringRef mdesc)
575 {
576 // In non-debug builds, the following attributes aren't very useful.
577 #ifndef DEBUG
578 if (CFEqual(CFSTR("data"), attr->name)||
579 CFEqual(CFSTR("v_pk"), attr->name)) {
580 return;
581 }
582 #endif
583
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);
595 } else {
596 CFStringAppendFormat(mdesc, 0, CFSTR("%@"), value);
597 }
598 }
599 }
600
601 static CFStringRef SecDbItemCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
602 CFStringRef desc;
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);
607
608 // First, the primary key attributes
609 SecDbForEachAttrWithMask(item->class, attr, kSecDbPrimaryKeyFlag) {
610 SecDbItemAppendAttributeToDescription(item, attr, mdesc);
611 }
612
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);
618 }
619 }
620
621 // And finally, everything else
622 SecDbForEachAttr(item->class, attr) {
623 if((attr->flags & kSecDbPrimaryKeyFlag) != 0) {
624 continue;
625 }
626 if(CFEqualSafe(CFSTR("tomb"), attr->name)) {
627 continue;
628 }
629 SecDbItemAppendAttributeToDescription(item, attr, mdesc);
630 }
631 CFStringAppend(mdesc, CFSTR(">"));
632 desc = mdesc;
633 } else {
634 SecDbItemRef item = (SecDbItemRef)cf;
635 const UInt8 zero4[4] = {};
636 const UInt8 *pk = &zero4[0], *sha1 = &zero4[0];
637 char sync = 0;
638 char tomb = 0;
639 SInt64 rowid = 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;
646
647 SecDbForEachAttr(item->class, attr) {
648 CFTypeRef value;
649 switch (attr->kind) {
650 case kSecDbBlobAttr:
651 case kSecDbDataAttr:
652 case kSecDbStringAttr:
653 case kSecDbNumberAttr:
654 case kSecDbDateAttr:
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)) {
658 agrp = value;
659 } else {
660 // We don't log these, just record that we saw the attribute.
661 CFStringAppend(attrs, CFSTR(","));
662 CFStringAppend(attrs, attr->name);
663 }
664 }
665 break;
666 case kSecDbUUIDAttr:
667 if ((value = SecDbItemGetValue(item, attr, NULL))) {
668 if (CFEqual(attr->name, kSecAttrMultiUser)) {
669 if (isData(value)) {
670 CFStringAppend(attrs, CFSTR(","));
671 if (CFDataGetLength(value)) {
672 CFStringAppendHexData(attrs, value);
673 } else {
674 CFStringAppend(attrs, attr->name);
675 }
676 }
677 }
678 }
679 break;
680 case kSecDbCreationDateAttr:
681 // We don't care about this and every object has one.
682 break;
683 case kSecDbModificationDateAttr:
684 value = SecDbItemGetValue(item, attr, NULL);
685 if (isDate(value))
686 mdat = der_encode_generalizedtime_body(CFDateGetAbsoluteTime(value), NULL, mdat, &mdatbuf[31]);
687 break;
688 case kSecDbSHA1Attr:
689 value = SecDbItemGetValue(item, attr, NULL);
690 if (isData(value) && CFDataGetLength(value) >= (CFIndex)sizeof(zero4))
691 sha1 = CFDataGetBytePtr(value);
692 break;
693 case kSecDbRowIdAttr:
694 value = SecDbItemGetValue(item, attr, NULL);
695 if (isNumber(value))
696 CFNumberGetValue(value, kCFNumberSInt64Type, &rowid);
697 break;
698 case kSecDbPrimaryKeyAttr:
699 value = SecDbItemGetValue(item, attr, NULL);
700 if (isData(value))
701 pk = CFDataGetBytePtr(value);
702 break;
703 case kSecDbSyncAttr:
704 value = SecDbItemGetValue(item, attr, NULL);
705 if (isNumber(value))
706 CFNumberGetValue(value, kCFNumberCharType, &sync);
707 break;
708 case kSecDbTombAttr:
709 value = SecDbItemGetValue(item, attr, NULL);
710 if (isNumber(value))
711 CFNumberGetValue(value, kCFNumberCharType, &tomb);
712 break;
713 case kSecDbAccessAttr:
714 value = SecDbItemGetValue(item, attr, NULL);
715 if (isString(value))
716 access = value;
717 break;
718 case kSecDbUTombAttr:
719 value = SecDbItemGetValue(item, attr, NULL);
720 if (isBoolean(value))
721 utomb = value;
722 case kSecDbAccessControlAttr:
723 /* TODO: Add formatting of ACLs. */
724 break;
725 }
726 }
727
728 desc = CFStringCreateWithFormat(CFGetAllocator(cf), NULL,
729 CFSTR(
730 "%s,"
731 "%@,"
732 "%02X%02X%02X%02X,"
733 "%s,"
734 "%@,"
735 "%@,"
736 "%"PRId64
737 "%@,"
738 "%s,"
739 "%s"
740 "%02X%02X%02X%02X"),
741 tomb ? "T" : "O",
742 item->class->name,
743 pk[0], pk[1], pk[2], pk[3],
744 sync ? "S" : "L",
745 access,
746 agrp,
747 rowid,
748 attrs,
749 mdat,
750 utomb ? (CFEqual(utomb, kCFBooleanFalse) ? "F," : "T,") : "",
751 sha1[0], sha1[1], sha1[2], sha1[3]);
752 CFReleaseSafe(attrs);
753 }
754
755 return desc;
756 }
757
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);
764 }
765
766 static CFHashCode SecDbItemHash(CFTypeRef cf) {
767 SecDbItemRef item = (SecDbItemRef)cf;
768 CFDataRef digest = SecDbItemGetSHA1(item, NULL);
769 CFHashCode code;
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);
773 return code;
774 }
775
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;
781 if (item1)
782 digest1 = SecDbItemGetSHA1(item1, NULL);
783 if (item2)
784 digest2 = SecDbItemGetSHA1(item2, NULL);
785 Boolean equal = CFEqual(digest1, digest2);
786 return equal;
787 }
788
789 CFGiblisWithHashFor(SecDbItem)
790
791 static SecDbItemRef SecDbItemCreate(CFAllocatorRef allocator, const SecDbClass *class, keybag_handle_t keybag) {
792 SecDbItemRef item = CFTypeAllocate(SecDbItem, struct SecDbItem, allocator);
793 item->class = class;
794 item->attributes = CFDictionaryCreateMutableForCFTypes(allocator);
795 item->keybag = keybag;
796 item->_edataState = kSecDbItemDirty;
797 item->cryptoOp = kAKSKeyOpDecrypt;
798
799 return item;
800 }
801
802 const SecDbClass *SecDbItemGetClass(SecDbItemRef item) {
803 return item->class;
804 }
805
806 keybag_handle_t SecDbItemGetKeybag(SecDbItemRef item) {
807 return item->keybag;
808 }
809
810 bool SecDbItemSetKeybag(SecDbItemRef item, keybag_handle_t keybag, CFErrorRef *error) {
811 if (!SecDbItemEnsureDecrypted(item, true, error))
812 return false;
813 if (item->keybag != keybag) {
814 item->keybag = keybag;
815 if (item->_edataState == kSecDbItemClean) {
816 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, NULL), kCFNull, NULL);
817 }
818 }
819
820 return true;
821 }
822
823 bool SecDbItemSetValue(SecDbItemRef item, const SecDbAttr *desc, CFTypeRef value, CFErrorRef *error) {
824 // Propagate chained errors.
825 if (!desc)
826 return false;
827
828 if (!value)
829 value = kCFNull;
830
831 if (desc->setValue)
832 return desc->setValue(item, desc, value, error);
833
834 if (desc->flags & kSecDbInCryptoDataFlag || desc->flags & kSecDbInAuthenticatedDataFlag) {
835 if (!SecDbItemEnsureDecrypted(item, true, error)) {
836 return false;
837 }
838 }
839
840 bool changed = false;
841 CFTypeRef attr = NULL;
842 switch (desc->kind) {
843 case kSecDbPrimaryKeyAttr:
844 case kSecDbDataAttr:
845 attr = copyData(value);
846 break;
847 case kSecDbEncryptedDataAttr:
848 attr = copyData(value);
849 if (attr) {
850 if (item->_edataState == kSecDbItemEncrypting)
851 item->_edataState = kSecDbItemClean;
852 else
853 item->_edataState = kSecDbItemEncrypted;
854 } else if (!value || CFEqual(kCFNull, value)) {
855 item->_edataState = kSecDbItemDirty;
856 }
857 break;
858 case kSecDbBlobAttr:
859 case kSecDbAccessControlAttr:
860 attr = copyBlob(value);
861 break;
862 case kSecDbDateAttr:
863 case kSecDbCreationDateAttr:
864 case kSecDbModificationDateAttr:
865 attr = copyDate(value);
866 break;
867 case kSecDbNumberAttr:
868 case kSecDbSyncAttr:
869 case kSecDbTombAttr:
870 case kSecDbRowIdAttr:
871 attr = copyNumber(value);
872 break;
873 case kSecDbAccessAttr:
874 case kSecDbStringAttr:
875 attr = copyString(value);
876 break;
877 case kSecDbSHA1Attr:
878 attr = copySHA1(value);
879 break;
880 case kSecDbUTombAttr:
881 attr = CFRetainSafe(asBoolean(value, NULL));
882 break;
883 case kSecDbUUIDAttr:
884 attr = copyUUID(value);
885 break;
886 }
887
888 if (attr) {
889 CFTypeRef ovalue = CFDictionaryGetValue(item->attributes, desc->name);
890 changed = (!ovalue || !CFEqual(ovalue, attr));
891 CFDictionarySetValue(item->attributes, desc->name, attr);
892 CFRelease(attr);
893 } else {
894 if (value && !CFEqual(kCFNull, value)) {
895 SecError(errSecItemInvalidValue, error, CFSTR("attribute %@: value: %@ failed to convert"), desc->name, value);
896 return false;
897 }
898 CFTypeRef ovalue = CFDictionaryGetValue(item->attributes, desc->name);
899 changed = (ovalue && !CFEqual(ovalue, kCFNull));
900 CFDictionaryRemoveValue(item->attributes, desc->name);
901 }
902
903 if (changed) {
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));
912 }
913
914 return true;
915 }
916
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))
921 return false;
922 }
923 return true;
924 }
925
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);
930 }
931 }
932 return false;
933 }
934
935 bool SecItemPreserveAttribute(SecDbItemRef target, SecDbItemRef source, const SecDbAttr* attr) {
936 CFErrorRef cferror = nil;
937 CFTypeRef v = SecDbItemGetValue(source, attr, &cferror);
938 if(cferror) {
939 secnotice("secitem", "Merging: unable to get attribute (%@) : %@", attr->name, cferror);
940 CFReleaseNull(cferror);
941 return false;
942 }
943 if(!v || CFEqualSafe(v, kCFNull)) {
944 return true;
945 }
946 secnotice("secitem", "Preserving existing data for %@", attr->name);
947 SecDbItemSetValue(target, attr, v, &cferror);
948 if(cferror) {
949 secnotice("secitem", "Unable to set attribute (%@) : %@", attr->name, cferror);
950 CFReleaseNull(cferror);
951 return false;
952 }
953 return true;
954 }
955
956
957 bool SecDbItemSetAccessControl(SecDbItemRef item, SecAccessControlRef access_control, CFErrorRef *error) {
958 bool ok = true;
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);
965 CFRelease(data);
966 }
967 return ok;
968 }
969
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))
973 CFReleaseNull(item);
974 return item;
975 }
976
977 static CFTypeRef
978 SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator, sqlite3_stmt *stmt, const SecDbAttr *attr, int col, CFErrorRef *error) {
979 CFTypeRef value = NULL;
980 switch (attr->kind) {
981 case kSecDbDateAttr:
982 case kSecDbCreationDateAttr:
983 case kSecDbModificationDateAttr:
984 value = SecDbColumnCopyDate(allocator, stmt, col, error);
985 break;
986 case kSecDbBlobAttr:
987 case kSecDbNumberAttr:
988 switch (sqlite3_column_type(stmt, col)) {
989 case SQLITE_INTEGER:
990 value = SecDbColumnCopyNumber(allocator, stmt, col, error);
991 break;
992 case SQLITE_FLOAT:
993 value = SecDbColumnCopyDouble(allocator, stmt, col, error);
994 break;
995 case SQLITE_TEXT:
996 value = SecDbColumnCopyString(allocator, stmt, col, error,
997 attr->flags);
998 break;
999 case SQLITE_BLOB:
1000 value = SecDbColumnCopyData(allocator, stmt, col, error);
1001 break;
1002 case SQLITE_NULL:
1003 value = kCFNull;
1004 break;
1005 }
1006 break;
1007 case kSecDbAccessAttr:
1008 case kSecDbStringAttr:
1009 value = SecDbColumnCopyString(allocator, stmt, col, error,
1010 attr->flags);
1011 break;
1012 case kSecDbDataAttr:
1013 case kSecDbUUIDAttr:
1014 case kSecDbSHA1Attr:
1015 case kSecDbPrimaryKeyAttr:
1016 case kSecDbEncryptedDataAttr:
1017 value = SecDbColumnCopyData(allocator, stmt, col, error);
1018 break;
1019 case kSecDbSyncAttr:
1020 case kSecDbTombAttr:
1021 value = SecDbColumnCopyNumber(allocator, stmt, col, error);
1022 break;
1023 case kSecDbRowIdAttr:
1024 value = SecDbColumnCopyNumber64(allocator, stmt, col, error);
1025 break;
1026 case kSecDbAccessControlAttr:
1027 case kSecDbUTombAttr:
1028 /* This attributes does not have any database column associated, exists only inside encrypted blob as metadata. */
1029 break;
1030 }
1031 return value;
1032 }
1033
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);
1036 int col = 0;
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));
1041
1042 CFDictionarySetValue(item->attributes, SecDbAttrGetHashName(attr), value);
1043 CFRelease(value);
1044 }
1045
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;
1049 }
1050 }
1051
1052 errOut:
1053 return item;
1054 }
1055
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);
1060 if (edata_attr) {
1061 if (!SecDbItemSetValue(item, edata_attr, edata, error))
1062 CFReleaseNull(item);
1063 }
1064 return item;
1065 }
1066
1067 // TODO: Hack -- Replace with real filtering
1068
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()));
1074 }
1075
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());
1079 }
1080
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)) {
1090 if (!value)
1091 SecError(errSecParam, error, CFSTR("NULL value in dictionary"));
1092 } else {
1093 value = SecDbItemGetValue(item, attr, error);
1094 }
1095 if (!value || !SecDbItemSetValue(new_item, attr, value, error)) {
1096 CFReleaseNull(new_item);
1097 break;
1098 }
1099 }
1100 }
1101 return new_item;
1102 }
1103
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);
1107 if (!old_date)
1108 return false;
1109 CFDateRef new_date = SecDbItemGetValue(new_item, attr, error);
1110 if (!new_date)
1111 return false;
1112 bool ok = true;
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);
1118 }
1119 }
1120 return ok;
1121 }
1122
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);
1127 }
1128
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);
1136 break;
1137 }
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);
1143 break;
1144 }
1145 } else if (attr->kind == kSecDbModificationDateAttr) {
1146 if(tombstone_time_from_item) {
1147 SecItemPreserveAttribute(new_item, item, attr);
1148 }
1149
1150 if (!SecDbItemMakeAttrYounger(new_item, item, attr, error)) {
1151 CFReleaseNull(new_item);
1152 break;
1153 }
1154 } else if (makeTombStone && attr->kind == kSecDbUTombAttr) {
1155 if (makeTombStone)
1156 SecDbItemSetValue(new_item, attr, makeTombStone, error);
1157 }
1158 }
1159
1160 return new_item;
1161 }
1162
1163 bool SecDbItemIsEngineInternalState(SecDbItemRef itemObject) {
1164 // Only used for controlling logging
1165 // Use agrp=com.apple.security.sos, since it is not encrypted
1166 if (!itemObject) {
1167 return false;
1168 }
1169 const SecDbAttr *agrp = SecDbAttrWithKey(SecDbItemGetClass(itemObject), kSecAttrAccessGroup, NULL);
1170 CFTypeRef cfval = SecDbItemGetValue(itemObject, agrp, NULL);
1171 return cfval && CFStringCompareSafe(cfval, kSOSInternalAccessGroup, NULL) == kCFCompareEqualTo;
1172 }
1173
1174
1175 // MARK: -
1176 // MARK: SQL Construction helpers -- These should become private in the future
1177
1178 void SecDbAppendElement(CFMutableStringRef sql, CFStringRef value, bool *needComma) {
1179 assert(needComma);
1180 if (*needComma) {
1181 CFStringAppend(sql, CFSTR(","));
1182 } else {
1183 *needComma = true;
1184 }
1185 CFStringAppend(sql, value);
1186 }
1187
1188 static void SecDbAppendElementEquals(CFMutableStringRef sql, CFStringRef value, bool *needComma) {
1189 SecDbAppendElement(sql, value, needComma);
1190 CFStringAppend(sql, CFSTR("=?"));
1191 }
1192
1193 /* Append AND is needWhere is NULL or *needWhere is false. Append WHERE
1194 otherwise. Upon return *needWhere will be false. */
1195 void
1196 SecDbAppendWhereOrAnd(CFMutableStringRef sql, bool *needWhere) {
1197 if (!needWhere || !*needWhere) {
1198 CFStringAppend(sql, CFSTR(" AND "));
1199 } else {
1200 CFStringAppend(sql, CFSTR(" WHERE "));
1201 *needWhere = false;
1202 }
1203 }
1204
1205 void
1206 SecDbAppendWhereOrAndEquals(CFMutableStringRef sql, CFStringRef col, bool *needWhere) {
1207 SecDbAppendWhereOrAnd(sql, needWhere);
1208 CFStringAppend(sql, col);
1209 CFStringAppend(sql, CFSTR("=?"));
1210 }
1211
1212 void
1213 SecDbAppendWhereOrAndNotEquals(CFMutableStringRef sql, CFStringRef col, bool *needWhere) {
1214 SecDbAppendWhereOrAnd(sql, needWhere);
1215 CFStringAppend(sql, col);
1216 CFStringAppend(sql, CFSTR("!=?"));
1217 }
1218
1219 static void SecDbAppendCountArgsAndCloseParen(CFMutableStringRef sql, CFIndex count) {
1220 bool needComma = false;
1221 while (count-- > 0)
1222 SecDbAppendElement(sql, CFSTR("?"), &needComma);
1223 CFStringAppend(sql, CFSTR(")"));
1224 }
1225
1226 void
1227 SecDbAppendWhereOrAndIn(CFMutableStringRef sql, CFStringRef col, bool *needWhere, CFIndex count) {
1228 if (count == 1)
1229 return SecDbAppendWhereOrAndEquals(sql, col, needWhere);
1230 SecDbAppendWhereOrAnd(sql, needWhere);
1231 CFStringAppend(sql, col);
1232 CFStringAppend(sql, CFSTR(" IN ("));
1233 SecDbAppendCountArgsAndCloseParen(sql, count);
1234 }
1235
1236 void
1237 SecDbAppendWhereOrAndNotIn(CFMutableStringRef sql, CFStringRef col, bool *needWhere, CFIndex count) {
1238 if (count == 1)
1239 return SecDbAppendWhereOrAndNotEquals(sql, col, needWhere);
1240 SecDbAppendWhereOrAnd(sql, needWhere);
1241 CFStringAppend(sql, col);
1242 CFStringAppend(sql, CFSTR(" NOT IN ("));
1243 SecDbAppendCountArgsAndCloseParen(sql, count);
1244 }
1245
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)) {
1255 ++used_attr;
1256 SecDbAppendElement(sql, attr->name, &needComma);
1257 }
1258 }
1259 CFStringAppend(sql, CFSTR(")VALUES(?"));
1260 while (used_attr-- > 1) {
1261 CFStringAppend(sql, CFSTR(",?"));
1262 }
1263 CFStringAppend(sql, CFSTR(")"));
1264 return sql;
1265
1266 }
1267
1268 static bool SecDbItemInsertBind(SecDbItemRef item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr)(const SecDbAttr *attr)) {
1269 bool ok = true;
1270 int param = 0;
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);
1276 if (!ok)
1277 break;
1278 }
1279 }
1280 return ok;
1281 }
1282
1283 sqlite3_int64 SecDbItemGetRowId(SecDbItemRef item, CFErrorRef *error) {
1284 sqlite3_int64 row_id = 0;
1285 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error);
1286 if (attr) {
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);
1290 }
1291
1292 return row_id;
1293 }
1294
1295 static CFNumberRef SecDbItemCreateRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error) {
1296 return CFNumberCreate(CFGetAllocator(item), kCFNumberSInt64Type, &rowid);
1297 }
1298
1299 bool SecDbItemSetRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error) {
1300 bool ok = true;
1301 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error);
1302 if (attr) {
1303 CFNumberRef value = SecDbItemCreateRowId(item, rowid, error);
1304 if (!value)
1305 return false;
1306
1307 ok = SecDbItemSetValue(item, attr, value, error);
1308 CFRelease(value);
1309 }
1310 return ok;
1311 }
1312
1313 bool SecDbItemClearRowId(SecDbItemRef item, CFErrorRef *error) {
1314 bool ok = true;
1315 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error);
1316 if (attr) {
1317 CFDictionaryRemoveValue(item->attributes, attr->name);
1318 //ok = SecDbItemSetValue(item, attr, kCFNull, error);
1319 }
1320 return ok;
1321 }
1322
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);
1326 }
1327
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;
1334 }
1335 CFReleaseSafe(localError);
1336 return is_syncable_or_corrupted;
1337 }
1338
1339 bool SecDbItemIsSyncable(SecDbItemRef item) {
1340 bool is_syncable;
1341 if (SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, NULL), &is_syncable, NULL))
1342 return is_syncable;
1343 return false;
1344 }
1345
1346 bool SecDbItemSetSyncable(SecDbItemRef item, bool sync, CFErrorRef *error)
1347 {
1348 return SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, error), sync ? kCFBooleanTrue : kCFBooleanFalse, error);
1349 }
1350
1351 bool SecDbItemIsTombstone(SecDbItemRef item) {
1352 bool is_tomb;
1353 if (SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbTombAttr, NULL), &is_tomb, NULL))
1354 return is_tomb;
1355 return false;
1356 }
1357
1358 CFDataRef SecDbItemGetPrimaryKey(SecDbItemRef item, CFErrorRef *error) {
1359 return SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbPrimaryKeyAttr, error), error);
1360 }
1361
1362 CFDataRef SecDbItemGetSHA1(SecDbItemRef item, CFErrorRef *error) {
1363 return SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, error), error);
1364 }
1365
1366 static SecDbQueryRef SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item, CFErrorRef *error) {
1367 CFMutableDictionaryRef dict = SecDbItemCopyPListWithMask(item, kSecDbPrimaryKeyFlag, error);
1368 if (!dict)
1369 return NULL;
1370
1371 SecDbQueryRef query = query_create(item->class, NULL, NULL, NULL, error);
1372 if (query) {
1373 CFReleaseSafe(query->q_item);
1374 query->q_item = dict;
1375 }
1376 else
1377 CFRelease(dict);
1378
1379 return query;
1380 }
1381
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));
1387 bool akpu = false;
1388
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;
1395
1396 acccData = (CFDataRef)SecDbItemGetValue(item, desc, &localError);
1397 if (isData(acccData)) {
1398 accc = SecAccessControlCreateFromData(CFGetAllocator(item), acccData, &localError);
1399 }
1400
1401 if (accc && CFEqualSafe(SecAccessControlGetProtection(accc), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)) {
1402 akpu = true;
1403 secwarning("cannot decrypt item %@, item is irrecoverably lost with older passcode (error %@)", item, localError);
1404 } else {
1405 secerror("error %@ reading item %@ (corrupted)", localError, item);
1406 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem);
1407 }
1408 CFReleaseNull(localError);
1409 *is_corrupt = true;
1410 }
1411 }
1412
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);
1422 *is_corrupt = true;
1423 }
1424
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);
1429 if (value) {
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);
1433 *is_corrupt = true;
1434 break;
1435 }
1436 } else {
1437 if (SecErrorGetOSStatus(localError) == errSecDecode) {
1438 // We failed to decrypt the item
1439 if (akpu) {
1440 secwarning("attribute %@: %@ item %@ (item lost with older passcode)", attr->name, localError, item);
1441 } else {
1442 secerror("error attribute %@: %@ item %@ (corrupted)", attr->name, localError, item);
1443 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem);
1444 }
1445 *is_corrupt = true;
1446 CFReleaseNull(localError);
1447 }
1448 break;
1449 }
1450 }
1451 }
1452
1453 CFReleaseSafe(computedSHA1);
1454 CFReleaseSafe(storedSHA1);
1455 return SecErrorPropagate(localError, error);
1456 }
1457
1458 static void SecDbItemRecordUpdate(SecDbConnectionRef dbconn, SecDbItemRef deleted, SecDbItemRef inserted) {
1459 SecDbRecordChange(dbconn, deleted, inserted);
1460 }
1461
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);
1465 };
1466
1467 if (!SecDbItemEnsureDecrypted(item, true, error)) {
1468 return false;
1469 }
1470
1471 CFStringRef sql = SecDbItemCopyInsertSQL(item, use_attr);
1472 __block bool ok = sql;
1473 if (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));
1478 });
1479 CFRelease(sql);
1480 }
1481 if (ok) {
1482 secnotice("item", "inserted %@", item);
1483 SecDbItemRecordUpdate(dbconn, NULL, item);
1484 } else {
1485 if (SecDbItemIsEngineInternalState(item)) {
1486 secdebug ("item", "insert failed for item %@ with %@", item, error ? *error : NULL);
1487 } else {
1488 secnotice("item", "insert failed for item %@ with %@", item, error ? *error : NULL);
1489 }
1490 }
1491
1492 return ok;
1493 }
1494
1495 bool SecErrorIsSqliteDuplicateItemError(CFErrorRef error) {
1496 return error && CFErrorGetCode(error) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(error));
1497 }
1498
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);
1504 if (query) {
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;
1512 if (is_corrupt) {
1513 // If old_item is corrupted pretend it's not there and just replace it.
1514 replace = item;
1515 CFRetain(replace);
1516 if(error)
1517 CFReleaseNull(*error); //item is corrupted and will be replaced, so drop the error
1518 } else if (ok && duplicate) {
1519 duplicate(old_item, &replace);
1520 }
1521 if (replace) {
1522 const SecDbAttr *rowid_attr = SecDbClassAttrWithKind(old_item->class, kSecDbRowIdAttr, error);
1523 CFNumberRef oldrowid = SecDbItemGetCachedValue(old_item, rowid_attr);
1524 if (oldrowid) {
1525 ok = SecDbItemSetValue(replace, rowid_attr, oldrowid, &localError);
1526 if (ok && !is_corrupt) {
1527 ok = SecDbItemMakeYounger(replace, old_item, error);
1528 }
1529 ok = ok && SecDbItemDoUpdate(old_item, replace, dbconn, &localError, ^bool (const SecDbAttr *attr) {
1530 return attr->kind == kSecDbRowIdAttr;
1531 });
1532 } else {
1533 ok = SecError(errSecInternal, &localError, CFSTR("no rowid for %@"), old_item);
1534 }
1535 CFRelease(replace);
1536 if (ok)
1537 CFReleaseNull(localError); // Clear the error, since we replaced the item.
1538 }
1539 });
1540 SecDbItemSetCredHandle(item, query->q_use_cred_handle);
1541 ok &= query_destroy(query, error);
1542 }
1543 }
1544
1545 return ok & SecErrorPropagate(localError, error); // Don't use && here!
1546 }
1547
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)) {
1551 CFRetain(item);
1552 *replace = item;
1553
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);
1560 }
1561 }
1562 }
1563 }
1564 });
1565 }
1566
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) {
1575 ++used_attr;
1576 SecDbAppendElementEquals(sql, attr->name, &needComma);
1577 }
1578
1579 bool needWhere = true;
1580 SecDbForEachAttr(old_item->class, attr) {
1581 if (use_attr_in_where(attr)) {
1582 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere);
1583 }
1584 }
1585
1586 return sql;
1587 }
1588
1589 static bool SecDbItemUpdateBind(SecDbItemRef old_item, SecDbItemRef new_item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr_in_where)(const SecDbAttr *attr)) {
1590 bool ok = true;
1591 int param = 0;
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);
1596 if (!ok)
1597 break;
1598 }
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);
1604 if (!ok)
1605 break;
1606 }
1607 }
1608 return ok;
1609 }
1610
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;
1615 if (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);
1618 });
1619 CFRelease(sql);
1620 }
1621 if (ok) {
1622 if (SecDbItemIsEngineInternalState(old_item)) {
1623 secdebug ("item", "replaced %@ in %@", old_item, dbconn);
1624 secdebug ("item", " with %@ in %@", new_item, dbconn);
1625 } else {
1626 secnotice("item", "replaced %@ in %@", old_item, dbconn);
1627 secnotice("item", " with %@ in %@", new_item, dbconn);
1628 }
1629 SecDbItemRecordUpdate(dbconn, old_item, new_item);
1630 }
1631 return ok;
1632 }
1633
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);
1642 }
1643 }
1644
1645 return sql;
1646 }
1647
1648 static bool SecDbItemDeleteBind(SecDbItemRef item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr_in_where)(const SecDbAttr *attr)) {
1649 bool ok = true;
1650 int param = 0;
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);
1656 if (!ok)
1657 break;
1658 }
1659 }
1660 return ok;
1661 }
1662
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;
1666 if (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);
1669 });
1670 CFRelease(sql);
1671 }
1672 return ok;
1673 }
1674
1675 bool SecDbItemDoDeleteSilently(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) {
1676 return SecDbItemDoDeleteOnly(item, dbconn, error, ^bool(const SecDbAttr *attr) {
1677 return attr->kind == kSecDbRowIdAttr;
1678 });
1679 }
1680
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);
1683 if (ok) {
1684 secnotice("item", "deleted %@ from %@", item, dbconn);
1685 SecDbItemRecordUpdate(dbconn, item, NULL);
1686 }
1687 return ok;
1688 }
1689
1690 #if 0
1691 static bool SecDbItemDeleteTombstone(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) {
1692 bool ok = true;
1693 // TODO: Treat non decryptable items like tombstones here too and delete them
1694 SecDbItemRef tombstone = SecDbItemCopyTombstone(item, error);
1695 ok = tombstone;
1696 if (tombstone) {
1697 ok = SecDbItemClearRowId(tombstone, error);
1698 if (ok) {
1699 ok = SecDbItemDoDelete(tombstone, dbconn, error, ^bool (const SecDbAttr *attr) {
1700 return SecDbIsTombstoneDbSelectAttr(attr);
1701 });
1702 }
1703 CFRelease(tombstone);
1704 }
1705 return ok;
1706 }
1707 #endif
1708
1709 static bool
1710 isCKKSEnabled(void)
1711 {
1712 #if OCTAGON
1713 return SecCKKSIsEnabled();
1714 #else
1715 return false;
1716 #endif
1717 }
1718
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;
1723
1724 CFDataRef old_pk = SecDbItemGetPrimaryKey(old_item, error);
1725 CFDataRef new_pk = SecDbItemGetPrimaryKey(new_item, error);
1726
1727 ok = old_pk && new_pk;
1728
1729 bool pk_equal = ok && CFEqual(old_pk, new_pk);
1730 if (pk_equal) {
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.
1735
1736 s3dl_item_make_new_uuid(new_item, uuid_from_primary_key, error);
1737 }
1738 ok = ok && SecDbItemDoUpdate(old_item, new_item, dbconn, &localError, ^bool(const SecDbAttr *attr) {
1739 return attr->kind == kSecDbRowIdAttr;
1740 });
1741
1742 if (localError) {
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);
1747 ok = query;
1748 if (query) {
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);
1758 }
1759 if (ok && (is_corrupt || is_tomb)) {
1760 ok = SecDbItemDoDelete(old_item, dbconn, error, ^bool (const SecDbAttr *attr) {
1761 return attr->kind == kSecDbRowIdAttr;
1762 });
1763 ok = ok && SecDbItemDoUpdate(duplicate_item, new_item, dbconn, error, ^bool (const SecDbAttr *attr) {
1764 return attr->kind == kSecDbRowIdAttr;
1765 });
1766 CFReleaseNull(localError);
1767 }
1768 });
1769 ok &= query_destroy(query, error);
1770 }
1771 }
1772
1773 if (localError) {
1774 ok = false;
1775 if (error && *error == NULL) {
1776 *error = localError;
1777 localError = NULL;
1778 }
1779 CFReleaseSafe(localError);
1780 }
1781 }
1782
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);
1787 ok = tombstone;
1788 if (tombstone) {
1789 ok = (SecDbItemClearRowId(tombstone, error) &&
1790 SecDbItemDoInsert(tombstone, dbconn, error));
1791 CFRelease(tombstone);
1792 }
1793 }
1794
1795 return ok;
1796 }
1797
1798 // Replace the object with a tombstone
1799 bool SecDbItemDelete(SecDbItemRef item, SecDbConnectionRef dbconn, CFBooleanRef makeTombstone, bool tombstone_time_from_item, CFErrorRef *error) {
1800 bool ok = false;
1801 if (!CFEqualSafe(makeTombstone, kCFBooleanFalse)) {
1802 SecDbItemRef tombstone = SecDbItemCopyTombstone(item, makeTombstone, tombstone_time_from_item, error);
1803 if (tombstone) {
1804 ok = SecDbItemDoUpdate(item, tombstone, dbconn, error, ^bool(const SecDbAttr *attr) {
1805 return attr->kind == kSecDbRowIdAttr;
1806 });
1807 CFRelease(tombstone);
1808 }
1809 } else {
1810 ok = SecDbItemDoDelete(item, dbconn, error, ^bool(const SecDbAttr *attr) {
1811 return attr->kind == kSecDbRowIdAttr;
1812 });
1813 }
1814 return ok;
1815 }
1816
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);
1828 }
1829
1830 // From which table?
1831 CFStringAppend(sql, CFSTR(" FROM "));
1832 CFStringAppend(sql, query->q_class->name);
1833
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);
1842 if (length > 0) {
1843 CFTypeRef head = CFArrayGetValueAtIndex(array, 0);
1844 if (CFEqualSafe(head, kCFNull)) {
1845 SecDbAppendWhereOrAndNotIn(sql, attr->name, &needWhere, length - 1);
1846 } else {
1847 SecDbAppendWhereOrAndIn(sql, attr->name, &needWhere, length);
1848 }
1849 }
1850 } else {
1851 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere);
1852 }
1853 }
1854 }
1855 // Append SQL for access groups and limits.
1856 if (add_where_sql)
1857 add_where_sql(sql, &needWhere);
1858
1859 return sql;
1860 }
1861
1862 static bool SecDbItemSelectBindValue(SecDbQueryRef query, sqlite3_stmt *stmt, int param, const SecDbAttr *attr, CFTypeRef inValue, CFErrorRef *error) {
1863 bool ok = true;
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);
1868 } else {
1869 value = SecDbAttrCopyValueForDb(attr, inValue, error);
1870 }
1871 ok = ok && value != NULL && SecDbBindObject(stmt, ++param, value, error);
1872 CFReleaseSafe(value);
1873 return ok;
1874 }
1875
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)) {
1890 range.length--;
1891 range.location++;
1892 }
1893 }
1894 CFArrayApplyFunction(array, range, apply_block_1, (void (^)(const void *value)) ^(const void *arrayValue) {
1895 ok = SecDbItemSelectBindValue(query, stmt, param++, attr, arrayValue, error);
1896 });
1897 } else {
1898 ok = SecDbItemSelectBindValue(query, stmt, param++, attr, value, error);
1899 }
1900
1901 if (!ok)
1902 break;
1903 }
1904 }
1905 // TODO: Bind arguments for access groups and limits.
1906 if (bind_added_where)
1907 bind_added_where(stmt, ++param);
1908
1909 return ok;
1910 }
1911
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;
1922 };
1923 }
1924 if (use_attr_in_where == NULL) {
1925 use_attr_in_where = ^bool (const SecDbAttr* attr) { return false; };
1926 }
1927
1928 CFStringRef sql = SecDbItemCopySelectSQL(query, return_attr, use_attr_in_where, add_where_sql);
1929 if (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);
1934 if (item) {
1935 CFRetainAssign(item->credHandle, query->q_use_cred_handle);
1936 handle_row(item, stop);
1937 CFRelease(item);
1938 } else {
1939 //*stop = true;
1940 //ok = false;
1941 }
1942 }));
1943 });
1944 CFRelease(sql);
1945 } else {
1946 ok = false;
1947 }
1948 return ok;
1949 }
1950