]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecDbItem.c
Security-58286.230.21.tar.gz
[apple/security.git] / OSX / sec / 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 #include <securityd/SecDbItem.h>
30 #include <securityd/SecDbKeychainItem.h>
31 #include <securityd/SecItemDb.h>
32 #include <utilities/SecCFWrappers.h>
33 #include <utilities/SecCFCCWrappers.h>
34 #include <utilities/der_date.h>
35 #include <utilities/der_plist.h>
36 #include <utilities/debugging.h>
37
38 #include <Security/SecBasePriv.h>
39 #include <Security/SecInternal.h>
40 #include <corecrypto/ccsha1.h>
41 #include <Security/SecItem.h>
42 #include <Security/SecItemPriv.h>
43 #include <Security/SecAccessControl.h>
44 #include <Security/SecAccessControlPriv.h>
45 #include <securityd/SecItemSchema.h>
46
47 #include <keychain/ckks/CKKS.h>
48
49 // MARK: type converters
50
51 CFStringRef copyString(CFTypeRef obj) {
52 CFTypeID tid = CFGetTypeID(obj);
53 if (tid == CFStringGetTypeID()) {
54 return CFStringCreateCopy(0, obj);
55 }else if (tid == CFDataGetTypeID()) {
56 return CFStringCreateFromExternalRepresentation(0, obj, kCFStringEncodingUTF8);
57 } else if (tid == CFUUIDGetTypeID()) {
58 return CFUUIDCreateString(NULL, obj);
59 } else {
60 return NULL;
61 }
62 }
63
64 CFDataRef copyData(CFTypeRef obj) {
65 CFTypeID tid = CFGetTypeID(obj);
66 if (tid == CFDataGetTypeID()) {
67 return CFDataCreateCopy(0, obj);
68 } else if (tid == CFStringGetTypeID()) {
69 return CFStringCreateExternalRepresentation(0, obj, kCFStringEncodingUTF8, 0);
70 } else if (tid == CFNumberGetTypeID()) {
71 SInt32 value;
72 CFNumberGetValue(obj, kCFNumberSInt32Type, &value);
73 return CFDataCreate(0, (const UInt8 *)&value, sizeof(value));
74 } else {
75 return NULL;
76 }
77 }
78
79 CFTypeRef copyUUID(CFTypeRef obj) {
80 CFTypeID tid = CFGetTypeID(obj);
81 if (tid == CFDataGetTypeID()) {
82 CFIndex length = CFDataGetLength(obj);
83 if (length != 0 && length != 16)
84 return NULL;
85 return CFDataCreateCopy(NULL, obj);
86 } else if (tid == CFNullGetTypeID()) {
87 return CFDataCreate(NULL, NULL, 0);
88 } else if (tid == CFUUIDGetTypeID()) {
89 CFUUIDBytes uuidbytes = CFUUIDGetUUIDBytes(obj);
90 CFDataRef uuiddata = CFDataCreate(NULL, (void*) &uuidbytes, sizeof(uuidbytes));
91 return uuiddata;
92 } else {
93 return NULL;
94 }
95 }
96
97
98 CFTypeRef copyBlob(CFTypeRef obj) {
99 CFTypeID tid = CFGetTypeID(obj);
100 if (tid == CFDataGetTypeID()) {
101 return CFDataCreateCopy(0, obj);
102 } else if (tid == CFStringGetTypeID()) {
103 return CFStringCreateCopy(0, obj);
104 } else if (tid == CFNumberGetTypeID()) {
105 CFRetain(obj);
106 return obj;
107 } else {
108 return NULL;
109 }
110 }
111
112 CFDataRef copySHA1(CFTypeRef obj) {
113 CFTypeID tid = CFGetTypeID(obj);
114 if (tid == CFDataGetTypeID() && CFDataGetLength(obj) == CCSHA1_OUTPUT_SIZE) {
115 return CFDataCreateCopy(CFGetAllocator(obj), obj);
116 } else {
117 return NULL;
118 }
119 }
120
121 CFTypeRef copyNumber(CFTypeRef obj) {
122 CFTypeID tid = CFGetTypeID(obj);
123 if (tid == CFNumberGetTypeID()) {
124 CFRetain(obj);
125 return obj;
126 } else if (tid == CFBooleanGetTypeID()) {
127 SInt32 value = CFBooleanGetValue(obj);
128 return CFNumberCreate(0, kCFNumberSInt32Type, &value);
129 } else if (tid == CFStringGetTypeID()) {
130 SInt32 value = CFStringGetIntValue(obj);
131 CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) value);
132 /* If a string converted to an int isn't equal to the int printed as
133 a string, return a CFStringRef instead. */
134 if (!CFEqual(t, obj)) {
135 CFRelease(t);
136 return CFStringCreateCopy(0, obj);
137 }
138 CFRelease(t);
139 return CFNumberCreate(0, kCFNumberSInt32Type, &value);
140 } else
141 return NULL;
142 }
143
144 CFDateRef copyDate(CFTypeRef obj) {
145 CFTypeID tid = CFGetTypeID(obj);
146 if (tid == CFDateGetTypeID()) {
147 CFRetain(obj);
148 return obj;
149 } else
150 return NULL;
151 }
152
153 // MARK: SecDbColumn accessors, to retrieve values as CF types in SecDbStep.
154
155 static CFDataRef SecDbColumnCopyData(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) {
156 return CFDataCreate(allocator, sqlite3_column_blob(stmt, col),
157 sqlite3_column_bytes(stmt, col));
158 //return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt, col),
159 // sqlite3_column_bytes(stmt, col),
160 // kCFAllocatorNull);
161 }
162
163 static CFDateRef SecDbColumnCopyDate(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) {
164 return CFDateCreate(allocator, sqlite3_column_double(stmt, col));
165 }
166
167 static CFNumberRef SecDbColumnCopyDouble(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) {
168 double number = sqlite3_column_double(stmt, col);
169 return CFNumberCreate(allocator, kCFNumberDoubleType, &number);
170 }
171
172 static CFNumberRef SecDbColumnCopyNumber64(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) {
173 sqlite_int64 number = sqlite3_column_int64(stmt, col);
174 return CFNumberCreate(allocator, kCFNumberSInt64Type, &number);
175 }
176
177 static CFNumberRef SecDbColumnCopyNumber(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error) {
178 sqlite_int64 number = sqlite3_column_int64(stmt, col);
179 if (INT32_MIN <= number && number <= INT32_MAX) {
180 int32_t num32 = (int32_t)number;
181 return CFNumberCreate(allocator, kCFNumberSInt32Type, &num32);
182 } else {
183 return CFNumberCreate(allocator, kCFNumberSInt64Type, &number);
184 }
185 }
186
187 static CFTypeRef SecDbColumnCopyString(CFAllocatorRef allocator, sqlite3_stmt *stmt, int col, CFErrorRef *error,
188 CFOptionFlags flags) {
189 const unsigned char *text = sqlite3_column_text(stmt, col);
190 if (!text || 0 == strlen((const char *)text)) {
191 if (flags & kSecDbDefaultEmptyFlag) {
192 return CFSTR("");
193 } else if (flags & kSecDbDefault0Flag) {
194 return CFSTR("0");
195 } else {
196 return kCFNull;
197 }
198 }
199 return CFStringCreateWithBytes(allocator, text, strlen((const char *)text), kCFStringEncodingUTF8, false);
200 }
201
202 // MARK: SecDbClass helpers
203
204 const SecDbAttr *SecDbClassAttrWithKind(const SecDbClass *class, SecDbAttrKind kind, CFErrorRef *error) {
205 const SecDbAttr *result = NULL;
206 SecDbForEachAttr(class, desc) {
207 if (desc->kind == kind)
208 result = desc;
209 }
210
211 if (!result)
212 SecError(errSecInternal, error, CFSTR("Can't find attribute of kind %d in class %@"), kind, class->name);
213
214 return result;
215 }
216
217 // MARK: SecDbAttr helpers
218
219 static bool SecDbIsTombstoneDbSelectAttr(const SecDbAttr *attr) {
220 return attr->flags & kSecDbPrimaryKeyFlag || attr->kind == kSecDbTombAttr;
221 }
222
223 #if 0
224 static bool SecDbIsTombstoneDbInsertAttr(const SecDbAttr *attr) {
225 return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbModificationDateAttr;
226 }
227 #endif
228
229 static bool SecDbIsTombstoneDbUpdateAttr(const SecDbAttr *attr) {
230 // We add AuthenticatedData to include UUIDs, which can't be primary keys
231 return SecDbIsTombstoneDbSelectAttr(attr) || attr->kind == kSecDbAccessAttr || attr->kind == kSecDbCreationDateAttr || attr->kind == kSecDbRowIdAttr || (attr->flags & kSecDbInAuthenticatedDataFlag);
232 }
233
234 CFTypeRef SecDbAttrCopyDefaultValue(const SecDbAttr *attr, CFErrorRef *error) {
235 CFTypeRef value = NULL;
236 switch (attr->kind) {
237 case kSecDbAccessAttr:
238 case kSecDbStringAttr:
239 case kSecDbAccessControlAttr:
240 value = CFSTR("");
241 break;
242 case kSecDbBlobAttr:
243 case kSecDbDataAttr:
244 value = CFDataCreate(kCFAllocatorDefault, NULL, 0);
245 break;
246 case kSecDbUUIDAttr:
247 value = CFDataCreate(kCFAllocatorDefault, NULL, 0);
248 break;
249 case kSecDbNumberAttr:
250 case kSecDbSyncAttr:
251 case kSecDbTombAttr:
252 {
253 int32_t zero = 0;
254 value = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &zero);
255 break;
256 }
257 case kSecDbDateAttr:
258 value = CFDateCreate(kCFAllocatorDefault, 0.0);
259 break;
260 case kSecDbCreationDateAttr:
261 case kSecDbModificationDateAttr:
262 value = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
263 break;
264 default:
265 SecError(errSecInternal, error, CFSTR("attr %@ has no default value"), attr->name);
266 value = NULL;
267 }
268
269 return value;
270 }
271
272 static CFTypeRef SecDbAttrCopyValueForDb(const SecDbAttr *attr, CFTypeRef value, CFErrorRef *error) {
273 CFDataRef data = NULL;
274 CFTypeRef result = NULL;
275
276 if (value == NULL)
277 value = kCFNull;
278
279 if (CFEqual(value, kCFNull) && attr->flags & kSecDbPrimaryKeyFlag) {
280 // SQLITE3 doesn't like NULL for primary key attributes, pretend kSecDbDefaultEmptyFlag was specified
281 require_quiet(result = SecDbAttrCopyDefaultValue(attr, error), out);
282 } else {
283 result = CFRetain(value);
284 }
285
286 if (attr->flags & kSecDbSHA1ValueInFlag && !CFEqual(result, kCFNull)) {
287 require_action_quiet(data = copyData(result), out,
288 SecError(errSecInternal, error, CFSTR("failed to get attribute %@ data"), attr->name);
289 CFReleaseNull(result));
290 CFAssignRetained(result, CFDataCopySHA1Digest(data, error));
291 }
292
293 out:
294 CFReleaseSafe(data);
295 return result;
296 }
297
298 static CFStringRef SecDbAttrGetHashName(const SecDbAttr *attr) {
299 if ((attr->flags & kSecDbSHA1ValueInFlag) == 0) {
300 return attr->name;
301 }
302
303 static dispatch_once_t once;
304 static CFMutableDictionaryRef hash_store;
305 static dispatch_queue_t queue;
306 dispatch_once(&once, ^{
307 queue = dispatch_queue_create("secd-hash-name", NULL);
308 hash_store = CFDictionaryCreateMutableForCFTypes(NULL);
309 });
310
311 __block CFStringRef name;
312 dispatch_sync(queue, ^{
313 name = CFDictionaryGetValue(hash_store, attr->name);
314 if (name == NULL) {
315 name = CFStringCreateWithFormat(NULL, NULL, CFSTR("#%@"), attr->name);
316 CFDictionarySetValue(hash_store, attr->name, name);
317 CFRelease(name);
318 }
319 });
320 return name;
321 }
322
323 // MARK: SecDbItem
324
325 CFTypeRef SecDbItemGetCachedValueWithName(SecDbItemRef item, CFStringRef name) {
326 return CFDictionaryGetValue(item->attributes, name);
327 }
328
329 static CFTypeRef SecDbItemGetCachedValue(SecDbItemRef item, const SecDbAttr *desc) {
330 return CFDictionaryGetValue(item->attributes, desc->name);
331 }
332
333 CFMutableDictionaryRef SecDbItemCopyPListWithMask(SecDbItemRef item, CFOptionFlags mask, CFErrorRef *error) {
334 CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
335 SecDbForEachAttrWithMask(item->class, desc, mask) {
336 CFTypeRef value = SecDbItemGetValue(item, desc, error);
337 if (value) {
338 if (!CFEqual(kCFNull, value)) {
339 CFDictionarySetValue(dict, desc->name, value);
340 } else if (desc->flags & kSecDbNotNullFlag) {
341 SecError(errSecDecode, error, CFSTR("attribute %@ has NULL value"), desc->name);
342 secerror("%@", error ? *error : (CFErrorRef)CFSTR("error == NULL"));
343 CFReleaseNull(dict);
344 break;
345 }
346 } else {
347 CFReleaseNull(dict);
348 break;
349 }
350 }
351 return dict;
352 }
353
354 void SecDbItemSetCredHandle(SecDbItemRef item, CFTypeRef cred_handle) {
355 CFRetainAssign(item->credHandle, cred_handle);
356 }
357
358 void SecDbItemSetCallerAccessGroups(SecDbItemRef item, CFArrayRef caller_access_groups) {
359 CFRetainAssign(item->callerAccessGroups, caller_access_groups);
360 }
361
362 CFDataRef SecDbItemCopyEncryptedDataToBackup(SecDbItemRef item, uint64_t handle, CFErrorRef *error) {
363 CFDataRef edata = NULL;
364 keybag_handle_t keybag = (keybag_handle_t)handle;
365 CFMutableDictionaryRef attributes = SecDbItemCopyPListWithMask(item, kSecDbInCryptoDataFlag, error);
366 CFMutableDictionaryRef auth_attributes = SecDbItemCopyPListWithMask(item, kSecDbInAuthenticatedDataFlag, error);
367 if (attributes || auth_attributes) {
368 SecAccessControlRef access_control = SecDbItemCopyAccessControl(item, error);
369 if (access_control) {
370 if (ks_encrypt_data_legacy(keybag, access_control, item->credHandle, attributes, auth_attributes, &edata, false, error)) {
371 item->_edataState = kSecDbItemEncrypting;
372 } else {
373 seccritical("ks_encrypt_data (db): failed: %@", error ? *error : (CFErrorRef)CFSTR(""));
374 }
375 CFRelease(access_control);
376 }
377 CFReleaseNull(attributes);
378 CFReleaseNull(auth_attributes);
379 }
380
381 return edata;
382 }
383
384 bool SecDbItemEnsureDecrypted(SecDbItemRef item, bool decryptSecretData, CFErrorRef *error) {
385
386 // If we haven't yet decrypted the item, make sure we do so now
387 bool result = true;
388 if (item->_edataState == kSecDbItemEncrypted || (decryptSecretData && item->_edataState == kSecDbItemSecretEncrypted)) {
389 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, error);
390 if (attr) {
391 CFDataRef edata = SecDbItemGetCachedValue(item, attr);
392 if (!edata)
393 return SecError(errSecInternal, error, CFSTR("state= encrypted but edata is NULL"));
394 // Decrypt calls set value a bunch of times which clears our edata and changes our state.
395 item->_edataState = kSecDbItemDecrypting;
396 result = SecDbItemDecrypt(item, decryptSecretData, edata, error);
397 if (result)
398 item->_edataState = decryptSecretData ? kSecDbItemClean : kSecDbItemSecretEncrypted;
399 else
400 item->_edataState = kSecDbItemEncrypted;
401 }
402 }
403 return result;
404 }
405
406 // Only called if cached value is not found.
407 static CFTypeRef SecDbItemCopyValue(SecDbItemRef item, const SecDbAttr *attr, CFErrorRef *error) {
408 if (attr->copyValue)
409 return attr->copyValue(item, attr, error);
410
411 CFTypeRef value = NULL;
412 switch (attr->kind) {
413 // These have an explicit copyValue; here to shut up compiler
414 case kSecDbSHA1Attr:
415 case kSecDbEncryptedDataAttr:
416 case kSecDbPrimaryKeyAttr:
417 value = NULL;
418 break;
419 case kSecDbAccessAttr:
420 case kSecDbStringAttr:
421 case kSecDbBlobAttr:
422 case kSecDbAccessControlAttr:
423 if (attr->flags & kSecDbNotNullFlag) {
424 if (attr->flags & kSecDbDefault0Flag) {
425 value = CFSTR("0");
426 break;
427 } else if (attr->kind != kSecDbBlobAttr && attr->flags & kSecDbDefaultEmptyFlag) {
428 // blob drops through to data everything else is empty string
429 value = CFSTR("");
430 break;
431 }
432 }
433 //DROPTHROUGH
434 case kSecDbDataAttr:
435 if (attr->flags & kSecDbNotNullFlag && attr->flags & kSecDbDefaultEmptyFlag) {
436 value = CFDataCreate(CFGetAllocator(item), NULL, 0);
437 } else {
438 value = kCFNull;
439 }
440 break;
441 case kSecDbUUIDAttr:
442 value = CFDataCreate(CFGetAllocator(item), NULL, 0);
443 break;
444 case kSecDbNumberAttr:
445 case kSecDbSyncAttr:
446 case kSecDbTombAttr:
447 if (attr->flags & kSecDbNotNullFlag) {
448 int32_t zero = 0;
449 value = CFNumberCreate(CFGetAllocator(item), kCFNumberSInt32Type, &zero);
450 } else {
451 value = kCFNull;
452 }
453 break;
454 case kSecDbDateAttr:
455 if (attr->flags & kSecDbNotNullFlag && attr->flags & kSecDbDefault0Flag) {
456 value = CFDateCreate(kCFAllocatorDefault, 0.0);
457 } else {
458 value = kCFNull;
459 }
460 break;
461 case kSecDbRowIdAttr:
462 if (attr->flags & kSecDbNotNullFlag) {
463 // No can do, error?
464 }
465 value = kCFNull;
466 break;
467 case kSecDbCreationDateAttr:
468 case kSecDbModificationDateAttr:
469 value = CFDateCreate(CFGetAllocator(item), CFAbsoluteTimeGetCurrent());
470 break;
471 case kSecDbUTombAttr:
472 value = kCFNull;
473 break;
474 }
475
476 return value;
477 }
478
479 // SecDbItemGetValue will return kCFNull if there is no value for an attribute and this was not
480 // an error. It will return NULL and optionally set *error if there was an error computing an
481 // attribute, or if a required attribute was missing a value and had no known way to compute
482 // it's value.
483 CFTypeRef SecDbItemGetValue(SecDbItemRef item, const SecDbAttr *desc, CFErrorRef *error) {
484 // Propagate chained errors
485 if (!desc)
486 return NULL;
487
488 if (desc->flags & kSecDbInCryptoDataFlag || desc->flags & kSecDbInAuthenticatedDataFlag || desc->flags & kSecDbReturnDataFlag) {
489 if (!SecDbItemEnsureDecrypted(item, desc->flags & kSecDbReturnDataFlag, error))
490 return NULL;
491 }
492
493 CFTypeRef value = SecDbItemGetCachedValue(item, desc);
494 if (!value) {
495 value = SecDbItemCopyValue(item, desc, error);
496 if (value) {
497 if (CFEqual(kCFNull, value)) {
498 CFRelease(value); // This is redundant but it shuts clang's static analyzer up.
499 value = kCFNull;
500 } else {
501 SecDbItemSetValue(item, desc, value, error);
502 CFRelease(value);
503 value = SecDbItemGetCachedValue(item, desc);
504 }
505 }
506 }
507 return value;
508 }
509
510 CFTypeRef SecDbItemGetValueKind(SecDbItemRef item, SecDbAttrKind descKind, CFErrorRef *error) {
511 CFTypeRef result = NULL;
512
513 const SecDbClass * itemClass = SecDbItemGetClass(item);
514 const SecDbAttr * desc = SecDbClassAttrWithKind(itemClass, descKind, error);
515
516 if (desc) {
517 result = SecDbItemGetValue(item, desc, error);
518 }
519
520 return result;
521 }
522
523
524 // Similar as SecDbItemGetValue, but if attr represents attribute stored into DB field as hash, returns
525 // hashed value for the attribute.
526 static CFTypeRef SecDbItemCopyValueForDb(SecDbItemRef item, const SecDbAttr *desc, CFErrorRef *error) {
527 CFTypeRef value = NULL;
528 CFStringRef hash_name = NULL;
529 hash_name = SecDbAttrGetHashName(desc);
530 if ((desc->flags & kSecDbSHA1ValueInFlag) && (desc->flags & kSecDbInFlag)) {
531 value = CFRetainSafe(CFDictionaryGetValue(item->attributes, hash_name));
532 }
533
534 if (value == NULL) {
535 require_quiet(value = SecDbItemGetValue(item, desc, error), out);
536 require_action_quiet(value = SecDbAttrCopyValueForDb(desc, value, error), out, CFReleaseNull(value));
537 if ((desc->flags & kSecDbSHA1ValueInFlag) != 0) {
538 CFDictionarySetValue(item->attributes, hash_name, value);
539 }
540 }
541
542 out:
543 return value;
544 }
545
546 static bool SecDbItemGetBoolValue(SecDbItemRef item, const SecDbAttr *desc, bool *bvalue, CFErrorRef *error) {
547 CFTypeRef value = SecDbItemGetValue(item, desc, error);
548 if (!value)
549 return false;
550 char cvalue;
551 *bvalue = (isNumber(value) && CFNumberGetValue(value, kCFNumberCharType, &cvalue) && cvalue == 1);
552 return true;
553 }
554
555 static CFStringRef SecDbItemCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
556 CFStringRef desc;
557 if (isDictionary(formatOptions) && CFDictionaryContainsKey(formatOptions, kSecDebugFormatOption)) {
558 SecDbItemRef item = (SecDbItemRef)cf;
559 CFMutableStringRef mdesc = CFStringCreateMutable(CFGetAllocator(cf), 0);
560 CFStringAppendFormat(mdesc, NULL, CFSTR("<%@"), item->class->name);
561 SecDbForEachAttr(item->class, attr) {
562 CFTypeRef value = SecDbItemGetValue(item, attr, NULL);
563 if (value) {
564 CFStringAppend(mdesc, CFSTR(","));
565 CFStringAppend(mdesc, attr->name);
566 CFStringAppend(mdesc, CFSTR("="));
567 if (CFEqual(CFSTR("data"), attr->name)) {
568 CFStringAppendEncryptedData(mdesc, value);
569 } else if (CFEqual(CFSTR("v_Data"), attr->name)) {
570 CFStringAppend(mdesc, CFSTR("<?>"));
571 } else if (isData(value)) {
572 CFStringAppendHexData(mdesc, value);
573 } else {
574 CFStringAppendFormat(mdesc, 0, CFSTR("%@"), value);
575 }
576 }
577 }
578 CFStringAppend(mdesc, CFSTR(">"));
579 desc = mdesc;
580 } else {
581 SecDbItemRef item = (SecDbItemRef)cf;
582 const UInt8 zero4[4] = {};
583 const UInt8 *pk = &zero4[0], *sha1 = &zero4[0];
584 char sync = 0;
585 char tomb = 0;
586 SInt64 rowid = 0;
587 CFStringRef access = NULL;
588 uint8_t mdatbuf[32] = {};
589 uint8_t *mdat = &mdatbuf[0];
590 CFMutableStringRef attrs = CFStringCreateMutable(kCFAllocatorDefault, 0);
591 CFStringRef agrp = NULL;
592 CFBooleanRef utomb = NULL;
593
594 SecDbForEachAttr(item->class, attr) {
595 CFTypeRef value;
596 switch (attr->kind) {
597 case kSecDbBlobAttr:
598 case kSecDbDataAttr:
599 case kSecDbStringAttr:
600 case kSecDbNumberAttr:
601 case kSecDbDateAttr:
602 case kSecDbEncryptedDataAttr:
603 if (attr->flags & (kSecDbReturnAttrFlag | kSecDbReturnDataFlag) && (value = SecDbItemGetValue(item, attr, NULL)) && !CFEqual(value, kCFNull)) {
604 if (isString(value) && CFEqual(attr->name, kSecAttrAccessGroup)) {
605 agrp = value;
606 } else {
607 // We don't log these, just record that we saw the attribute.
608 CFStringAppend(attrs, CFSTR(","));
609 CFStringAppend(attrs, attr->name);
610 }
611 }
612 break;
613 case kSecDbUUIDAttr:
614 if ((value = SecDbItemGetValue(item, attr, NULL))) {
615 if (CFEqual(attr->name, kSecAttrMultiUser)) {
616 if (isData(value)) {
617 CFStringAppend(attrs, CFSTR(","));
618 if (CFDataGetLength(value)) {
619 CFStringAppendHexData(attrs, value);
620 } else {
621 CFStringAppend(attrs, attr->name);
622 }
623 }
624 }
625 }
626 break;
627 case kSecDbCreationDateAttr:
628 // We don't care about this and every object has one.
629 break;
630 case kSecDbModificationDateAttr:
631 value = SecDbItemGetValue(item, attr, NULL);
632 if (isDate(value))
633 mdat = der_encode_generalizedtime_body(CFDateGetAbsoluteTime(value), NULL, mdat, &mdatbuf[31]);
634 break;
635 case kSecDbSHA1Attr:
636 value = SecDbItemGetValue(item, attr, NULL);
637 if (isData(value) && CFDataGetLength(value) >= (CFIndex)sizeof(zero4))
638 sha1 = CFDataGetBytePtr(value);
639 break;
640 case kSecDbRowIdAttr:
641 value = SecDbItemGetValue(item, attr, NULL);
642 if (isNumber(value))
643 CFNumberGetValue(value, kCFNumberSInt64Type, &rowid);
644 break;
645 case kSecDbPrimaryKeyAttr:
646 value = SecDbItemGetValue(item, attr, NULL);
647 if (isData(value))
648 pk = CFDataGetBytePtr(value);
649 break;
650 case kSecDbSyncAttr:
651 value = SecDbItemGetValue(item, attr, NULL);
652 if (isNumber(value))
653 CFNumberGetValue(value, kCFNumberCharType, &sync);
654 break;
655 case kSecDbTombAttr:
656 value = SecDbItemGetValue(item, attr, NULL);
657 if (isNumber(value))
658 CFNumberGetValue(value, kCFNumberCharType, &tomb);
659 break;
660 case kSecDbAccessAttr:
661 value = SecDbItemGetValue(item, attr, NULL);
662 if (isString(value))
663 access = value;
664 break;
665 case kSecDbUTombAttr:
666 value = SecDbItemGetValue(item, attr, NULL);
667 if (isBoolean(value))
668 utomb = value;
669 case kSecDbAccessControlAttr:
670 /* TODO: Add formatting of ACLs. */
671 break;
672 }
673 }
674
675 desc = CFStringCreateWithFormat(CFGetAllocator(cf), NULL,
676 CFSTR(
677 "%s,"
678 "%@,"
679 "%02X%02X%02X%02X,"
680 "%s,"
681 "%@,"
682 "%@,"
683 "%"PRId64
684 "%@,"
685 "%s,"
686 "%s"
687 "%02X%02X%02X%02X"),
688 tomb ? "T" : "O",
689 item->class->name,
690 pk[0], pk[1], pk[2], pk[3],
691 sync ? "S" : "L",
692 access,
693 agrp,
694 rowid,
695 attrs,
696 mdat,
697 utomb ? (CFEqual(utomb, kCFBooleanFalse) ? "F," : "T,") : "",
698 sha1[0], sha1[1], sha1[2], sha1[3]);
699 CFReleaseSafe(attrs);
700 }
701
702 return desc;
703 }
704
705 static void SecDbItemDestroy(CFTypeRef cf) {
706 SecDbItemRef item = (SecDbItemRef)cf;
707 CFReleaseSafe(item->attributes);
708 CFReleaseSafe(item->credHandle);
709 CFReleaseSafe(item->callerAccessGroups);
710 CFReleaseSafe(item->cryptoOp);
711 }
712
713 static CFHashCode SecDbItemHash(CFTypeRef cf) {
714 SecDbItemRef item = (SecDbItemRef)cf;
715 CFDataRef digest = SecDbItemGetSHA1(item, NULL);
716 CFHashCode code;
717 const UInt8 *p = CFDataGetBytePtr(digest);
718 // Read first 8 bytes of digest in order
719 code = p[0] + ((p[1] + ((p[2] + ((p[3] + ((p[4] + ((p[5] + ((p[6] + (p[7] << 8)) << 8)) << 8)) << 8)) << 8)) << 8)) << 8);
720 return code;
721 }
722
723 static Boolean SecDbItemCompare(CFTypeRef cf1, CFTypeRef cf2) {
724 SecDbItemRef item1 = (SecDbItemRef)cf1;
725 SecDbItemRef item2 = (SecDbItemRef)cf2;
726 CFDataRef digest1 = NULL;
727 CFDataRef digest2 = NULL;
728 if (item1)
729 digest1 = SecDbItemGetSHA1(item1, NULL);
730 if (item2)
731 digest2 = SecDbItemGetSHA1(item2, NULL);
732 Boolean equal = CFEqual(digest1, digest2);
733 return equal;
734 }
735
736 CFGiblisWithHashFor(SecDbItem)
737
738 static SecDbItemRef SecDbItemCreate(CFAllocatorRef allocator, const SecDbClass *class, keybag_handle_t keybag) {
739 SecDbItemRef item = CFTypeAllocate(SecDbItem, struct SecDbItem, allocator);
740 item->class = class;
741 item->attributes = CFDictionaryCreateMutableForCFTypes(allocator);
742 item->keybag = keybag;
743 item->_edataState = kSecDbItemDirty;
744 item->cryptoOp = kAKSKeyOpDecrypt;
745
746 return item;
747 }
748
749 const SecDbClass *SecDbItemGetClass(SecDbItemRef item) {
750 return item->class;
751 }
752
753 keybag_handle_t SecDbItemGetKeybag(SecDbItemRef item) {
754 return item->keybag;
755 }
756
757 bool SecDbItemSetKeybag(SecDbItemRef item, keybag_handle_t keybag, CFErrorRef *error) {
758 if (!SecDbItemEnsureDecrypted(item, true, error))
759 return false;
760 if (item->keybag != keybag) {
761 item->keybag = keybag;
762 if (item->_edataState == kSecDbItemClean) {
763 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, NULL), kCFNull, NULL);
764 }
765 }
766
767 return true;
768 }
769
770 bool SecDbItemSetValue(SecDbItemRef item, const SecDbAttr *desc, CFTypeRef value, CFErrorRef *error) {
771 // Propagate chained errors.
772 if (!desc)
773 return false;
774
775 if (!value)
776 value = kCFNull;
777
778 if (desc->setValue)
779 return desc->setValue(item, desc, value, error);
780
781 if (desc->flags & kSecDbInCryptoDataFlag || desc->flags & kSecDbInAuthenticatedDataFlag) {
782 if (!SecDbItemEnsureDecrypted(item, true, error)) {
783 return false;
784 }
785 }
786
787 bool changed = false;
788 CFTypeRef attr = NULL;
789 switch (desc->kind) {
790 case kSecDbPrimaryKeyAttr:
791 case kSecDbDataAttr:
792 attr = copyData(value);
793 break;
794 case kSecDbEncryptedDataAttr:
795 attr = copyData(value);
796 if (attr) {
797 if (item->_edataState == kSecDbItemEncrypting)
798 item->_edataState = kSecDbItemClean;
799 else
800 item->_edataState = kSecDbItemEncrypted;
801 } else if (!value || CFEqual(kCFNull, value)) {
802 item->_edataState = kSecDbItemDirty;
803 }
804 break;
805 case kSecDbBlobAttr:
806 case kSecDbAccessControlAttr:
807 attr = copyBlob(value);
808 break;
809 case kSecDbDateAttr:
810 case kSecDbCreationDateAttr:
811 case kSecDbModificationDateAttr:
812 attr = copyDate(value);
813 break;
814 case kSecDbNumberAttr:
815 case kSecDbSyncAttr:
816 case kSecDbTombAttr:
817 case kSecDbRowIdAttr:
818 attr = copyNumber(value);
819 break;
820 case kSecDbAccessAttr:
821 case kSecDbStringAttr:
822 attr = copyString(value);
823 break;
824 case kSecDbSHA1Attr:
825 attr = copySHA1(value);
826 break;
827 case kSecDbUTombAttr:
828 attr = CFRetainSafe(asBoolean(value, NULL));
829 break;
830 case kSecDbUUIDAttr:
831 attr = copyUUID(value);
832 break;
833 }
834
835 if (attr) {
836 CFTypeRef ovalue = CFDictionaryGetValue(item->attributes, desc->name);
837 changed = (!ovalue || !CFEqual(ovalue, attr));
838 CFDictionarySetValue(item->attributes, desc->name, attr);
839 CFRelease(attr);
840 } else {
841 if (value && !CFEqual(kCFNull, value)) {
842 SecError(errSecItemInvalidValue, error, CFSTR("attribute %@: value: %@ failed to convert"), desc->name, value);
843 return false;
844 }
845 CFTypeRef ovalue = CFDictionaryGetValue(item->attributes, desc->name);
846 changed = (ovalue && !CFEqual(ovalue, kCFNull));
847 CFDictionaryRemoveValue(item->attributes, desc->name);
848 }
849
850 if (changed) {
851 if (desc->flags & kSecDbInHashFlag)
852 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, NULL), kCFNull, NULL);
853 if (desc->flags & kSecDbPrimaryKeyFlag)
854 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbPrimaryKeyAttr, NULL), kCFNull, NULL);
855 if ((desc->flags & kSecDbInCryptoDataFlag || desc->flags & kSecDbInAuthenticatedDataFlag) && (item->_edataState == kSecDbItemClean || (item->_edataState == kSecDbItemSecretEncrypted && (desc->flags & kSecDbReturnDataFlag) == 0)))
856 SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, NULL), kCFNull, NULL);
857 if (desc->flags & kSecDbSHA1ValueInFlag)
858 CFDictionaryRemoveValue(item->attributes, SecDbAttrGetHashName(desc));
859 }
860
861 return true;
862 }
863
864 bool SecDbItemSetValues(SecDbItemRef item, CFDictionaryRef values, CFErrorRef *error) {
865 SecDbForEachAttr(item->class, attr) {
866 CFTypeRef value = CFDictionaryGetValue(values, attr->name);
867 if (value && !SecDbItemSetValue(item, attr, value, error))
868 return false;
869 }
870 return true;
871 }
872
873 bool SecDbItemSetValueWithName(SecDbItemRef item, CFStringRef name, CFTypeRef value, CFErrorRef *error) {
874 SecDbForEachAttr(item->class, attr) {
875 if (CFEqual(attr->name, name)) {
876 return SecDbItemSetValue(item, attr, value, error);
877 }
878 }
879 return false;
880 }
881
882 bool SecDbItemSetAccessControl(SecDbItemRef item, SecAccessControlRef access_control, CFErrorRef *error) {
883 bool ok = true;
884 if (item->_edataState == kSecDbItemClean)
885 ok = SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbEncryptedDataAttr, error), kCFNull, error);
886 if (ok && access_control) { //added check for access_control because ks_decrypt_data can leave NULL in access_control in case of error
887 item->_edataState = kSecDbItemDirty;
888 CFDataRef data = SecAccessControlCopyData(access_control);
889 ok = SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbAccessControlAttr, error), data, error);
890 CFRelease(data);
891 }
892 return ok;
893 }
894
895 SecDbItemRef SecDbItemCreateWithAttributes(CFAllocatorRef allocator, const SecDbClass *class, CFDictionaryRef attributes, keybag_handle_t keybag, CFErrorRef *error) {
896 SecDbItemRef item = SecDbItemCreate(kCFAllocatorDefault, class, keybag);
897 if (item && !SecDbItemSetValues(item, attributes, error))
898 CFReleaseNull(item);
899 return item;
900 }
901
902 static CFTypeRef
903 SecDbColumnCopyValueWithAttr(CFAllocatorRef allocator, sqlite3_stmt *stmt, const SecDbAttr *attr, int col, CFErrorRef *error) {
904 CFTypeRef value = NULL;
905 switch (attr->kind) {
906 case kSecDbDateAttr:
907 case kSecDbCreationDateAttr:
908 case kSecDbModificationDateAttr:
909 value = SecDbColumnCopyDate(allocator, stmt, col, error);
910 break;
911 case kSecDbBlobAttr:
912 case kSecDbNumberAttr:
913 switch (sqlite3_column_type(stmt, col)) {
914 case SQLITE_INTEGER:
915 value = SecDbColumnCopyNumber(allocator, stmt, col, error);
916 break;
917 case SQLITE_FLOAT:
918 value = SecDbColumnCopyDouble(allocator, stmt, col, error);
919 break;
920 case SQLITE_TEXT:
921 value = SecDbColumnCopyString(allocator, stmt, col, error,
922 attr->flags);
923 break;
924 case SQLITE_BLOB:
925 value = SecDbColumnCopyData(allocator, stmt, col, error);
926 break;
927 case SQLITE_NULL:
928 value = kCFNull;
929 break;
930 }
931 break;
932 case kSecDbAccessAttr:
933 case kSecDbStringAttr:
934 value = SecDbColumnCopyString(allocator, stmt, col, error,
935 attr->flags);
936 break;
937 case kSecDbDataAttr:
938 case kSecDbUUIDAttr:
939 case kSecDbSHA1Attr:
940 case kSecDbPrimaryKeyAttr:
941 case kSecDbEncryptedDataAttr:
942 value = SecDbColumnCopyData(allocator, stmt, col, error);
943 break;
944 case kSecDbSyncAttr:
945 case kSecDbTombAttr:
946 value = SecDbColumnCopyNumber(allocator, stmt, col, error);
947 break;
948 case kSecDbRowIdAttr:
949 value = SecDbColumnCopyNumber64(allocator, stmt, col, error);
950 break;
951 case kSecDbAccessControlAttr:
952 case kSecDbUTombAttr:
953 /* This attributes does not have any database column associated, exists only inside encrypted blob as metadata. */
954 break;
955 }
956 return value;
957 }
958
959 SecDbItemRef SecDbItemCreateWithStatement(CFAllocatorRef allocator, const SecDbClass *class, sqlite3_stmt *stmt, keybag_handle_t keybag, CFErrorRef *error, bool (^return_attr)(const SecDbAttr *attr)) {
960 SecDbItemRef item = SecDbItemCreate(allocator, class, keybag);
961 int col = 0;
962 SecDbForEachAttr(class, attr) {
963 if (return_attr(attr)) {
964 CFTypeRef value = SecDbColumnCopyValueWithAttr(allocator, stmt, attr, col++, error);
965 require_action_quiet(value, errOut, CFReleaseNull(item));
966
967 CFDictionarySetValue(item->attributes, SecDbAttrGetHashName(attr), value);
968 CFRelease(value);
969 }
970
971 const SecDbAttr *data_attr = SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr, NULL);
972 if (data_attr != NULL && CFDictionaryGetValue(item->attributes, data_attr->name) != NULL) {
973 item->_edataState = kSecDbItemEncrypted;
974 }
975 }
976
977 errOut:
978 return item;
979 }
980
981 SecDbItemRef SecDbItemCreateWithEncryptedData(CFAllocatorRef allocator, const SecDbClass *class,
982 CFDataRef edata, keybag_handle_t keybag, CFErrorRef *error) {
983 SecDbItemRef item = SecDbItemCreate(allocator, class, keybag);
984 const SecDbAttr *edata_attr = SecDbClassAttrWithKind(class, kSecDbEncryptedDataAttr, error);
985 if (edata_attr) {
986 if (!SecDbItemSetValue(item, edata_attr, edata, error))
987 CFReleaseNull(item);
988 }
989 return item;
990 }
991
992 // TODO: Hack -- Replace with real filtering
993
994 // Return true iff an item for which SecDbItemIsSyncable() already returns true should be part of the v2 view.
995 bool SecDbItemInV2(SecDbItemRef item) {
996 const SecDbClass *iclass = SecDbItemGetClass(item);
997 return (SecDbItemGetCachedValueWithName(item, kSecAttrSyncViewHint) == NULL &&
998 (iclass == genp_class() || iclass == inet_class() || iclass == keys_class() || iclass == cert_class()));
999 }
1000
1001 // Return true iff an item for which SecDbItemIsSyncable() and SecDbItemInV2() already return true should be part of the v0 view.
1002 bool SecDbItemInV2AlsoInV0(SecDbItemRef item) {
1003 return (SecDbItemGetCachedValueWithName(item, kSecAttrTokenID) == NULL && SecDbItemGetClass(item) != cert_class());
1004 }
1005
1006 SecDbItemRef SecDbItemCopyWithUpdates(SecDbItemRef item, CFDictionaryRef updates, CFErrorRef *error) {
1007 SecDbItemRef new_item = SecDbItemCreate(CFGetAllocator(item), item->class, item->keybag);
1008 SecDbItemSetCredHandle(new_item, item->credHandle);
1009 SecDbForEachAttr(item->class, attr) {
1010 // Copy each attribute, except the mod date attribute (it will be reset to now when needed),
1011 // from the updates dict unless it's not there in which case we copy the attribute from the passed in item.
1012 if (attr->kind != kSecDbModificationDateAttr && attr->kind != kSecDbEncryptedDataAttr && attr->kind != kSecDbSHA1Attr && attr->kind != kSecDbPrimaryKeyAttr) {
1013 CFTypeRef value = NULL;
1014 if (CFDictionaryGetValueIfPresent(updates, attr->name, &value)) {
1015 if (!value)
1016 SecError(errSecParam, error, CFSTR("NULL value in dictionary"));
1017 } else {
1018 value = SecDbItemGetValue(item, attr, error);
1019 }
1020 if (!value || !SecDbItemSetValue(new_item, attr, value, error)) {
1021 CFReleaseNull(new_item);
1022 break;
1023 }
1024 }
1025 }
1026 return new_item;
1027 }
1028
1029 // Ensure that the date value of attr of new_item is greater than that of old_item.
1030 static bool SecDbItemMakeAttrYounger(SecDbItemRef new_item, SecDbItemRef old_item, const SecDbAttr *attr, CFErrorRef *error) {
1031 CFDateRef old_date = SecDbItemGetValue(old_item, attr, error);
1032 if (!old_date)
1033 return false;
1034 CFDateRef new_date = SecDbItemGetValue(new_item, attr, error);
1035 if (!new_date)
1036 return false;
1037 bool ok = true;
1038 if (CFDateCompare(new_date, old_date, NULL) != kCFCompareGreaterThan) {
1039 CFDateRef adjusted_date = CFDateCreate(kCFAllocatorDefault, CFDateGetAbsoluteTime(old_date) + 0.001);
1040 if (adjusted_date) {
1041 ok = SecDbItemSetValue(new_item, attr, adjusted_date, error);
1042 CFRelease(adjusted_date);
1043 }
1044 }
1045 return ok;
1046 }
1047
1048 // Ensure that the mod date of new_item is greater than that of old_item.
1049 static bool SecDbItemMakeYounger(SecDbItemRef new_item, SecDbItemRef old_item, CFErrorRef *error) {
1050 const SecDbAttr *attr = SecDbClassAttrWithKind(new_item->class, kSecDbModificationDateAttr, error);
1051 return attr && SecDbItemMakeAttrYounger(new_item, old_item, attr, error);
1052 }
1053
1054 static SecDbItemRef SecDbItemCopyTombstone(SecDbItemRef item, CFBooleanRef makeTombStone, CFErrorRef *error) {
1055 SecDbItemRef new_item = SecDbItemCreate(CFGetAllocator(item), item->class, item->keybag);
1056 SecDbForEachAttr(item->class, attr) {
1057 if (attr->kind == kSecDbTombAttr) {
1058 // Set the tomb attr to true to indicate a tombstone.
1059 if (!SecDbItemSetValue(new_item, attr, kCFBooleanTrue, error)) {
1060 CFReleaseNull(new_item);
1061 break;
1062 }
1063 } else if (SecDbIsTombstoneDbUpdateAttr(attr)) {
1064 // Copy all primary key attributes and creation timestamps from the original item.
1065 CFTypeRef value = SecDbItemGetValue(item, attr, error);
1066 if (!value || (!CFEqual(kCFNull, value) && !SecDbItemSetValue(new_item, attr, value, error))) {
1067 CFReleaseNull(new_item);
1068 break;
1069 }
1070 } else if (attr->kind == kSecDbModificationDateAttr) {
1071 if (!SecDbItemMakeAttrYounger(new_item, item, attr, error)) {
1072 CFReleaseNull(new_item);
1073 break;
1074 }
1075 } else if (makeTombStone && attr->kind == kSecDbUTombAttr) {
1076 if (makeTombStone)
1077 SecDbItemSetValue(new_item, attr, makeTombStone, error);
1078 }
1079 }
1080
1081 return new_item;
1082 }
1083
1084 bool SecDbItemIsEngineInternalState(SecDbItemRef itemObject) {
1085 // Only used for controlling logging
1086 // Use agrp=com.apple.security.sos, since it is not encrypted
1087 if (!itemObject) {
1088 return false;
1089 }
1090 const SecDbAttr *agrp = SecDbAttrWithKey(SecDbItemGetClass(itemObject), kSecAttrAccessGroup, NULL);
1091 CFTypeRef cfval = SecDbItemGetValue(itemObject, agrp, NULL);
1092 return cfval && CFStringCompareSafe(cfval, kSOSInternalAccessGroup, NULL) == kCFCompareEqualTo;
1093 }
1094
1095
1096 // MARK: -
1097 // MARK: SQL Construction helpers -- These should become private in the future
1098
1099 void SecDbAppendElement(CFMutableStringRef sql, CFStringRef value, bool *needComma) {
1100 assert(needComma);
1101 if (*needComma) {
1102 CFStringAppend(sql, CFSTR(","));
1103 } else {
1104 *needComma = true;
1105 }
1106 CFStringAppend(sql, value);
1107 }
1108
1109 static void SecDbAppendElementEquals(CFMutableStringRef sql, CFStringRef value, bool *needComma) {
1110 SecDbAppendElement(sql, value, needComma);
1111 CFStringAppend(sql, CFSTR("=?"));
1112 }
1113
1114 /* Append AND is needWhere is NULL or *needWhere is false. Append WHERE
1115 otherwise. Upon return *needWhere will be false. */
1116 void
1117 SecDbAppendWhereOrAnd(CFMutableStringRef sql, bool *needWhere) {
1118 if (!needWhere || !*needWhere) {
1119 CFStringAppend(sql, CFSTR(" AND "));
1120 } else {
1121 CFStringAppend(sql, CFSTR(" WHERE "));
1122 *needWhere = false;
1123 }
1124 }
1125
1126 void
1127 SecDbAppendWhereOrAndEquals(CFMutableStringRef sql, CFStringRef col, bool *needWhere) {
1128 SecDbAppendWhereOrAnd(sql, needWhere);
1129 CFStringAppend(sql, col);
1130 CFStringAppend(sql, CFSTR("=?"));
1131 }
1132
1133 void
1134 SecDbAppendWhereOrAndNotEquals(CFMutableStringRef sql, CFStringRef col, bool *needWhere) {
1135 SecDbAppendWhereOrAnd(sql, needWhere);
1136 CFStringAppend(sql, col);
1137 CFStringAppend(sql, CFSTR("!=?"));
1138 }
1139
1140 static void SecDbAppendCountArgsAndCloseParen(CFMutableStringRef sql, CFIndex count) {
1141 bool needComma = false;
1142 while (count-- > 0)
1143 SecDbAppendElement(sql, CFSTR("?"), &needComma);
1144 CFStringAppend(sql, CFSTR(")"));
1145 }
1146
1147 void
1148 SecDbAppendWhereOrAndIn(CFMutableStringRef sql, CFStringRef col, bool *needWhere, CFIndex count) {
1149 if (count == 1)
1150 return SecDbAppendWhereOrAndEquals(sql, col, needWhere);
1151 SecDbAppendWhereOrAnd(sql, needWhere);
1152 CFStringAppend(sql, col);
1153 CFStringAppend(sql, CFSTR(" IN ("));
1154 SecDbAppendCountArgsAndCloseParen(sql, count);
1155 }
1156
1157 void
1158 SecDbAppendWhereOrAndNotIn(CFMutableStringRef sql, CFStringRef col, bool *needWhere, CFIndex count) {
1159 if (count == 1)
1160 return SecDbAppendWhereOrAndNotEquals(sql, col, needWhere);
1161 SecDbAppendWhereOrAnd(sql, needWhere);
1162 CFStringAppend(sql, col);
1163 CFStringAppend(sql, CFSTR(" NOT IN ("));
1164 SecDbAppendCountArgsAndCloseParen(sql, count);
1165 }
1166
1167 static CFStringRef SecDbItemCopyInsertSQL(SecDbItemRef item, bool(^use_attr)(const SecDbAttr *attr)) {
1168 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(item), 0);
1169 CFStringAppend(sql, CFSTR("INSERT INTO "));
1170 CFStringAppend(sql, item->class->name);
1171 CFStringAppend(sql, CFSTR("("));
1172 bool needComma = false;
1173 CFIndex used_attr = 0;
1174 SecDbForEachAttr(item->class, attr) {
1175 if (use_attr(attr)) {
1176 ++used_attr;
1177 SecDbAppendElement(sql, attr->name, &needComma);
1178 }
1179 }
1180 CFStringAppend(sql, CFSTR(")VALUES(?"));
1181 while (used_attr-- > 1) {
1182 CFStringAppend(sql, CFSTR(",?"));
1183 }
1184 CFStringAppend(sql, CFSTR(")"));
1185 return sql;
1186
1187 }
1188
1189 static bool SecDbItemInsertBind(SecDbItemRef item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr)(const SecDbAttr *attr)) {
1190 bool ok = true;
1191 int param = 0;
1192 SecDbForEachAttr(item->class, attr) {
1193 if (use_attr(attr)) {
1194 CFTypeRef value = SecDbItemCopyValueForDb(item, attr, error);
1195 ok = value && SecDbBindObject(stmt, ++param, value, error);
1196 CFReleaseSafe(value);
1197 if (!ok)
1198 break;
1199 }
1200 }
1201 return ok;
1202 }
1203
1204 sqlite3_int64 SecDbItemGetRowId(SecDbItemRef item, CFErrorRef *error) {
1205 sqlite3_int64 row_id = 0;
1206 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error);
1207 if (attr) {
1208 CFNumberRef number = SecDbItemGetValue(item, attr, error);
1209 if (!isNumber(number)|| !CFNumberGetValue(number, kCFNumberSInt64Type, &row_id))
1210 SecDbError(SQLITE_ERROR, error, CFSTR("rowid %@ is not a 64 bit number"), number);
1211 }
1212
1213 return row_id;
1214 }
1215
1216 static CFNumberRef SecDbItemCreateRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error) {
1217 return CFNumberCreate(CFGetAllocator(item), kCFNumberSInt64Type, &rowid);
1218 }
1219
1220 bool SecDbItemSetRowId(SecDbItemRef item, sqlite3_int64 rowid, CFErrorRef *error) {
1221 bool ok = true;
1222 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error);
1223 if (attr) {
1224 CFNumberRef value = SecDbItemCreateRowId(item, rowid, error);
1225 if (!value)
1226 return false;
1227
1228 ok = SecDbItemSetValue(item, attr, value, error);
1229 CFRelease(value);
1230 }
1231 return ok;
1232 }
1233
1234 bool SecDbItemClearRowId(SecDbItemRef item, CFErrorRef *error) {
1235 bool ok = true;
1236 const SecDbAttr *attr = SecDbClassAttrWithKind(item->class, kSecDbRowIdAttr, error);
1237 if (attr) {
1238 CFDictionaryRemoveValue(item->attributes, attr->name);
1239 //ok = SecDbItemSetValue(item, attr, kCFNull, error);
1240 }
1241 return ok;
1242 }
1243
1244 static bool SecDbItemSetLastInsertRowId(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) {
1245 sqlite3_int64 rowid = sqlite3_last_insert_rowid(SecDbHandle(dbconn));
1246 return SecDbItemSetRowId(item, rowid, error);
1247 }
1248
1249 bool SecDbItemIsSyncableOrCorrupted(SecDbItemRef item) {
1250 bool is_syncable_or_corrupted = false;
1251 CFErrorRef localError = NULL;
1252 if (!SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, &localError),
1253 &is_syncable_or_corrupted, &localError)) {
1254 is_syncable_or_corrupted = SecErrorGetOSStatus(localError) == errSecDecode;
1255 }
1256 CFReleaseSafe(localError);
1257 return is_syncable_or_corrupted;
1258 }
1259
1260 bool SecDbItemIsSyncable(SecDbItemRef item) {
1261 bool is_syncable;
1262 if (SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, NULL), &is_syncable, NULL))
1263 return is_syncable;
1264 return false;
1265 }
1266
1267 bool SecDbItemSetSyncable(SecDbItemRef item, bool sync, CFErrorRef *error)
1268 {
1269 return SecDbItemSetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSyncAttr, error), sync ? kCFBooleanTrue : kCFBooleanFalse, error);
1270 }
1271
1272 bool SecDbItemIsTombstone(SecDbItemRef item) {
1273 bool is_tomb;
1274 if (SecDbItemGetBoolValue(item, SecDbClassAttrWithKind(item->class, kSecDbTombAttr, NULL), &is_tomb, NULL))
1275 return is_tomb;
1276 return false;
1277 }
1278
1279 CFDataRef SecDbItemGetPrimaryKey(SecDbItemRef item, CFErrorRef *error) {
1280 return SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbPrimaryKeyAttr, error), error);
1281 }
1282
1283 CFDataRef SecDbItemGetSHA1(SecDbItemRef item, CFErrorRef *error) {
1284 return SecDbItemGetValue(item, SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, error), error);
1285 }
1286
1287 static SecDbQueryRef SecDbQueryCreateWithItemPrimaryKey(SecDbItemRef item, CFErrorRef *error) {
1288 CFMutableDictionaryRef dict = SecDbItemCopyPListWithMask(item, kSecDbPrimaryKeyFlag, error);
1289 if (!dict)
1290 return NULL;
1291
1292 SecDbQueryRef query = query_create(item->class, NULL, NULL, error);
1293 if (query) {
1294 CFReleaseSafe(query->q_item);
1295 query->q_item = dict;
1296 }
1297 else
1298 CFRelease(dict);
1299
1300 return query;
1301 }
1302
1303 static bool SecDbItemIsCorrupt(SecDbItemRef item, bool *is_corrupt, CFErrorRef *error) {
1304 CFErrorRef localError = NULL;
1305 // Cache the storedSHA1 digest so we use the one from the db not the recomputed one for notifications.
1306 const struct SecDbAttr *sha1attr = SecDbClassAttrWithKind(item->class, kSecDbSHA1Attr, &localError);
1307 CFDataRef storedSHA1 = CFRetainSafe(SecDbItemGetValue(item, sha1attr, &localError));
1308 bool akpu = false;
1309
1310 if (localError || !SecDbItemEnsureDecrypted(item, true, &localError)) {
1311 if (SecErrorGetOSStatus(localError) == errSecDecode) {
1312 // We failed to decrypt the item
1313 const SecDbAttr *desc = SecDbClassAttrWithKind(item->class, kSecDbAccessControlAttr, &localError);
1314 SecAccessControlRef accc = NULL;
1315 CFDataRef acccData = NULL;
1316
1317 acccData = (CFDataRef)SecDbItemGetValue(item, desc, &localError);
1318 if (isData(acccData)) {
1319 accc = SecAccessControlCreateFromData(CFGetAllocator(item), acccData, &localError);
1320 }
1321
1322 if (accc && CFEqualSafe(SecAccessControlGetProtection(accc), kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly)) {
1323 akpu = true;
1324 secwarning("cannot decrypt item %@, item is irrecoverably lost with older passcode (error %@)", item, localError);
1325 } else {
1326 secerror("error %@ reading item %@ (corrupted)", localError, item);
1327 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem);
1328 }
1329 CFReleaseNull(localError);
1330 *is_corrupt = true;
1331 }
1332 }
1333
1334 // Recompute sha1 hash attribute and compare with the cached one.
1335 CFDataRef computedSHA1 = SecDbItemCopyValue(item, sha1attr, &localError);
1336 if (storedSHA1 && computedSHA1 && !CFEqual(storedSHA1, computedSHA1)) {
1337 CFStringRef storedHex = CFDataCopyHexString(storedSHA1), computedHex = CFDataCopyHexString(computedSHA1);
1338 secerror("error %@ %@ != %@ item %@ (corrupted)", sha1attr->name, storedHex, computedHex, item);
1339 __security_simulatecrash(CFSTR("Corrupted item (sha1 mismatch) found in keychain"), __sec_exception_code_CorruptItem);
1340 CFReleaseSafe(storedHex);
1341 CFReleaseSafe(computedHex);
1342 *is_corrupt = true;
1343 }
1344
1345 // Sanity check that all attributes that must not be NULL actually aren't
1346 if (!localError) SecDbForEachAttr(item->class, attr) {
1347 if (attr->flags & (kSecDbInCryptoDataFlag | kSecDbInAuthenticatedDataFlag)) {
1348 CFTypeRef value = SecDbItemGetValue(item, attr, &localError);
1349 if (value) {
1350 if (CFEqual(kCFNull, value) && attr->flags & kSecDbNotNullFlag) {
1351 secerror("error attribute %@ has NULL value in item %@ (corrupted)", attr->name, item);
1352 __security_simulatecrash(CFSTR("Corrupted item (attr NULL) found in keychain"), __sec_exception_code_CorruptItem);
1353 *is_corrupt = true;
1354 break;
1355 }
1356 } else {
1357 if (SecErrorGetOSStatus(localError) == errSecDecode) {
1358 // We failed to decrypt the item
1359 if (akpu) {
1360 secwarning("attribute %@: %@ item %@ (item lost with older passcode)", attr->name, localError, item);
1361 } else {
1362 secerror("error attribute %@: %@ item %@ (corrupted)", attr->name, localError, item);
1363 __security_simulatecrash(CFSTR("Corrupted item found in keychain"), __sec_exception_code_CorruptItem);
1364 }
1365 *is_corrupt = true;
1366 CFReleaseNull(localError);
1367 }
1368 break;
1369 }
1370 }
1371 }
1372
1373 CFReleaseSafe(computedSHA1);
1374 CFReleaseSafe(storedSHA1);
1375 return SecErrorPropagate(localError, error);
1376 }
1377
1378 static void SecDbItemRecordUpdate(SecDbConnectionRef dbconn, SecDbItemRef deleted, SecDbItemRef inserted) {
1379 SecDbRecordChange(dbconn, deleted, inserted);
1380 }
1381
1382 static bool SecDbItemDoInsert(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) {
1383 bool (^use_attr)(const SecDbAttr *attr) = ^bool(const SecDbAttr *attr) {
1384 return (attr->flags & kSecDbInFlag);
1385 };
1386
1387 if (!SecDbItemEnsureDecrypted(item, true, error)) {
1388 return false;
1389 }
1390
1391 CFStringRef sql = SecDbItemCopyInsertSQL(item, use_attr);
1392 __block bool ok = sql;
1393 if (sql) {
1394 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) {
1395 ok = (SecDbItemInsertBind(item, stmt, error, use_attr) &&
1396 SecDbStep(dbconn, stmt, error, NULL) &&
1397 SecDbItemSetLastInsertRowId(item, dbconn, error));
1398 });
1399 CFRelease(sql);
1400 }
1401 if (ok) {
1402 secnotice("item", "inserted %@", item);
1403 SecDbItemRecordUpdate(dbconn, NULL, item);
1404 } else {
1405 if (SecDbItemIsEngineInternalState(item)) {
1406 secdebug ("item", "insert failed for item %@ with %@", item, error ? *error : NULL);
1407 } else {
1408 secnotice("item", "insert failed for item %@ with %@", item, error ? *error : NULL);
1409 }
1410 }
1411
1412 return ok;
1413 }
1414
1415 bool SecErrorIsSqliteDuplicateItemError(CFErrorRef error) {
1416 return error && CFErrorGetCode(error) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(error));
1417 }
1418
1419 bool SecDbItemInsertOrReplace(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, void(^duplicate)(SecDbItemRef item, SecDbItemRef *replace)) {
1420 __block CFErrorRef localError = NULL;
1421 __block bool ok = SecDbItemDoInsert(item, dbconn, &localError);
1422 if (!ok && localError && CFErrorGetCode(localError) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(localError))) {
1423 SecDbQueryRef query = SecDbQueryCreateWithItemPrimaryKey(item, error);
1424 if (query) {
1425 CFRetainAssign(query->q_use_cred_handle, item->credHandle);
1426 SecDbItemSelect(query, dbconn, error, NULL, ^bool(const SecDbAttr *attr) {
1427 return attr->flags & kSecDbPrimaryKeyFlag;
1428 }, NULL, NULL, ^(SecDbItemRef old_item, bool *stop) {
1429 bool is_corrupt = false;
1430 ok = SecDbItemIsCorrupt(old_item, &is_corrupt, error);
1431 SecDbItemRef replace = NULL;
1432 if (is_corrupt) {
1433 // If old_item is corrupted pretend it's not there and just replace it.
1434 replace = item;
1435 CFRetain(replace);
1436 if(error)
1437 CFReleaseNull(*error); //item is corrupted and will be replaced, so drop the error
1438 } else if (ok && duplicate) {
1439 duplicate(old_item, &replace);
1440 }
1441 if (replace) {
1442 const SecDbAttr *rowid_attr = SecDbClassAttrWithKind(old_item->class, kSecDbRowIdAttr, error);
1443 CFNumberRef oldrowid = SecDbItemGetCachedValue(old_item, rowid_attr);
1444 if (oldrowid) {
1445 ok = SecDbItemSetValue(replace, rowid_attr, oldrowid, &localError);
1446 if (ok && !is_corrupt) {
1447 ok = SecDbItemMakeYounger(replace, old_item, error);
1448 }
1449 ok = ok && SecDbItemDoUpdate(old_item, replace, dbconn, &localError, ^bool (const SecDbAttr *attr) {
1450 return attr->kind == kSecDbRowIdAttr;
1451 });
1452 } else {
1453 ok = SecError(errSecInternal, &localError, CFSTR("no rowid for %@"), old_item);
1454 }
1455 CFRelease(replace);
1456 if (ok)
1457 CFReleaseNull(localError); // Clear the error, since we replaced the item.
1458 }
1459 });
1460 SecDbItemSetCredHandle(item, query->q_use_cred_handle);
1461 ok &= query_destroy(query, error);
1462 }
1463 }
1464
1465 return ok & SecErrorPropagate(localError, error); // Don't use && here!
1466 }
1467
1468 bool SecDbItemInsert(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) {
1469 return SecDbItemInsertOrReplace(item, dbconn, error, ^(SecDbItemRef old_item, SecDbItemRef *replace) {
1470 if (SecDbItemIsTombstone(old_item)) {
1471 CFRetain(item);
1472 *replace = item;
1473 }
1474 });
1475 }
1476
1477 static CFStringRef SecDbItemCopyUpdateSQL(SecDbItemRef old_item, SecDbItemRef new_item, bool(^use_attr_in_where)(const SecDbAttr *attr)) {
1478 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(new_item), 0);
1479 CFStringAppend(sql, CFSTR("UPDATE "));
1480 CFStringAppend(sql, new_item->class->name);
1481 CFStringAppend(sql, CFSTR(" SET "));
1482 bool needComma = false;
1483 CFIndex used_attr = 0;
1484 SecDbForEachAttrWithMask(new_item->class, attr, kSecDbInFlag) {
1485 ++used_attr;
1486 SecDbAppendElementEquals(sql, attr->name, &needComma);
1487 }
1488
1489 bool needWhere = true;
1490 SecDbForEachAttr(old_item->class, attr) {
1491 if (use_attr_in_where(attr)) {
1492 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere);
1493 }
1494 }
1495
1496 return sql;
1497 }
1498
1499 static bool SecDbItemUpdateBind(SecDbItemRef old_item, SecDbItemRef new_item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr_in_where)(const SecDbAttr *attr)) {
1500 bool ok = true;
1501 int param = 0;
1502 SecDbForEachAttrWithMask(new_item->class, attr, kSecDbInFlag) {
1503 CFTypeRef value = SecDbItemCopyValueForDb(new_item, attr, error);
1504 ok &= value && SecDbBindObject(stmt, ++param, value, error);
1505 CFReleaseSafe(value);
1506 if (!ok)
1507 break;
1508 }
1509 SecDbForEachAttr(old_item->class, attr) {
1510 if (use_attr_in_where(attr)) {
1511 CFTypeRef value = SecDbItemCopyValueForDb(old_item, attr, error);
1512 ok &= value && SecDbBindObject(stmt, ++param, value, error);
1513 CFReleaseSafe(value);
1514 if (!ok)
1515 break;
1516 }
1517 }
1518 return ok;
1519 }
1520
1521 // Primary keys are the same -- do an update
1522 bool SecDbItemDoUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnectionRef dbconn, CFErrorRef *error, bool (^use_attr_in_where)(const SecDbAttr *attr)) {
1523 CFStringRef sql = SecDbItemCopyUpdateSQL(old_item, new_item, use_attr_in_where);
1524 __block bool ok = sql;
1525 if (sql) {
1526 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) {
1527 ok = SecDbItemUpdateBind(old_item, new_item, stmt, error, use_attr_in_where) && SecDbStep(dbconn, stmt, error, NULL);
1528 });
1529 CFRelease(sql);
1530 }
1531 if (ok) {
1532 if (SecDbItemIsEngineInternalState(old_item)) {
1533 secdebug ("item", "replaced %@ in %@", old_item, dbconn);
1534 secdebug ("item", " with %@ in %@", new_item, dbconn);
1535 } else {
1536 secnotice("item", "replaced %@ in %@", old_item, dbconn);
1537 secnotice("item", " with %@ in %@", new_item, dbconn);
1538 }
1539 SecDbItemRecordUpdate(dbconn, old_item, new_item);
1540 }
1541 return ok;
1542 }
1543
1544 static CFStringRef SecDbItemCopyDeleteSQL(SecDbItemRef item, bool(^use_attr_in_where)(const SecDbAttr *attr)) {
1545 CFMutableStringRef sql = CFStringCreateMutable(CFGetAllocator(item), 0);
1546 CFStringAppend(sql, CFSTR("DELETE FROM "));
1547 CFStringAppend(sql, item->class->name);
1548 bool needWhere = true;
1549 SecDbForEachAttr(item->class, attr) {
1550 if (use_attr_in_where(attr)) {
1551 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere);
1552 }
1553 }
1554
1555 return sql;
1556 }
1557
1558 static bool SecDbItemDeleteBind(SecDbItemRef item, sqlite3_stmt *stmt, CFErrorRef *error, bool(^use_attr_in_where)(const SecDbAttr *attr)) {
1559 bool ok = true;
1560 int param = 0;
1561 SecDbForEachAttr(item->class, attr) {
1562 if (use_attr_in_where(attr)) {
1563 CFTypeRef value = SecDbItemCopyValueForDb(item, attr, error);
1564 ok &= value && SecDbBindObject(stmt, ++param, value, error);
1565 CFReleaseSafe(value);
1566 if (!ok)
1567 break;
1568 }
1569 }
1570 return ok;
1571 }
1572
1573 static bool SecDbItemDoDeleteOnly(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, bool (^use_attr_in_where)(const SecDbAttr *attr)) {
1574 CFStringRef sql = SecDbItemCopyDeleteSQL(item, use_attr_in_where);
1575 __block bool ok = sql;
1576 if (sql) {
1577 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) {
1578 ok = SecDbItemDeleteBind(item, stmt, error, use_attr_in_where) && SecDbStep(dbconn, stmt, error, NULL);
1579 });
1580 CFRelease(sql);
1581 }
1582 return ok;
1583 }
1584
1585 bool SecDbItemDoDeleteSilently(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) {
1586 return SecDbItemDoDeleteOnly(item, dbconn, error, ^bool(const SecDbAttr *attr) {
1587 return attr->kind == kSecDbRowIdAttr;
1588 });
1589 }
1590
1591 static bool SecDbItemDoDelete(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error, bool (^use_attr_in_where)(const SecDbAttr *attr)) {
1592 bool ok = SecDbItemDoDeleteOnly(item, dbconn, error, use_attr_in_where);
1593 if (ok) {
1594 secnotice("item", "deleted %@ from %@", item, dbconn);
1595 SecDbItemRecordUpdate(dbconn, item, NULL);
1596 }
1597 return ok;
1598 }
1599
1600 #if 0
1601 static bool SecDbItemDeleteTombstone(SecDbItemRef item, SecDbConnectionRef dbconn, CFErrorRef *error) {
1602 bool ok = true;
1603 // TODO: Treat non decryptable items like tombstones here too and delete them
1604 SecDbItemRef tombstone = SecDbItemCopyTombstone(item, error);
1605 ok = tombstone;
1606 if (tombstone) {
1607 ok = SecDbItemClearRowId(tombstone, error);
1608 if (ok) {
1609 ok = SecDbItemDoDelete(tombstone, dbconn, error, ^bool (const SecDbAttr *attr) {
1610 return SecDbIsTombstoneDbSelectAttr(attr);
1611 });
1612 }
1613 CFRelease(tombstone);
1614 }
1615 return ok;
1616 }
1617 #endif
1618
1619 // Replace old_item with new_item. If primary keys are the same this does an update otherwise it does a delete + add
1620 bool SecDbItemUpdate(SecDbItemRef old_item, SecDbItemRef new_item, SecDbConnectionRef dbconn, CFBooleanRef makeTombstone, bool uuid_from_primary_key, CFErrorRef *error) {
1621 __block bool ok = true;
1622 __block CFErrorRef localError = NULL;
1623
1624 CFDataRef old_pk = SecDbItemGetPrimaryKey(old_item, error);
1625 CFDataRef new_pk = SecDbItemGetPrimaryKey(new_item, error);
1626
1627 ok = old_pk && new_pk;
1628
1629 bool pk_equal = ok && CFEqual(old_pk, new_pk);
1630 if (pk_equal) {
1631 ok = SecDbItemMakeYounger(new_item, old_item, error);
1632 } else if(!CFEqualSafe(makeTombstone, kCFBooleanFalse) && SecCKKSIsEnabled()) {
1633 // The primary keys aren't equal, and we're going to make a tombstone.
1634 // Help CKKS out: the tombstone should have the existing item's UUID, and the newly updated item should have a new UUID.
1635
1636 s3dl_item_make_new_uuid(new_item, uuid_from_primary_key, error);
1637 }
1638 ok = ok && SecDbItemDoUpdate(old_item, new_item, dbconn, &localError, ^bool(const SecDbAttr *attr) {
1639 return attr->kind == kSecDbRowIdAttr;
1640 });
1641
1642 if (localError) {
1643 if(CFErrorGetCode(localError) == SQLITE_CONSTRAINT && CFEqual(kSecDbErrorDomain, CFErrorGetDomain(localError))) {
1644 /* Update failed because we changed the PrimaryKey and there was a dup.
1645 Find the dup and see if it is a tombstone or corrupted item. */
1646 SecDbQueryRef query = SecDbQueryCreateWithItemPrimaryKey(new_item, error);
1647 ok = query;
1648 if (query) {
1649 ok &= SecDbItemSelect(query, dbconn, error, NULL, ^bool(const SecDbAttr *attr) {
1650 return attr->flags & kSecDbPrimaryKeyFlag;
1651 }, NULL, NULL, ^(SecDbItemRef duplicate_item, bool *stop) {
1652 bool is_corrupt = false;
1653 bool is_tomb = false;
1654 ok = SecDbItemIsCorrupt(duplicate_item, &is_corrupt, error);
1655 if (ok && !is_corrupt) {
1656 if ((is_tomb = SecDbItemIsTombstone(duplicate_item)))
1657 ok = SecDbItemMakeYounger(new_item, duplicate_item, error);
1658 }
1659 if (ok && (is_corrupt || is_tomb)) {
1660 ok = SecDbItemDoDelete(old_item, dbconn, error, ^bool (const SecDbAttr *attr) {
1661 return attr->kind == kSecDbRowIdAttr;
1662 });
1663 ok = ok && SecDbItemDoUpdate(duplicate_item, new_item, dbconn, error, ^bool (const SecDbAttr *attr) {
1664 return attr->kind == kSecDbRowIdAttr;
1665 });
1666 CFReleaseNull(localError);
1667 }
1668 });
1669 ok &= query_destroy(query, error);
1670 }
1671 }
1672
1673 if (localError) {
1674 ok = false;
1675 if (error && *error == NULL) {
1676 *error = localError;
1677 localError = NULL;
1678 }
1679 CFReleaseSafe(localError);
1680 }
1681 }
1682
1683 if (ok && !pk_equal && !CFEqualSafe(makeTombstone, kCFBooleanFalse)) {
1684 /* The primary key of new_item is different than that of old_item, we
1685 have been asked to make a tombstone so leave one for the old_item. */
1686 SecDbItemRef tombstone = SecDbItemCopyTombstone(old_item, makeTombstone, error);
1687 ok = tombstone;
1688 if (tombstone) {
1689 ok = (SecDbItemClearRowId(tombstone, error) &&
1690 SecDbItemDoInsert(tombstone, dbconn, error));
1691 CFRelease(tombstone);
1692 }
1693 }
1694
1695 return ok;
1696 }
1697
1698 // Replace the object with a tombstone
1699 bool SecDbItemDelete(SecDbItemRef item, SecDbConnectionRef dbconn, CFBooleanRef makeTombstone, CFErrorRef *error) {
1700 bool ok = false;
1701 if (!CFEqualSafe(makeTombstone, kCFBooleanFalse)) {
1702 SecDbItemRef tombstone = SecDbItemCopyTombstone(item, makeTombstone, error);
1703 if (tombstone) {
1704 ok = SecDbItemDoUpdate(item, tombstone, dbconn, error, ^bool(const SecDbAttr *attr) {
1705 return attr->kind == kSecDbRowIdAttr;
1706 });
1707 CFRelease(tombstone);
1708 }
1709 } else {
1710 ok = SecDbItemDoDelete(item, dbconn, error, ^bool(const SecDbAttr *attr) {
1711 return attr->kind == kSecDbRowIdAttr;
1712 });
1713 }
1714 return ok;
1715 }
1716
1717 CFStringRef SecDbItemCopySelectSQL(SecDbQueryRef query,
1718 bool (^return_attr)(const SecDbAttr *attr),
1719 bool (^use_attr_in_where)(const SecDbAttr *attr),
1720 bool (^add_where_sql)(CFMutableStringRef sql, bool *needWhere)) {
1721 CFMutableStringRef sql = CFStringCreateMutable(kCFAllocatorDefault, 0);
1722 CFStringAppend(sql, CFSTR("SELECT "));
1723 // What are we selecting?
1724 bool needComma = false;
1725 SecDbForEachAttr(query->q_class, attr) {
1726 if (return_attr(attr))
1727 SecDbAppendElement(sql, attr->name, &needComma);
1728 }
1729
1730 // From which table?
1731 CFStringAppend(sql, CFSTR(" FROM "));
1732 CFStringAppend(sql, query->q_class->name);
1733
1734 // And which elements do we want to select
1735 bool needWhere = true;
1736 SecDbForEachAttr(query->q_class, attr) {
1737 if (use_attr_in_where(attr)) {
1738 CFTypeRef value = CFDictionaryGetValue(query->q_item, attr->name);
1739 if (isArray(value)) {
1740 CFArrayRef array = (CFArrayRef)value;
1741 CFIndex length = CFArrayGetCount(array);
1742 if (length > 0) {
1743 CFTypeRef head = CFArrayGetValueAtIndex(array, 0);
1744 if (CFEqualSafe(head, kCFNull)) {
1745 SecDbAppendWhereOrAndNotIn(sql, attr->name, &needWhere, length - 1);
1746 } else {
1747 SecDbAppendWhereOrAndIn(sql, attr->name, &needWhere, length);
1748 }
1749 }
1750 } else {
1751 SecDbAppendWhereOrAndEquals(sql, attr->name, &needWhere);
1752 }
1753 }
1754 }
1755 // Append SQL for access groups and limits.
1756 if (add_where_sql)
1757 add_where_sql(sql, &needWhere);
1758
1759 return sql;
1760 }
1761
1762 static bool SecDbItemSelectBindValue(SecDbQueryRef query, sqlite3_stmt *stmt, int param, const SecDbAttr *attr, CFTypeRef inValue, CFErrorRef *error) {
1763 bool ok = true;
1764 CFTypeRef value = NULL;
1765 if (attr->kind == kSecDbRowIdAttr) {
1766 // TODO: Ignores inValue and uses rowid directly instead HACK should go
1767 value = CFNumberCreate(NULL, kCFNumberSInt64Type, &query->q_row_id);
1768 } else {
1769 value = SecDbAttrCopyValueForDb(attr, inValue, error);
1770 }
1771 ok = ok && value != NULL && SecDbBindObject(stmt, ++param, value, error);
1772 CFReleaseSafe(value);
1773 return ok;
1774 }
1775
1776 bool SecDbItemSelectBind(SecDbQueryRef query, sqlite3_stmt *stmt, CFErrorRef *error,
1777 bool (^use_attr_in_where)(const SecDbAttr *attr),
1778 bool (^bind_added_where)(sqlite3_stmt *stmt, int col)) {
1779 __block bool ok = true;
1780 __block int param = 0;
1781 SecDbForEachAttr(query->q_class, attr) {
1782 if (use_attr_in_where(attr)) {
1783 CFTypeRef value = CFDictionaryGetValue(query->q_item, attr->name);
1784 if (isArray(value)) {
1785 CFArrayRef array = (CFArrayRef)value;
1786 CFRange range = {.location = 0, .length = CFArrayGetCount(array) };
1787 if (range.length > 0) {
1788 CFTypeRef head = CFArrayGetValueAtIndex(array, 0);
1789 if (CFEqualSafe(head, kCFNull)) {
1790 range.length--;
1791 range.location++;
1792 }
1793 }
1794 CFArrayApplyFunction(array, range, apply_block_1, (void (^)(const void *value)) ^(const void *value) {
1795 ok = SecDbItemSelectBindValue(query, stmt, param++, attr, value, error);
1796 });
1797 } else {
1798 ok = SecDbItemSelectBindValue(query, stmt, param++, attr, value, error);
1799 }
1800
1801 if (!ok)
1802 break;
1803 }
1804 }
1805 // TODO: Bind arguments for access groups and limits.
1806 if (bind_added_where)
1807 bind_added_where(stmt, ++param);
1808
1809 return ok;
1810 }
1811
1812 bool SecDbItemSelect(SecDbQueryRef query, SecDbConnectionRef dbconn, CFErrorRef *error,
1813 bool (^return_attr)(const SecDbAttr *attr),
1814 bool (^use_attr_in_where)(const SecDbAttr *attr),
1815 bool (^add_where_sql)(CFMutableStringRef sql, bool *needWhere),
1816 bool (^bind_added_where)(sqlite3_stmt *stmt, int col),
1817 void (^handle_row)(SecDbItemRef item, bool *stop)) {
1818 __block bool ok = true;
1819 if (return_attr == NULL) {
1820 return_attr = ^bool (const SecDbAttr * attr) {
1821 return attr->kind == kSecDbRowIdAttr || attr->kind == kSecDbEncryptedDataAttr || attr->kind == kSecDbSHA1Attr;
1822 };
1823 }
1824 if (use_attr_in_where == NULL) {
1825 use_attr_in_where = ^bool (const SecDbAttr* attr) { return false; };
1826 }
1827
1828 CFStringRef sql = SecDbItemCopySelectSQL(query, return_attr, use_attr_in_where, add_where_sql);
1829 if (sql) {
1830 ok &= SecDbPrepare(dbconn, sql, error, ^(sqlite3_stmt *stmt) {
1831 ok = (SecDbItemSelectBind(query, stmt, error, use_attr_in_where, bind_added_where) &&
1832 SecDbStep(dbconn, stmt, error, ^(bool *stop) {
1833 SecDbItemRef item = SecDbItemCreateWithStatement(kCFAllocatorDefault, query->q_class, stmt, query->q_keybag, error, return_attr);
1834 if (item) {
1835 CFRetainAssign(item->credHandle, query->q_use_cred_handle);
1836 handle_row(item, stop);
1837 CFRelease(item);
1838 } else {
1839 //*stop = true;
1840 //ok = false;
1841 }
1842 }));
1843 });
1844 CFRelease(sql);
1845 } else {
1846 ok = false;
1847 }
1848 return ok;
1849 }
1850