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