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