2 * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecItemServer.c - CoreFoundation-based constants and functions for
26 access to Security items (certificates, keys, identities, and
30 #include <securityd/SecItemServer.h>
32 #include <Security/SecItem.h>
33 #include <Security/SecItemPriv.h>
34 #include <Security/SecItemInternal.h>
35 #include <Security/SecKey.h>
36 #include <Security/SecKeyPriv.h>
37 #include <Security/SecCertificateInternal.h>
38 #include <Security/SecIdentity.h>
39 #include <Security/SecIdentityPriv.h>
40 #include <Security/SecFramework.h>
41 #include <Security/SecRandom.h>
42 #include <Security/SecBasePriv.h>
49 #include <sys/param.h>
51 #include <Security/SecBase.h>
52 #include <CoreFoundation/CFData.h>
53 #include <CoreFoundation/CFDate.h>
54 #include <CoreFoundation/CFDictionary.h>
55 #include <CoreFoundation/CFNumber.h>
56 #include <CoreFoundation/CFString.h>
57 #include <CoreFoundation/CFURL.h>
58 #include <CommonCrypto/CommonDigest.h>
59 #include <CommonCrypto/CommonCryptor.h>
60 #include <CommonCrypto/CommonCryptorSPI.h>
61 #include <libkern/OSByteOrder.h>
62 #include <security_utilities/debugging.h>
64 #include <Security/SecInternal.h>
65 #include <TargetConditionals.h>
66 #include "securityd_client.h"
67 #include "securityd_server.h"
69 #include <AssertMacros.h>
73 #if TARGET_OS_EMBEDDED && !TARGET_IPHONE_SIMULATOR
75 #define USE_KEYSTORE 1
76 #else /* no hardware aes */
78 #define USE_KEYSTORE 0
79 #endif /* hardware aes */
82 #include <IOKit/IOKitLib.h>
83 #include <Kernel/IOKit/crypto/IOAESTypes.h>
84 #endif /* USE_HWAES */
87 #include <Kernel/IOKit/crypto/AppleKeyStoreDefs.h>
88 #include <MobileKeyBag/MobileKeyBag.h>
89 typedef int32_t keyclass_t
;
91 /* TODO: this needs to be available in the sim! */
92 #define kAppleKeyStoreKeyWrap 0
93 #define kAppleKeyStoreKeyUnwrap 1
94 typedef int32_t keyclass_t
;
95 typedef int32_t key_handle_t
;
96 typedef int32_t keybag_handle_t
;
105 #endif /* USE_KEYSTORE */
107 /* KEYBAG_LEGACY and KEYBAG_NONE are private to security and have special meaning.
108 They should not collide with AppleKeyStore constants, but are only referenced
112 KEYBAG_LEGACY
= -3, /* Set q_keybag to KEYBAG_LEGACY to use legacy decrypt. */
113 KEYBAG_BACKUP
= -2, /* -2 == backup_keybag_handle, constant dictated by AKS */
114 KEYBAG_NONE
= -1, /* Set q_keybag to KEYBAG_NONE to obtain cleartext data. */
115 KEYBAG_DEVICE
= 0, /* 0 == device_keybag_handle, constant dictated by AKS */
119 #include <CoreFoundation/CFPriv.h>
121 /* Pass NULL for the current user's home directory */
123 CFURLRef
CFCopyHomeDirectoryURLForUser(CFStringRef uName
);
126 /* label when certificate data is joined with key data */
127 #define CERTIFICATE_DATA_COLUMN_LABEL "certdata"
129 #define CURRENT_DB_VERSION 5
133 static bool isArray(CFTypeRef cfType
) {
134 return cfType
&& CFGetTypeID(cfType
) == CFArrayGetTypeID();
137 static bool isData(CFTypeRef cfType
) {
138 return cfType
&& CFGetTypeID(cfType
) == CFDataGetTypeID();
141 static bool isDictionary(CFTypeRef cfType
) {
142 return cfType
&& CFGetTypeID(cfType
) == CFDictionaryGetTypeID();
145 static bool isNumber(CFTypeRef cfType
) {
146 return cfType
&& CFGetTypeID(cfType
) == CFNumberGetTypeID();
149 static bool isNumberOfType(CFTypeRef cfType
, CFNumberType number
) {
150 return isNumber(cfType
) && CFNumberGetType(cfType
) == number
;
153 static bool isString(CFTypeRef cfType
) {
154 return cfType
&& CFGetTypeID(cfType
) == CFStringGetTypeID();
157 typedef struct s3dl_db
161 pthread_mutex_t mutex
;
162 /* Linked list of s3dl_db_thread * */
163 struct s3dl_db_thread
*dbt_head
;
164 /* True iff crypto facilities are available and keychain key is usable. */
168 typedef s3dl_db
*db_handle
;
170 typedef struct s3dl_db_thread
172 struct s3dl_db_thread
*dbt_next
;
179 typedef struct s3dl_results_handle
183 } s3dl_results_handle
;
185 /* Mapping from class name to kc_class pointer. */
186 static CFDictionaryRef gClasses
;
188 /* Forward declaration of import export SPIs. */
191 kSecSysBoundItemFilter
,
192 kSecBackupableItemFilter
,
195 static CFDictionaryRef
SecServerExportKeychainPlist(s3dl_db_thread
*dbt
,
196 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
197 enum SecItemFilter filter
, int version
, OSStatus
*error
);
198 static OSStatus
SecServerImportKeychainInPlist(s3dl_db_thread
*dbt
,
199 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
200 CFDictionaryRef keychain
, enum SecItemFilter filter
);
202 #if USE_HWAES || USE_KEYSTORE
204 * Encryption support.
206 static pthread_once_t hwcrypto_init_once
= PTHREAD_ONCE_INIT
;
207 static io_connect_t hwaes_codec
= MACH_PORT_NULL
;
208 static io_connect_t keystore
= MACH_PORT_NULL
;
210 static void service_matching_callback(void *refcon
, io_iterator_t iterator
)
212 io_object_t obj
= IO_OBJECT_NULL
;
214 while ((obj
= IOIteratorNext(iterator
))) {
215 kern_return_t ret
= IOServiceOpen(obj
, mach_task_self(), 0,
216 (io_connect_t
*)refcon
);
218 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
219 "IOServiceOpen() failed: %x", ret
);
221 IOObjectRelease(obj
);
225 static void connect_to_service(const char *className
, io_connect_t
*connect
)
227 kern_return_t kernResult
;
228 io_iterator_t iterator
= MACH_PORT_NULL
;
229 IONotificationPortRef port
= MACH_PORT_NULL
;
230 CFDictionaryRef classToMatch
;
232 if ((classToMatch
= IOServiceMatching(className
)) == NULL
) {
233 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
234 "IOServiceMatching failed for '%s'", className
);
238 /* consumed by IOServiceGetMatchingServices, we need it if that fails. */
239 CFRetain(classToMatch
);
240 kernResult
= IOServiceGetMatchingServices(kIOMasterPortDefault
,
241 classToMatch
, &iterator
);
243 if (kernResult
== KERN_SUCCESS
) {
244 CFRelease(classToMatch
);
246 asl_log(NULL
, NULL
, ASL_LEVEL_WARNING
,
247 "IOServiceGetMatchingServices() failed %x using notifiction",
250 port
= IONotificationPortCreate(kIOMasterPortDefault
);
252 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
253 "IONotificationPortCreate() failed");
257 kernResult
= IOServiceAddMatchingNotification(port
,
258 kIOFirstMatchNotification
, classToMatch
, service_matching_callback
,
261 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
262 "IOServiceAddMatchingNotification() failed: %x", kernResult
);
267 /* Check whether it was already there before we registered for the
269 service_matching_callback(connect
, iterator
);
272 /* We'll get set up to wait for it to appear */
273 if (*connect
== MACH_PORT_NULL
) {
274 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
275 "Waiting for %s to show up.", className
);
276 CFStringRef mode
= CFSTR("WaitForCryptoService");
277 CFRunLoopAddSource(CFRunLoopGetCurrent(),
278 IONotificationPortGetRunLoopSource(port
), mode
);
279 CFRunLoopRunInMode(mode
, 30.0, true);
280 if (hwaes_codec
== MACH_PORT_NULL
)
281 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "Cannot find AES driver");
283 IONotificationPortDestroy(port
);
286 IOObjectRelease(iterator
);
289 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "Obtained connection %d for %s",
290 *connect
, className
);
295 static void hwcrypto_init(void)
297 connect_to_service(kIOAESAcceleratorClass
, &hwaes_codec
);
298 connect_to_service(kAppleKeyStoreServiceName
, &keystore
);
300 if (keystore
!= MACH_PORT_NULL
) {
301 IOReturn kernResult
= IOConnectCallMethod(keystore
,
302 kAppleKeyStoreUserClientOpen
, NULL
, 0, NULL
, 0, NULL
, NULL
,
305 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
306 "Failed to open AppleKeyStore: %x", kernResult
);
308 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "Opened AppleKeyStore");
311 /* TODO: Remove this once the kext runs the daemon on demand if
312 there is no system keybag. */
313 int kb_state
= MKBGetDeviceLockState(NULL
);
314 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "AppleKeyStore lock state: %d",
318 static bool hwaes_crypt(IOAESOperation operation
, UInt32 keyHandle
,
319 UInt32 keySizeInBits
, const UInt8
*keyBits
, const UInt8
*iv
,
320 UInt32 textLength
, UInt8
*plainText
, UInt8
*cipherText
)
322 struct IOAESAcceleratorRequest aesRequest
;
323 kern_return_t kernResult
;
327 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "aes codec not initialized");
331 aesRequest
.plainText
= plainText
;
332 aesRequest
.cipherText
= cipherText
;
333 aesRequest
.textLength
= textLength
;
334 memcpy(aesRequest
.iv
.ivBytes
, iv
, 16);
335 aesRequest
.operation
= operation
;
336 aesRequest
.keyData
.key
.keyLength
= keySizeInBits
;
337 aesRequest
.keyData
.keyHandle
= keyHandle
;
339 memcpy(aesRequest
.keyData
.key
.keyBytes
, keyBits
, keySizeInBits
/ 8);
341 bzero(aesRequest
.keyData
.key
.keyBytes
, keySizeInBits
/ 8);
344 outSize
= sizeof(aesRequest
);
345 kernResult
= IOConnectCallStructMethod(hwaes_codec
,
346 kIOAESAcceleratorPerformAES
, &aesRequest
, outSize
,
347 &aesRequest
, &outSize
);
349 if (kernResult
!= KERN_SUCCESS
) {
350 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "kIOAESAcceleratorPerformAES: %x",
355 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
,
356 "kIOAESAcceleratorPerformAES processed: %lu bytes", textLength
);
361 static bool hwaes_key_available(void)
363 /* The AES driver needs to have a 16byte aligned address */
365 UInt8
*bufp
= (UInt8
*)(((intptr_t)&buf
[15]) & ~15);
367 pthread_once(&hwcrypto_init_once
, hwcrypto_init
);
368 return hwaes_crypt(IOAESOperationEncrypt
,
369 kIOAESAcceleratorKeyHandleKeychain
, 128, NULL
, bufp
, 16, bufp
, bufp
);
373 #else /* !USE_HWAES */
375 static bool hwaes_key_available(void)
380 #endif /* !USE_HWAES */
382 static int s3dl_create_path(const char *path
)
384 char pathbuf
[PATH_MAX
];
385 size_t pos
, len
= strlen(path
);
386 if (len
== 0 || len
> PATH_MAX
)
387 return SQLITE_CANTOPEN
;
388 memcpy(pathbuf
, path
, len
);
389 for (pos
= len
; --pos
> 0;)
391 /* Search backwards for trailing '/'. */
392 if (pathbuf
[pos
] == '/')
395 /* Attempt to create parent directories of the database. */
396 if (!mkdir(pathbuf
, 0777))
404 return SQLITE_CANTOPEN
;
406 return SQLITE_READONLY
;
409 if (err
== ENOSPC
|| err
== EDQUOT
)
414 /* EFAULT || ELOOP | ENAMETOOLONG || something else */
415 return SQLITE_INTERNAL
;
422 /* Start an exclusive transaction if we don't have one yet. */
423 static int s3dl_begin_transaction(s3dl_db_thread
*dbt
)
425 if (dbt
->in_transaction
)
426 return dbt
->autocommit
? SQLITE_INTERNAL
: SQLITE_OK
;
428 int s3e
= sqlite3_exec(dbt
->s3_handle
, "BEGIN EXCLUSIVE TRANSACTION",
430 if (s3e
== SQLITE_OK
)
431 dbt
->in_transaction
= true;
436 /* Commit the current transaction if we have one. */
437 static int s3dl_commit_transaction(s3dl_db_thread
*dbt
)
439 if (!dbt
->in_transaction
)
442 int s3e
= sqlite3_exec(dbt
->s3_handle
, "COMMIT TRANSACTION",
444 if (s3e
== SQLITE_OK
)
445 dbt
->in_transaction
= false;
450 /* Rollback the current transaction if we have one. */
451 static int s3dl_rollback_transaction(s3dl_db_thread
*dbt
)
453 if (!dbt
->in_transaction
)
456 int s3e
= sqlite3_exec(dbt
->s3_handle
, "ROLLBACK TRANSACTION",
458 if (s3e
== SQLITE_OK
)
459 dbt
->in_transaction
= false;
464 /* If we are in a transaction and autocommt is on, commit the transaction
465 if s3e == SQLITE_OK, otherwise rollback the transaction. */
466 static int s3dl_end_transaction(s3dl_db_thread
*dbt
, int s3e
)
470 if (s3e
== SQLITE_OK
)
471 return s3dl_commit_transaction(dbt
);
473 s3dl_rollback_transaction(dbt
);
479 static int s3dl_close_dbt(s3dl_db_thread
*dbt
)
481 int s3e
= sqlite3_close(dbt
->s3_handle
);
487 kc_blob_attr
, // CFString or CFData, preserves caller provided type.
492 kc_creation_date_attr
,
493 kc_modification_date_attr
497 kc_constrain_not_null
= (1 << 0), // attr value can't be null
498 kc_constrain_default_0
= (1 << 1), // default attr value is 0
499 kc_constrain_default_empty
= (1 << 2), // default attr value is ""
500 kc_digest_attr
= (1 << 3), // col in db is sha1 of attr value
503 typedef struct kc_attr_desc
{
509 typedef struct kc_class
{
512 const kc_attr_desc
*attrs
;
516 typedef struct kc_item
{
518 CFMutableDictionaryRef a
;
521 typedef CFIndex kc_attr_id
;
524 #define KC_ATTR(name, kind, flags) { CFSTR(name), kc_ ## kind ## _attr, flags }
526 static const kc_attr_desc genp_attrs
[] = {
527 KC_ATTR("pdmn", string
, 0),
528 KC_ATTR("agrp", string
, 0),
529 KC_ATTR("cdat", creation_date
, 0),
530 KC_ATTR("mdat", modification_date
, 0),
531 KC_ATTR("desc", blob
, kc_digest_attr
),
532 KC_ATTR("icmt", blob
, kc_digest_attr
),
533 KC_ATTR("crtr", number
, 0),
534 KC_ATTR("type", number
, 0),
535 KC_ATTR("scrp", number
, 0),
536 KC_ATTR("labl", blob
, kc_digest_attr
),
537 KC_ATTR("alis", blob
, kc_digest_attr
),
538 KC_ATTR("invi", number
, 0),
539 KC_ATTR("nega", number
, 0),
540 KC_ATTR("cusi", number
, 0),
541 KC_ATTR("prot", blob
, kc_digest_attr
),
542 KC_ATTR("acct", blob
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
543 KC_ATTR("svce", blob
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
544 KC_ATTR("gena", blob
, kc_digest_attr
),
547 static const kc_attr_id genp_unique
[] = {
551 static kc_class_constraint genp_constraints
[] = {
552 { kc_unique_constraint
, sizeof(genp_unique
) / sizeof(*genp_unique
), genp_unique
},
555 static const kc_class genp_class
= {
556 .name
= CFSTR("genp"),
557 .n_attrs
= sizeof(genp_attrs
) / sizeof(*genp_attrs
),
561 static const kc_attr_desc inet_attrs
[] = {
562 KC_ATTR("pdmn", string
, 0),
563 KC_ATTR("agrp", string
, 0),
564 KC_ATTR("cdat", creation_date
, 0),
565 KC_ATTR("mdat", modification_date
, 0),
566 KC_ATTR("desc", blob
, kc_digest_attr
),
567 KC_ATTR("icmt", blob
, kc_digest_attr
),
568 KC_ATTR("crtr", number
, 0),
569 KC_ATTR("type", number
, 0),
570 KC_ATTR("scrp", number
, 0),
571 KC_ATTR("labl", blob
, kc_digest_attr
),
572 KC_ATTR("alis", blob
, kc_digest_attr
),
573 KC_ATTR("invi", number
, 0),
574 KC_ATTR("nega", number
, 0),
575 KC_ATTR("cusi", number
, 0),
576 KC_ATTR("prot", blob
, kc_digest_attr
),
577 KC_ATTR("acct", blob
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
578 KC_ATTR("sdmn", blob
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
579 KC_ATTR("srvr", blob
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
580 KC_ATTR("ptcl", number
, kc_constrain_not_null
| kc_constrain_default_0
),
581 KC_ATTR("atyp", blob
, kc_digest_attr
),
582 KC_ATTR("port", number
, kc_constrain_not_null
| kc_constrain_default_0
),
583 KC_ATTR("path", blob
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
586 static const kc_attr_id inet_unique
[] = {
587 // acct, sdmn, srvr, ptcl, atyp, port, path, agrp
588 15, 16, 17, 18, 19, 20, 21, 1
590 static kc_class_constraint inet_constraints
[] = {
591 { kc_unique_constraint
, sizeof(inet_unique
) / sizeof(*inet_unique
), inet_unique
},
594 static const kc_class inet_class
= {
595 .name
= CFSTR("inet"),
596 .n_attrs
= sizeof(inet_attrs
) / sizeof(*inet_attrs
),
600 static const kc_attr_desc cert_attrs
[] = {
601 KC_ATTR("pdmn", string
, 0),
602 KC_ATTR("agrp", string
, 0),
603 KC_ATTR("cdat", creation_date
, 0),
604 KC_ATTR("mdat", modification_date
, 0),
605 KC_ATTR("ctyp", number
, kc_constrain_not_null
| kc_constrain_default_0
),
606 KC_ATTR("cenc", number
, 0),
607 KC_ATTR("labl", blob
, kc_digest_attr
),
608 KC_ATTR("alis", blob
, kc_digest_attr
),
609 KC_ATTR("subj", data
, kc_digest_attr
),
610 KC_ATTR("issr", data
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
611 KC_ATTR("slnr", data
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
612 KC_ATTR("skid", data
, kc_digest_attr
),
613 KC_ATTR("pkhh", data
, 0),
616 static const kc_attr_id cert_unique
[] = {
617 //ctyp, issr, slnr, agrp
620 static kc_class_constraint cert_constraints
[] = {
621 { kc_unique_constraint
, sizeof(cert_unique
) / sizeof(*cert_unique
), cert_unique
, },
624 static const kc_class cert_class
= {
625 .name
= CFSTR("cert"),
626 .n_attrs
= sizeof(cert_attrs
) / sizeof(*cert_attrs
),
630 static const kc_attr_desc keys_attrs
[] = {
631 KC_ATTR("pdmn", string
, 0),
632 KC_ATTR("agrp", string
, 0),
633 KC_ATTR("cdat", creation_date
, 0),
634 KC_ATTR("mdat", modification_date
, 0),
635 KC_ATTR("kcls", number
, kc_constrain_not_null
| kc_constrain_default_0
),
636 KC_ATTR("labl", blob
, kc_digest_attr
),
637 KC_ATTR("alis", blob
, kc_digest_attr
),
638 KC_ATTR("perm", number
, 0),
639 KC_ATTR("priv", number
, 0),
640 KC_ATTR("modi", number
, 0),
641 KC_ATTR("klbl", data
, kc_constrain_not_null
| kc_constrain_default_empty
),
642 KC_ATTR("atag", blob
, kc_constrain_not_null
| kc_constrain_default_empty
| kc_digest_attr
),
643 KC_ATTR("crtr", number
, kc_constrain_not_null
| kc_constrain_default_0
),
644 KC_ATTR("type", number
, kc_constrain_not_null
| kc_constrain_default_0
),
645 KC_ATTR("bsiz", number
, kc_constrain_not_null
| kc_constrain_default_0
),
646 KC_ATTR("esiz", number
, kc_constrain_not_null
| kc_constrain_default_0
),
647 KC_ATTR("sdat", date
, kc_constrain_not_null
| kc_constrain_default_0
),
648 KC_ATTR("edat", date
, kc_constrain_not_null
| kc_constrain_default_0
),
649 KC_ATTR("sens", number
, 0),
650 KC_ATTR("asen", number
, 0),
651 KC_ATTR("extr", number
, 0),
652 KC_ATTR("next", number
, 0),
653 KC_ATTR("encr", number
, 0),
654 KC_ATTR("decr", number
, 0),
655 KC_ATTR("drve", number
, 0),
656 KC_ATTR("sign", number
, 0),
657 KC_ATTR("vrfy", number
, 0),
658 KC_ATTR("snrc", number
, 0),
659 KC_ATTR("vyrc", number
, 0),
660 KC_ATTR("wrap", number
, 0),
661 KC_ATTR("unwp", number
, 0),
664 static const kc_attr_id keys_unique
[] = {
665 // kcls, klbl, atag, crtr, type, bsiz, esiz, sdat, edat, agrp
666 2, 8, 9, 10, 11, 12, 13, 14, 15, 1
668 static kc_class_constraint keys_constraints
[] = {
669 { kc_unique_constraint
, sizeof(keys_unique
) / sizeof(*keys_unique
), keys_unique
, },
672 static const kc_class keys_class
= {
673 .name
= CFSTR("keys"),
674 .n_attrs
= sizeof(keys_attrs
) / sizeof(*keys_attrs
),
678 /* An identity which is really a cert + a key, so all cert and keys attrs are
680 static const kc_class identity_class
= {
681 .name
= CFSTR("idnt"),
686 static const kc_attr_desc
*kc_attr_desc_with_key(const kc_class
*c
,
689 /* Special case: identites can have all attributes of either cert
691 if (c
== &identity_class
) {
692 const kc_attr_desc
*desc
;
693 if (!(desc
= kc_attr_desc_with_key(&cert_class
, key
, 0)))
694 desc
= kc_attr_desc_with_key(&keys_class
, key
, error
);
700 for (ix
= 0; ix
< c
->n_attrs
; ++ix
) {
701 if (CFEqual(c
->attrs
[ix
].name
, key
)) {
702 return &c
->attrs
[ix
];
706 /* TODO: Remove this hack since it's violating this function's contract to always set an error when it returns NULL. */
707 if (CFEqual(key
, kSecAttrSynchronizable
))
712 if (error
&& !*error
)
713 *error
= errSecNoSuchAttr
;
718 static const char * const s3dl_upgrade_sql
[] = {
720 /* Upgrade from version 0 -- empty db a.k.a. current schema. */
722 "rowid INTEGER PRIMARY KEY AUTOINCREMENT,"
736 "acct BLOB NOT NULL DEFAULT '',"
737 "svce BLOB NOT NULL DEFAULT '',"
746 "rowid INTEGER PRIMARY KEY AUTOINCREMENT,"
760 "acct BLOB NOT NULL DEFAULT '',"
761 "sdmn BLOB NOT NULL DEFAULT '',"
762 "srvr BLOB NOT NULL DEFAULT '',"
763 "ptcl INTEGER NOT NULL DEFAULT 0,"
764 "atyp BLOB NOT NULL DEFAULT '',"
765 "port INTEGER NOT NULL DEFAULT 0,"
766 "path BLOB NOT NULL DEFAULT '',"
771 "acct,sdmn,srvr,ptcl,atyp,port,path,agrp"
774 "rowid INTEGER PRIMARY KEY AUTOINCREMENT,"
777 "ctyp INTEGER NOT NULL DEFAULT 0,"
782 "issr BLOB NOT NULL DEFAULT '',"
783 "slnr BLOB NOT NULL DEFAULT '',"
790 "ctyp,issr,slnr,agrp"
793 "rowid INTEGER PRIMARY KEY AUTOINCREMENT,"
796 "kcls INTEGER NOT NULL DEFAULT 0,"
802 "klbl BLOB NOT NULL DEFAULT '',"
803 "atag BLOB NOT NULL DEFAULT '',"
804 "crtr INTEGER NOT NULL DEFAULT 0,"
805 "type INTEGER NOT NULL DEFAULT 0,"
806 "bsiz INTEGER NOT NULL DEFAULT 0,"
807 "esiz INTEGER NOT NULL DEFAULT 0,"
808 "sdat REAL NOT NULL DEFAULT 0,"
809 "edat REAL NOT NULL DEFAULT 0,"
827 "kcls,klbl,atag,crtr,type,bsiz,esiz,sdat,edat,agrp"
829 "CREATE TABLE tversion(version INTEGER);"
830 "INSERT INTO tversion(version) VALUES(5);",
833 /* Create indices. */
834 "CREATE INDEX ialis ON cert(alis);"
835 "CREATE INDEX isubj ON cert(subj);"
836 "CREATE INDEX iskid ON cert(skid);"
837 "CREATE INDEX ipkhh ON cert(pkhh);"
838 "CREATE INDEX ikcls ON keys(kcls);"
839 "CREATE INDEX iklbl ON keys(klbl);"
840 "CREATE INDEX iencr ON keys(encr);"
841 "CREATE INDEX idecr ON keys(decr);"
842 "CREATE INDEX idrve ON keys(drve);"
843 "CREATE INDEX isign ON keys(sign);"
844 "CREATE INDEX ivrfy ON keys(vrfy);"
845 "CREATE INDEX iwrap ON keys(wrap);"
846 "CREATE INDEX iunwp ON keys(unwp);",
849 /* Rename version 1 tables. */
850 "ALTER TABLE genp RENAME TO ogenp;"
851 "ALTER TABLE inet RENAME TO oinet;"
852 "ALTER TABLE cert RENAME TO ocert;"
853 "ALTER TABLE keys RENAME TO okeys;",
856 /* Rename version 2 or version 3 tables and drop version table since
857 step 0 creates it. */
858 "ALTER TABLE genp RENAME TO ogenp;"
859 "ALTER TABLE inet RENAME TO oinet;"
860 "ALTER TABLE cert RENAME TO ocert;"
861 "ALTER TABLE keys RENAME TO okeys;"
862 "DROP TABLE tversion;",
865 /* Move data from version 1 or version 2 tables to new ones and drop old
867 /* Set the agrp on all (apple internal) items to apple. */
868 "INSERT INTO genp (rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,svce,gena,data) SELECT rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,svce,gena,data from ogenp;"
869 "INSERT INTO inet (rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,sdmn,srvr,ptcl,atyp,port,path,data) SELECT rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,sdmn,srvr,ptcl,atyp,port,path,data from oinet;"
870 "INSERT INTO cert (rowid,ctyp,cenc,labl,alis,subj,issr,slnr,skid,pkhh,data) SELECT rowid,ctyp,cenc,labl,alis,subj,issr,slnr,skid,pkhh,data from ocert;"
871 "INSERT INTO keys (rowid,kcls,labl,alis,perm,priv,modi,klbl,atag,crtr,type,bsiz,esiz,sdat,edat,sens,asen,extr,next,encr,decr,drve,sign,vrfy,snrc,vyrc,wrap,unwp,data) SELECT rowid,kcls,labl,alis,perm,priv,modi,klbl,atag,crtr,type,bsiz,esiz,sdat,edat,sens,asen,extr,next,encr,decr,drve,sign,vrfy,snrc,vyrc,wrap,unwp,data from okeys;"
872 "UPDATE genp SET agrp='apple';"
873 "UPDATE inet SET agrp='apple';"
874 "UPDATE cert SET agrp='apple';"
875 "UPDATE keys SET agrp='apple';"
882 /* Move data from version 3 tables to new ones and drop old ones. */
883 "INSERT INTO genp (rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,svce,gena,data,agrp) SELECT rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,svce,gena,data,agrp from ogenp;"
884 "INSERT INTO inet (rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,sdmn,srvr,ptcl,atyp,port,path,data,agrp) SELECT rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,sdmn,srvr,ptcl,atyp,port,path,data,agrp from oinet;"
885 "INSERT INTO cert (rowid,ctyp,cenc,labl,alis,subj,issr,slnr,skid,pkhh,data,agrp) SELECT rowid,ctyp,cenc,labl,alis,subj,issr,slnr,skid,pkhh,data,agrp from ocert;"
886 "INSERT INTO keys (rowid,kcls,labl,alis,perm,priv,modi,klbl,atag,crtr,type,bsiz,esiz,sdat,edat,sens,asen,extr,next,encr,decr,drve,sign,vrfy,snrc,vyrc,wrap,unwp,data,agrp) SELECT rowid,kcls,labl,alis,perm,priv,modi,klbl,atag,crtr,type,bsiz,esiz,sdat,edat,sens,asen,extr,next,encr,decr,drve,sign,vrfy,snrc,vyrc,wrap,unwp,data,agrp from okeys;"
893 /* Move data from version 4 tables to new ones and drop old ones. */
894 "INSERT INTO genp (rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,svce,gena,data,agrp,pdmn) SELECT rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,svce,gena,data,agrp,pdmn from ogenp;"
895 "INSERT INTO inet (rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,sdmn,srvr,ptcl,atyp,port,path,data,agrp,pdmn) SELECT rowid,cdat,mdat,desc,icmt,crtr,type,scrp,labl,alis,invi,nega,cusi,prot,acct,sdmn,srvr,ptcl,atyp,port,path,data,agrp,pdmn from oinet;"
896 "INSERT INTO cert (rowid,ctyp,cenc,labl,alis,subj,issr,slnr,skid,pkhh,data,agrp,pdmn) SELECT rowid,ctyp,cenc,labl,alis,subj,issr,slnr,skid,pkhh,data,agrp,pdmn from ocert;"
897 "INSERT INTO keys (rowid,kcls,labl,alis,perm,priv,modi,klbl,atag,crtr,type,bsiz,esiz,sdat,edat,sens,asen,extr,next,encr,decr,drve,sign,vrfy,snrc,vyrc,wrap,unwp,data,agrp,pdmn) SELECT rowid,kcls,labl,alis,perm,priv,modi,klbl,atag,crtr,type,bsiz,esiz,sdat,edat,sens,asen,extr,next,encr,decr,drve,sign,vrfy,snrc,vyrc,wrap,unwp,data,agrp,pdmn from okeys;"
911 static struct sql_stages s3dl_upgrade_script
[] = {
912 { -1, 0, 1, false }, /* Create version 5 database. */
913 { 2, 0, 4, true }, /* Upgrade to version 5 from version 1 (LittleBear). */
914 { 3, 0, 4, true }, /* Upgrade to version 5 from version 2 (BigBearBeta). */
915 { 3, 0, 5, true }, /* Upgrade to version 5 from version 3 (Apex). */
916 { 3, 0, 6, true }, /* Upgrade to version 5 from version 4 (Telluride). */
919 static int sql_run_script(s3dl_db_thread
*dbt
, int number
)
923 /* Script -1 == skip this step. */
927 /* If we are attempting to run a script we don't have, fail. */
928 if ((size_t)number
>= sizeof(s3dl_upgrade_sql
) / sizeof(*s3dl_upgrade_sql
))
929 return SQLITE_CORRUPT
;
932 s3e
= sqlite3_exec(dbt
->s3_handle
, s3dl_upgrade_sql
[number
],
933 NULL
, NULL
, &errmsg
);
935 secwarning("script %d: %s", number
, errmsg
);
936 sqlite3_free(errmsg
);
943 static int s3dl_dbt_upgrade_from_version(s3dl_db_thread
*dbt
, int version
)
945 /* We need to go from db version to CURRENT_DB_VERSION, let's do so. */
948 /* If we are attempting to upgrade to a version greater than what we have
949 an upgrade script for, fail. */
951 (size_t)version
>= sizeof(s3dl_upgrade_script
) / sizeof(*s3dl_upgrade_script
))
952 return SQLITE_CORRUPT
;
954 struct sql_stages
*script
= &s3dl_upgrade_script
[version
];
955 s3e
= sql_run_script(dbt
, script
->pre
);
956 if (s3e
== SQLITE_OK
)
957 s3e
= sql_run_script(dbt
, script
->main
);
958 if (s3e
== SQLITE_OK
)
959 s3e
= sql_run_script(dbt
, script
->post
);
960 if (script
->init_pdmn
) {
961 OSStatus status
= s3e
;
962 /* version 3 and earlier used legacy blob. */
963 CFDictionaryRef backup
= SecServerExportKeychainPlist(dbt
,
964 version
< 4 ? KEYBAG_LEGACY
: KEYBAG_DEVICE
,
965 KEYBAG_NONE
, kSecNoItemFilter
, version
, &status
);
968 secerror("Ignoring export error: %d during upgrade", status
);
970 status
= SecServerImportKeychainInPlist(dbt
, KEYBAG_NONE
,
971 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
);
973 } else if (status
== errSecInteractionNotAllowed
){
974 status
= errSecUpgradePending
;
982 static int s3dl_dbt_create_or_upgrade(s3dl_db_thread
*dbt
)
984 sqlite3_stmt
*stmt
= NULL
;
987 /* Find out if we need to upgrade from version 0 (empty db) or version 1
988 -- the db schema before we had a tversion table. */
989 s3e
= sqlite3_prepare(dbt
->s3_handle
, "SELECT cdat FROM genp", -1, &stmt
, NULL
);
991 sqlite3_finalize(stmt
);
993 return s3dl_dbt_upgrade_from_version(dbt
, s3e
? 0 : 1);
996 /* Return the current database version in *version. Returns a
998 static int s3dl_dbt_get_version(s3dl_db_thread
*dbt
, int *version
)
1000 sqlite3
*s3h
= dbt
->s3_handle
;
1003 sqlite3_stmt
*stmt
= NULL
;
1004 static const char sql
[] = "SELECT version FROM tversion LIMIT 1;";
1005 s3e
= sqlite3_prepare(s3h
, sql
, sizeof(sql
) - 1, &stmt
, NULL
);
1009 s3e
= sqlite3_step(stmt
);
1010 if (s3e
== SQLITE_ROW
) {
1011 *version
= sqlite3_column_int(stmt
, 0);
1014 secwarning("SELECT version step: %s", sqlite3_errmsg(s3h
));
1016 /* We have a VERSION table but we didn't find a version
1017 value now what? I suppose we pretend the db is corrupted
1018 since this isn't supposed to ever happen. */
1019 s3e
= SQLITE_CORRUPT
;
1024 /* We ignore this error since this function may return SQLITE_ERROR,
1025 SQLITE_IOERR_READ or SQLITE_ABORT if the stmt itself failed, but
1026 that's something we would have handeled already. */
1027 sqlite3_finalize(stmt
);
1033 /* This function is called if the db doesn't have the proper version. We
1034 start an exclusive transaction and recheck the version, and then perform
1035 the upgrade within that transaction. */
1036 static int s3dl_dbt_upgrade(s3dl_db_thread
*dbt
)
1041 require_noerr(s3e
= s3dl_begin_transaction(dbt
), errOut
);
1042 s3e
= s3dl_dbt_get_version(dbt
, &version
);
1044 s3e
= s3dl_dbt_upgrade_from_version(dbt
, version
);
1046 /* We have no version table yet so we need to create a new db or
1047 upgrade from version 1 (the db schema without a tversion table). */
1048 require_noerr(s3e
= s3dl_dbt_create_or_upgrade(dbt
), errOut
);
1053 return s3dl_end_transaction(dbt
, s3e
);
1056 static int s3dl_create_dbt(s3dl_db
*db
, s3dl_db_thread
**pdbt
, int create
)
1060 for (retries
= 0; retries
< 2; ++retries
) {
1061 s3e
= sqlite3_open(db
->db_name
, &s3h
);
1062 if (s3e
== SQLITE_CANTOPEN
&& create
)
1064 /* Make sure the path to db->db_name exists and is writable, then
1066 s3dl_create_path(db
->db_name
);
1067 s3e
= sqlite3_open(db
->db_name
, &s3h
);
1071 s3dl_db_thread
*dbt
= (s3dl_db_thread
*)malloc(sizeof(s3dl_db_thread
));
1072 dbt
->dbt_next
= NULL
;
1074 dbt
->s3_handle
= s3h
;
1075 dbt
->autocommit
= true;
1076 dbt
->in_transaction
= false;
1079 s3e
= s3dl_dbt_get_version(dbt
, &version
);
1080 if (s3e
== SQLITE_EMPTY
|| s3e
== SQLITE_ERROR
|| (!s3e
&& version
< CURRENT_DB_VERSION
)) {
1081 s3e
= s3dl_dbt_upgrade(dbt
);
1083 asl_log(NULL
, NULL
, ASL_LEVEL_CRIT
,
1084 "failed to upgrade keychain %s: %d", db
->db_name
, s3e
);
1085 if (s3e
!= errSecUpgradePending
) {
1086 s3e
= SQLITE_CORRUPT
;
1090 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
1091 "failed to obtain database version for %s: %d",
1093 } else if (version
> CURRENT_DB_VERSION
) {
1094 /* We can't downgrade so we treat a too new db as corrupted. */
1095 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
1096 "found keychain %s with version: %d which is newer than %d marking as corrupted",
1097 db
->db_name
, version
, CURRENT_DB_VERSION
);
1098 s3e
= SQLITE_CORRUPT
;
1102 s3dl_close_dbt(dbt
);
1109 if (s3e
== SQLITE_CORRUPT
|| s3e
== SQLITE_NOTADB
||
1110 s3e
== SQLITE_CANTOPEN
|| s3e
== SQLITE_PERM
||
1111 s3e
== SQLITE_CONSTRAINT
) {
1112 size_t len
= strlen(db
->db_name
);
1113 char *old_db_name
= malloc(len
+ 9);
1114 memcpy(old_db_name
, db
->db_name
, len
);
1115 strcpy(old_db_name
+ len
, ".corrupt");
1116 if (rename(db
->db_name
, old_db_name
)) {
1117 asl_log(NULL
, NULL
, ASL_LEVEL_CRIT
,
1118 "unable to rename corrupt keychain %s -> %s: %s",
1119 db
->db_name
, old_db_name
, strerror(errno
));
1123 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
1124 "renamed corrupt keychain %s -> %s (%d)",
1125 db
->db_name
, old_db_name
, s3e
);
1129 asl_log(NULL
, NULL
, ASL_LEVEL_CRIT
,
1130 "failed to open keychain %s: %d", db
->db_name
, s3e
);
1138 /* Called when a thread that was using this db goes away. */
1139 static void s3dl_dbt_destructor(void *data
)
1141 s3dl_db_thread
*dbt
= (s3dl_db_thread
*)data
;
1144 /* Remove the passed in dbt from the linked list. */
1145 /* TODO: Log pthread errors. */
1146 pthread_mutex_lock(&dbt
->db
->mutex
);
1147 s3dl_db_thread
**pdbt
= &dbt
->db
->dbt_head
;
1148 for (;*pdbt
; pdbt
= &(*pdbt
)->dbt_next
)
1152 *pdbt
= dbt
->dbt_next
;
1157 /* TODO: Log pthread errors. */
1158 pthread_mutex_unlock(&dbt
->db
->mutex
);
1160 /* Don't hold dbt->db->mutex while cleaning up the dbt. */
1162 s3dl_close_dbt(dbt
);
1165 /* Agressivly write to pdbHandle since we want to be able to call internal SPI
1166 function during initialization. */
1167 static int s3dl_create_db_handle(const char *db_name
, db_handle
*pdbHandle
,
1168 s3dl_db_thread
**pdbt
, bool autocommit
, bool create
, bool use_hwaes
)
1170 void *mem
= malloc(sizeof(s3dl_db
) + strlen(db_name
) + 1);
1171 s3dl_db
*db
= (s3dl_db
*)mem
;
1172 db
->db_name
= ((char *)mem
) + sizeof(*db
);
1173 strcpy(db
->db_name
, db_name
);
1174 /* Make sure we set this before calling s3dl_create_dbt, since that might
1175 trigger a db upgrade which needs to decrypt stuff. */
1176 db
->use_hwaes
= use_hwaes
;
1178 s3dl_db_thread
*dbt
;
1179 int s3e
= s3dl_create_dbt(db
, &dbt
, create
);
1180 if (s3e
!= SQLITE_OK
)
1182 if (s3e
== errSecUpgradePending
) {
1183 secerror("Device locked during initial open + upgrade attempt");
1190 dbt
->autocommit
= autocommit
;
1194 int err
= pthread_key_create(&db
->key
, s3dl_dbt_destructor
);
1196 err
= pthread_mutex_init(&db
->mutex
, NULL
);
1198 err
= pthread_setspecific(db
->key
, dbt
);
1201 /* TODO: Log err (which is an errno) somehow. */
1203 s3e
= s3dl_close_dbt(dbt
);
1204 if (s3e
== SQLITE_OK
)
1205 s3e
= SQLITE_INTERNAL
;
1212 *pdbHandle
= (db_handle
)db
;
1218 static int s3dl_close_db_handle(db_handle dbHandle
)
1220 s3dl_db
*db
= (s3dl_db
*)dbHandle
;
1221 int s3e
= SQLITE_OK
;
1223 /* Walk the list of dbt's and close them all. */
1224 s3dl_db_thread
*next_dbt
= db
->dbt_head
;
1227 s3dl_db_thread
*dbt
= next_dbt
;
1228 next_dbt
= next_dbt
->dbt_next
;
1229 int s3e2
= s3dl_close_dbt(dbt
);
1230 if (s3e2
!= SQLITE_OK
&& s3e
== SQLITE_OK
)
1234 pthread_key_delete(db
->key
);
1240 static int s3dl_get_dbt(db_handle dbHandle
, s3dl_db_thread
**pdbt
)
1243 return SQLITE_ERROR
;
1245 s3dl_db
*db
= (s3dl_db
*)dbHandle
;
1246 int s3e
= SQLITE_OK
;
1247 s3dl_db_thread
*dbt
= pthread_getspecific(db
->key
);
1250 /* We had no dbt yet, so create a new one, but don't create the
1252 s3e
= s3dl_create_dbt(db
, &dbt
, false);
1253 if (s3e
== SQLITE_OK
)
1255 /* Lock the mutex, insert the new entry at the head of the
1256 linked list and release the lock. */
1257 int err
= pthread_mutex_lock(&db
->mutex
);
1260 dbt
->dbt_next
= db
->dbt_head
;
1262 err
= pthread_mutex_unlock(&db
->mutex
);
1265 /* Set the dbt as this threads dbt for db. */
1267 err
= pthread_setspecific(db
->key
, dbt
);
1270 /* TODO: Log err (which is an errno) somehow. */
1271 s3e
= s3dl_close_dbt(dbt
);
1272 if (s3e
== SQLITE_OK
)
1273 s3e
= SQLITE_INTERNAL
;
1281 /* Return an OSStatus for a sqlite3 error code. */
1282 static OSStatus
osstatus_for_s3e(int s3e
)
1284 if (s3e
> 0 && s3e
<= SQLITE_DONE
) switch (s3e
)
1289 return errSecNotAvailable
; /* errSecDuplicateItem; */
1290 case SQLITE_FULL
: /* Happens if we run out of uniqueids */
1291 return errSecNotAvailable
; /* TODO: Replace with a better error code. */
1293 case SQLITE_READONLY
:
1294 return errSecNotAvailable
;
1295 case SQLITE_CANTOPEN
:
1296 return errSecNotAvailable
;
1298 return errSecNotAvailable
;
1299 case SQLITE_CONSTRAINT
:
1300 return errSecDuplicateItem
;
1303 case SQLITE_MISMATCH
:
1304 return errSecNoSuchAttr
;
1306 return errSecNotAvailable
;
1308 return -2; /* TODO: Replace with a real error code. */
1309 case SQLITE_INTERNAL
:
1311 return errSecNotAvailable
; /* TODO: Replace with a real error code. */
1316 const uint32_t v0KeyWrapOverHead
= 8;
1318 /* Wrap takes a 128 - 256 bit key as input and returns output of
1319 inputsize + 64 bits.
1320 In bytes this means that a
1321 16 byte (128 bit) key returns a 24 byte wrapped key
1322 24 byte (192 bit) key returns a 32 byte wrapped key
1323 32 byte (256 bit) key returns a 40 byte wrapped key */
1324 static int ks_crypt(uint32_t selector
, keybag_handle_t keybag
,
1325 keyclass_t keyclass
, uint32_t textLength
, const uint8_t *source
, uint8_t *dest
, size_t *dest_len
) {
1327 kern_return_t kernResult
;
1329 if (keystore
== MACH_PORT_NULL
) {
1330 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "No AppleKeyStore connection");
1331 return errSecNotAvailable
;
1334 uint64_t inputs
[] = { keybag
, keyclass
};
1335 uint32_t num_inputs
= sizeof(inputs
)/sizeof(*inputs
);
1336 kernResult
= IOConnectCallMethod(keystore
, selector
, inputs
,
1337 num_inputs
, source
, textLength
, NULL
, NULL
, dest
, dest_len
);
1339 if (kernResult
!= KERN_SUCCESS
) {
1340 if (kernResult
== kIOReturnNotPermitted
) {
1341 /* Access to item attempted while keychain is locked. */
1342 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
,
1343 "%s sel: %d bag: %d cls: %d src: %p len: %"PRIu32
" err: kIOReturnNotPermitted",
1344 (selector
== kAppleKeyStoreKeyWrap
? "kAppleKeyStoreKeyWrap"
1345 : "kAppleKeyStoreKeyUnwrap"),
1346 selector
, keybag
, keyclass
, source
, textLength
);
1347 return errSecInteractionNotAllowed
;
1348 } else if (kernResult
== kIOReturnError
) {
1349 /* Item can't be decrypted on this device, ever, so drop the item. */
1350 secerror("%s sel: %d bag: %d cls: %d src: %p len: %lu err: kIOReturnError",
1351 (selector
== kAppleKeyStoreKeyWrap
? "kAppleKeyStoreKeyWrap"
1352 : "kAppleKeyStoreKeyUnwrap"),
1353 selector
, keybag
, keyclass
, source
, textLength
);
1354 return errSecDecode
;
1356 secerror("%s sel: %d bag: %d cls: %d src: %p len: %lu err: %x",
1357 (selector
== kAppleKeyStoreKeyWrap
? "kAppleKeyStoreKeyWrap"
1358 : "kAppleKeyStoreKeyUnwrap"),
1359 selector
, keybag
, keyclass
, source
, textLength
, kernResult
);
1360 return errSecNotAvailable
;
1363 return errSecSuccess
;
1365 if (selector
== kAppleKeyStoreKeyWrap
) {
1366 /* The no encryption case. */
1367 if (*dest_len
>= textLength
+ 8) {
1368 memcpy(dest
, source
, textLength
);
1369 memset(dest
+ textLength
, 8, 8);
1370 *dest_len
= textLength
+ 8;
1372 return errSecNotAvailable
;
1373 } else if (selector
== kAppleKeyStoreKeyUnwrap
) {
1374 if (*dest_len
+ 8 >= textLength
) {
1375 memcpy(dest
, source
, textLength
- 8);
1376 *dest_len
= textLength
- 8;
1378 return errSecNotAvailable
;
1380 return errSecSuccess
;
1386 typedef struct kc_item
{
1387 CFMutableDictionaryRef item
;
1393 #define kc_item_size(n) sizeof(kc_item) + 2 * sizeof(void *)
1394 #define kc_item_init(i, n) do { \
1395 kc_item *_kc_item_a = (i); \
1396 _kc_item_a->item = NULL; \
1397 _kc_item_a->n_attrs = (n); \
1398 _kc_item_a->attrs = (CFStringRef *)&_kc_item_a->values[_kc_item_a->n_attrs]; \
1401 static kc_item
*kc_item_create(CFIndex n_attrs
) {
1402 kc_item
*item
= malloc(kc_item_size(n_attrs
));
1403 kc_item_init(item
, n_attrs
);
1407 static void kc_item_destroy(kc_item
*item
) {
1408 CFReleaseSafe(item
->item
);
1412 static kc_item
*kc_item_init_with_data() {
1416 /* Encodes an item. */
1417 static CFDataRef
kc_item_encode(const kc_item
*item
) {
1418 CFDictionaryRef attrs
= CFDictionaryCreate(0, (const void **)item
->attrs
,
1419 (const void **)item
->values
,
1420 item
->n_attrs
, 0, 0);
1421 CFDataRef encoded
= CFPropertyListCreateData(0, attrs
,
1422 kCFPropertyListBinaryFormat_v1_0
, 0, 0);
1427 struct kc_item_set_attr
{
1433 static void kc_item_set_attr(CFStringRef key
, void *value
,
1435 struct kc_item_set_attr
*c
= context
;
1436 if (CFGetTypeID(key
) != CFStringGetTypeID()) {
1440 c
->item
->attrs
[c
->ix
] = key
;
1441 c
->item
->values
[c
->ix
++] = value
;
1444 /* Returns a malloced item. */
1445 static CFDictionaryRef
kc_item_decode(CFDataRef encoded_item
) {
1446 CFPropertyListFormat format
;
1447 CFPropertyListRef attrs
;
1448 attrs
= CFPropertyListCreateWithData(0, encoded_item
,
1449 kCFPropertyListImmutable
, &format
, 0);
1453 kc_item
*item
= NULL
;
1454 if (CFGetTypeID(attrs
) != CFDictionaryGetTypeID())
1457 item
= kc_item_create(CFDictionaryGetCount(attrs
));
1459 CFDictionaryApplyFunction(attrs
,
1460 (CFDictionaryApplierFunction
)kc_item_set_attr
,
1468 kc_item_destroy(item
);
1475 /* Given plainText create and return a CFDataRef containing:
1476 BULK_KEY = RandomKey()
1477 version || keyclass || KeyStore_WRAP(keyclass, BULK_KEY) ||
1478 AES(BULK_KEY, NULL_IV, plainText || padding)
1480 static int ks_encrypt_data(keybag_handle_t keybag
,
1481 keyclass_t keyclass
, CFDataRef plainText
, CFDataRef
*pBlob
) {
1482 CFMutableDataRef blob
= NULL
;
1483 //check(keybag >= 0);
1485 /* Precalculate output blob length. */
1486 const uint32_t bulkKeySize
= 32; /* Use 256 bit AES key for bulkKey. */
1487 const uint32_t maxKeyWrapOverHead
= 8 + 32;
1488 uint8_t bulkKey
[bulkKeySize
];
1489 uint8_t bulkKeyWrapped
[bulkKeySize
+ maxKeyWrapOverHead
];
1490 size_t bulkKeyWrappedSize
= sizeof(bulkKeyWrapped
);
1491 uint32_t key_wrapped_size
;
1493 /* TODO: We should return a better error here. */
1494 int s3e
= errSecAllocate
;
1495 if (!plainText
|| CFGetTypeID(plainText
) != CFDataGetTypeID()
1501 size_t ptLen
= CFDataGetLength(plainText
);
1502 size_t ctLen
= ptLen
;
1504 uint32_t version
= 2;
1506 if (SecRandomCopyBytes(kSecRandomDefault
, bulkKeySize
, bulkKey
))
1509 /* Now that we're done using the bulkKey, in place encrypt it. */
1510 require_noerr_quiet(s3e
= ks_crypt(kAppleKeyStoreKeyWrap
, keybag
, keyclass
,
1511 bulkKeySize
, bulkKey
, bulkKeyWrapped
,
1512 &bulkKeyWrappedSize
), out
);
1513 key_wrapped_size
= (uint32_t)bulkKeyWrappedSize
;
1515 size_t blobLen
= sizeof(version
) + sizeof(keyclass
) +
1516 sizeof(key_wrapped_size
) + key_wrapped_size
+ ctLen
+ tagLen
;
1518 require_quiet(blob
= CFDataCreateMutable(NULL
, blobLen
), out
);
1519 CFDataSetLength(blob
, blobLen
);
1520 UInt8
*cursor
= CFDataGetMutableBytePtr(blob
);
1522 *((uint32_t *)cursor
) = version
;
1523 cursor
+= sizeof(version
);
1525 *((keyclass_t
*)cursor
) = keyclass
;
1526 cursor
+= sizeof(keyclass
);
1528 *((uint32_t *)cursor
) = key_wrapped_size
;
1529 cursor
+= sizeof(key_wrapped_size
);
1531 memcpy(cursor
, bulkKeyWrapped
, key_wrapped_size
);
1532 cursor
+= key_wrapped_size
;
1534 /* Encrypt the plainText with the bulkKey. */
1535 CCCryptorStatus ccerr
= CCCryptorGCM(kCCEncrypt
, kCCAlgorithmAES128
,
1536 bulkKey
, bulkKeySize
,
1538 NULL
, 0, /* auth data */
1539 CFDataGetBytePtr(plainText
), ptLen
,
1541 cursor
+ ctLen
, &tagLen
);
1543 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "CCCryptorGCM failed: %d", ccerr
);
1544 s3e
= errSecInternal
;
1548 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
1549 "CCCryptorGCM expected: 16 got: %ld byte tag", tagLen
);
1550 s3e
= errSecInternal
;
1555 memset(bulkKey
, 0, sizeof(bulkKey
));
1557 CFReleaseSafe(blob
);
1564 /* Given cipherText containing:
1565 version || keyclass || KeyStore_WRAP(keyclass, BULK_KEY) ||
1566 AES(BULK_KEY, NULL_IV, plainText || padding)
1567 return the plainText. */
1568 static int ks_decrypt_data(keybag_handle_t keybag
,
1569 keyclass_t
*pkeyclass
, CFDataRef blob
, CFDataRef
*pPlainText
,
1570 uint32_t *version_p
) {
1571 const uint32_t bulkKeySize
= 32; /* Use 256 bit AES key for bulkKey. */
1572 uint8_t bulkKey
[bulkKeySize
];
1573 size_t bulkKeyCapacity
= sizeof(bulkKey
);
1575 CFMutableDataRef plainText
= NULL
;
1578 int s3e
= errSecDecode
;
1580 /* TODO: We should return a better error here. */
1585 size_t blobLen
= CFDataGetLength(blob
);
1586 const uint8_t *cursor
= CFDataGetBytePtr(blob
);
1588 keyclass_t keyclass
;
1589 uint32_t wrapped_key_size
;
1591 /* Check for underflow, ensuring we have at least one full AES block left. */
1592 if (blobLen
< sizeof(version
) + sizeof(keyclass
) +
1593 bulkKeySize
+ v0KeyWrapOverHead
+ 16)
1596 version
= *((uint32_t *)cursor
);
1597 cursor
+= sizeof(version
);
1599 keyclass
= *((keyclass_t
*)cursor
);
1601 *pkeyclass
= keyclass
;
1602 cursor
+= sizeof(keyclass
);
1604 size_t minimum_blob_len
= sizeof(version
) + sizeof(keyclass
) + 16;
1605 size_t ctLen
= blobLen
- sizeof(version
) - sizeof(keyclass
);
1609 wrapped_key_size
= bulkKeySize
+ v0KeyWrapOverHead
;
1613 minimum_blob_len
-= 16; // Remove PKCS7 padding block requirement
1614 ctLen
-= tagLen
; // Remove tagLen from ctLen
1617 wrapped_key_size
= *((uint32_t *)cursor
);
1618 cursor
+= sizeof(wrapped_key_size
);
1619 minimum_blob_len
+= sizeof(wrapped_key_size
);
1620 ctLen
-= sizeof(wrapped_key_size
);
1626 /* Validate key wrap length against total length */
1627 require(blobLen
- minimum_blob_len
- tagLen
>= wrapped_key_size
, out
);
1628 ctLen
-= wrapped_key_size
;
1629 if (version
< 2 && (ctLen
& 0xF) != 0)
1632 /* Now unwrap the bulk key using a key in the keybag. */
1633 require_noerr_quiet(s3e
= ks_crypt(kAppleKeyStoreKeyUnwrap
, keybag
,
1634 keyclass
, wrapped_key_size
, cursor
, bulkKey
, &bulkKeyCapacity
), out
);
1635 cursor
+= wrapped_key_size
;
1637 plainText
= CFDataCreateMutable(NULL
, ctLen
);
1640 CFDataSetLength(plainText
, ctLen
);
1642 /* Decrypt the cipherText with the bulkKey. */
1643 CCCryptorStatus ccerr
;
1645 uint8_t tag
[tagLen
];
1646 ccerr
= CCCryptorGCM(kCCDecrypt
, kCCAlgorithmAES128
,
1647 bulkKey
, bulkKeySize
,
1649 NULL
, 0, /* auth data */
1651 CFDataGetMutableBytePtr(plainText
),
1654 secerror("CCCryptorGCM failed: %d", ccerr
);
1655 /* TODO: Should this be errSecDecode once AppleKeyStore correctly
1656 identifies uuid unwrap failures? */
1657 s3e
= errSecDecode
; /* errSecInteractionNotAllowed; */
1661 secerror("CCCryptorGCM expected: 16 got: %ld byte tag", tagLen
);
1662 s3e
= errSecInternal
;
1666 if (memcmp(tag
, cursor
, tagLen
)) {
1667 secerror("CCCryptorGCM computed tag not same as tag in blob");
1673 ccerr
= CCCrypt(kCCDecrypt
, kCCAlgorithmAES128
, kCCOptionPKCS7Padding
,
1674 bulkKey
, bulkKeySize
, NULL
, cursor
, ctLen
,
1675 CFDataGetMutableBytePtr(plainText
), ctLen
, &ptLen
);
1677 secerror("CCCrypt failed: %d", ccerr
);
1678 /* TODO: Should this be errSecDecode once AppleKeyStore correctly
1679 identifies uuid unwrap failures? */
1680 s3e
= errSecDecode
; /* errSecInteractionNotAllowed; */
1683 CFDataSetLength(plainText
, ptLen
);
1685 s3e
= errSecSuccess
;
1686 if (version_p
) *version_p
= version
;
1688 memset(bulkKey
, 0, bulkKeySize
);
1690 CFReleaseSafe(plainText
);
1692 *pPlainText
= plainText
;
1697 /* Iff dir_encrypt is true dir_encrypt source -> dest, otherwise decrypt
1698 source -> dest. In both cases iv is used asa the Initialization Vector and
1699 textLength bytes are encrypted or decrypted. TextLength must be a multiple
1700 of 16, since this function does not do any padding. */
1701 static bool kc_aes_crypt(s3dl_db_thread
*dbt
, bool dir_encrypt
, const UInt8
*iv
,
1702 UInt32 textLength
, const UInt8
*source
, UInt8
*dest
) {
1704 if (dbt
->db
->use_hwaes
) {
1705 IOAESOperation operation
;
1710 operation
= IOAESOperationEncrypt
;
1711 plainText
= (UInt8
*)source
;
1714 operation
= IOAESOperationDecrypt
;
1716 cipherText
= (UInt8
*)source
;
1719 return hwaes_crypt(operation
,
1720 kIOAESAcceleratorKeyHandleKeychain
, 128, NULL
, iv
, textLength
,
1721 plainText
, cipherText
);
1724 #endif /* USE_HWAES */
1726 /* The no encryption case. */
1727 memcpy(dest
, source
, textLength
);
1733 /* Pre 4.0 blob encryption code, unused when keystore support is enabled. */
1735 /* Given plainText create and return a CFDataRef containing:
1736 IV || AES(KC_KEY, IV, plainText || SHA1(plainText) || padding)
1738 static int kc_encrypt_data(s3dl_db_thread
*dbt
, CFDataRef plainText
,
1739 CFDataRef
*pCipherText
) {
1740 CFMutableDataRef cipherText
= NULL
;
1741 /* TODO: We should return a better error here. */
1742 int s3e
= SQLITE_AUTH
;
1743 if (!plainText
|| CFGetTypeID(plainText
) != CFDataGetTypeID()) {
1744 s3e
= SQLITE_MISMATCH
;
1748 CFIndex ptLen
= CFDataGetLength(plainText
);
1749 CFIndex ctLen
= ptLen
+ CC_SHA1_DIGEST_LENGTH
;
1750 CFIndex padLen
= 16 - (ctLen
& 15);
1751 /* Pad output buffer capacity to nearest multiple of 32 bytes for cache
1753 CFIndex paddedTotalLength
= (16 + ctLen
+ padLen
+ 0x1F) & ~0x1F;
1754 cipherText
= CFDataCreateMutable(NULL
, paddedTotalLength
);
1757 CFDataSetLength(cipherText
, 16 + ctLen
+ padLen
);
1758 UInt8
*iv
= CFDataGetMutableBytePtr(cipherText
);
1759 if (SecRandomCopyBytes(kSecRandomDefault
, 16, iv
))
1762 UInt8
*ct
= iv
+ 16;
1763 const UInt8
*pt
= CFDataGetBytePtr(plainText
);
1764 memcpy(ct
, pt
, ptLen
);
1765 CC_SHA1(pt
, ptLen
, ct
+ ptLen
);
1766 memset(ct
+ ctLen
, padLen
, padLen
);
1768 /* Encrypt the data in place. */
1769 if (!kc_aes_crypt(dbt
, true, iv
, ctLen
+ padLen
, ct
, ct
)) {
1776 CFReleaseSafe(cipherText
);
1778 *pCipherText
= cipherText
;
1784 /* Given cipherText containing:
1785 IV || AES(KC_KEY, IV, plainText || SHA1(plainText) || padding)
1786 return the plainText. */
1787 static int kc_decrypt_data(s3dl_db_thread
*dbt
, CFDataRef cipherText
,
1788 CFDataRef
*pPlainText
) {
1789 CFMutableDataRef plainText
= NULL
;
1790 /* TODO: We should return a better error here. */
1791 int s3e
= SQLITE_AUTH
;
1795 CFIndex ctLen
= CFDataGetLength(cipherText
);
1796 if (ctLen
< 48 || (ctLen
& 0xF) != 0)
1799 const UInt8
*iv
= CFDataGetBytePtr(cipherText
);
1800 const UInt8
*ct
= iv
+ 16;
1801 CFIndex ptLen
= ctLen
- 16;
1803 /* Cast: debug check for overflow before casting to uint32_t later */
1804 assert((unsigned long)ptLen
<UINT32_MAX
); /* correct as long as CFIndex is signed long */
1806 /* Pad output buffer capacity to nearest multiple of 32 bytes for cache
1808 CFIndex paddedLength
= (ptLen
+ 0x1F) & ~0x1F;
1809 plainText
= CFDataCreateMutable(NULL
, paddedLength
);
1812 CFDataSetLength(plainText
, ptLen
);
1813 UInt8
*pt
= CFDataGetMutableBytePtr(plainText
);
1815 /* 64 bits case: Worst case here is we dont decrypt the full data. No security issue */
1816 if (!kc_aes_crypt(dbt
, false, iv
, (uint32_t)ptLen
, ct
, pt
)) {
1820 /* Now check and remove the padding. */
1821 UInt8 pad
= pt
[ptLen
- 1];
1822 if (pad
< 1 || pad
> 16) {
1823 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
1824 "kc_decrypt_data: bad padding bytecount: 0x%02X", pad
);
1829 for (ix
= 0; ix
< pad
- 1; ++ix
) {
1830 if (pt
[ptLen
+ ix
] != pad
) {
1831 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
1832 "kc_decrypt_data: bad padding byte: %lu: 0x%02X",
1833 ix
, pt
[ptLen
+ ix
]);
1838 UInt8 sha1
[CC_SHA1_DIGEST_LENGTH
];
1839 ptLen
-= CC_SHA1_DIGEST_LENGTH
;
1840 /* 64 bits cast: worst case here is we dont hash the full data and the decrypt fail or
1841 suceed when it should not have. No security issue. */
1842 CC_SHA1(pt
, (CC_LONG
)ptLen
, sha1
);
1843 if (memcmp(sha1
, pt
+ ptLen
, CC_SHA1_DIGEST_LENGTH
)) {
1844 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "kc_decrypt_data: digest mismatch");
1848 CFDataSetLength(plainText
, ptLen
);
1853 CFReleaseSafe(plainText
);
1855 *pPlainText
= plainText
;
1860 /* AUDIT[securityd](done):
1861 value (ok) is a caller provided, non NULL CFTypeRef.
1863 static int kc_bind_paramter(sqlite3_stmt
*stmt
, int param
, CFTypeRef value
)
1868 /* TODO: Can we use SQLITE_STATIC below everwhere we currently use
1869 SQLITE_TRANSIENT since we finalize the statement before the value
1870 goes out of scope? */
1871 if (!value
|| (valueId
= CFGetTypeID(value
)) == CFNullGetTypeID()) {
1872 /* Skip bindings for NULL values. sqlite3 will interpret unbound
1873 params as NULL which is exactly what we want. */
1877 s3e
= sqlite3_bind_null(stmt
, param
);
1879 secdebug("bind", "bind_null: %d", s3e
);
1880 } else if (valueId
== CFStringGetTypeID()) {
1881 const char *cstr
= CFStringGetCStringPtr(value
, kCFStringEncodingUTF8
);
1883 s3e
= sqlite3_bind_text_wrapper(stmt
, param
, cstr
, strlen(cstr
),
1885 secdebug("bind", "quick bind_text: %s: %d", cstr
, s3e
);
1888 CFRange range
= { 0, CFStringGetLength(value
) };
1889 CFStringGetBytes(value
, range
, kCFStringEncodingUTF8
,
1890 0, FALSE
, NULL
, 0, &len
);
1892 CFIndex usedlen
= 0;
1894 CFStringGetBytes(value
, range
, kCFStringEncodingUTF8
,
1895 0, FALSE
, (UInt8
*)buf
, len
, &usedlen
);
1896 s3e
= sqlite3_bind_text_wrapper(stmt
, param
, buf
, usedlen
,
1898 secdebug("bind", "slow bind_text: %.*s: %d", usedlen
, buf
, s3e
);
1901 } else if (valueId
== CFDataGetTypeID()) {
1902 CFIndex len
= CFDataGetLength(value
);
1904 s3e
= sqlite3_bind_blob_wrapper(stmt
, param
, CFDataGetBytePtr(value
),
1905 len
, SQLITE_TRANSIENT
);
1906 secdebug("bind", "bind_blob: %.*s: %d",
1907 CFDataGetLength(value
), CFDataGetBytePtr(value
), s3e
);
1909 s3e
= sqlite3_bind_text(stmt
, param
, "", 0, SQLITE_TRANSIENT
);
1911 } else if (valueId
== CFDateGetTypeID()) {
1912 CFAbsoluteTime abs_time
= CFDateGetAbsoluteTime(value
);
1913 s3e
= sqlite3_bind_double(stmt
, param
, abs_time
);
1914 secdebug("bind", "bind_double: %f: %d", abs_time
, s3e
);
1915 } else if (valueId
== CFBooleanGetTypeID()) {
1916 int bval
= CFBooleanGetValue(value
);
1917 s3e
= sqlite3_bind_int(stmt
, param
, bval
);
1918 secdebug("bind", "bind_int: %d: %d", bval
, s3e
);
1919 } else if (valueId
== CFNumberGetTypeID()) {
1921 if (CFNumberIsFloatType(value
)) {
1923 convertOk
= CFNumberGetValue(value
, kCFNumberDoubleType
, &nval
);
1924 s3e
= sqlite3_bind_double(stmt
, param
, nval
);
1925 secdebug("bind", "bind_double: %f: %d", nval
, s3e
);
1927 CFIndex len
= CFNumberGetByteSize(value
);
1928 /* TODO: should sizeof int be 4? sqlite seems to think so. */
1929 if (len
<= (CFIndex
)sizeof(int)) {
1931 convertOk
= CFNumberGetValue(value
, kCFNumberIntType
, &nval
);
1932 s3e
= sqlite3_bind_int(stmt
, param
, nval
);
1933 secdebug("bind", "bind_int: %d: %d", nval
, s3e
);
1936 convertOk
= CFNumberGetValue(value
, kCFNumberLongLongType
,
1938 s3e
= sqlite3_bind_int64(stmt
, param
, nval
);
1939 secdebug("bind", "bind_int64: %lld: %d", nval
, s3e
);
1943 /* TODO: CFNumberGetValue failed somehow. */
1944 s3e
= SQLITE_INTERNAL
;
1947 /* Unsupported CF type used. */
1948 s3e
= SQLITE_MISMATCH
;
1954 /* Compile the statement in sql and return it as stmt. */
1955 static int kc_prepare_statement(sqlite3
*s3h
, CFStringRef sql
,
1956 sqlite3_stmt
**stmt
)
1959 const char *cstr
= CFStringGetCStringPtr(sql
, kCFStringEncodingUTF8
);
1961 secdebug("sql", "quick prepare: %s", cstr
);
1962 s3e
= sqlite3_prepare_wrapper(s3h
, cstr
, strlen(cstr
), stmt
, NULL
);
1965 CFRange range
= { 0, CFStringGetLength(sql
) };
1966 CFStringGetBytes(sql
, range
, kCFStringEncodingUTF8
,
1967 0, FALSE
, NULL
, 0, &len
);
1969 CFIndex usedlen
= 0;
1971 CFStringGetBytes(sql
, range
, kCFStringEncodingUTF8
,
1972 0, FALSE
, (UInt8
*)buf
, len
, &usedlen
);
1973 secdebug("sql", "slow prepare: %.*s", usedlen
, buf
);
1974 s3e
= sqlite3_prepare_wrapper(s3h
, buf
, usedlen
, stmt
, NULL
);
1978 /* sqlite3_prepare returns SQLITE_ERROR if the table doesn't exist or one
1979 of the attributes the caller passed in doesn't exist. */
1980 if (s3e
== SQLITE_ERROR
) {
1981 secdebug("sql", "sqlite3_prepare: %s", sqlite3_errmsg(s3h
));
1989 typedef uint32_t ReturnTypeMask
;
1992 kSecReturnDataMask
= 1 << 0,
1993 kSecReturnAttributesMask
= 1 << 1,
1994 kSecReturnRefMask
= 1 << 2,
1995 kSecReturnPersistentRefMask
= 1 << 3,
1998 /* Constant indicating there is no limit to the number of results to return. */
2001 kSecMatchUnlimited
= kCFNotFound
2004 /* Upper limit for number of keys in a QUERY dictionary. */
2006 #define QUERY_KEY_LIMIT (31 + 53)
2008 #define QUERY_KEY_LIMIT (53)
2017 /* Nothing in this struct is retained since all the
2018 values below are extracted from the dictionary passed in by the
2020 typedef struct Query
2022 /* Class of this query. */
2023 const kc_class
*q_class
;
2025 /* Dictionary with all attributes and values in clear (to be encrypted). */
2026 CFMutableDictionaryRef q_item
;
2028 /* q_pairs is an array of Pair structs. Elements with indices
2029 [0, q_attr_end) contain attribute key value pairs. Elements with
2030 indices [q_match_begin, q_match_end) contain match key value pairs.
2031 Thus q_attr_end is the number of attrs in q_pairs and
2032 q_match_begin - q_match_end is the number of matches in q_pairs. */
2033 CFIndex q_match_begin
;
2034 CFIndex q_match_end
;
2038 ReturnTypeMask q_return_type
;
2042 sqlite_int64 q_row_id
;
2044 CFArrayRef q_use_item_list
;
2045 #if defined(MULTIPLE_KEYCHAINS)
2046 CFArrayRef q_use_keychain
;
2047 CFArrayRef q_use_keychain_list
;
2048 #endif /* !defined(MULTIPLE_KEYCHAINS) */
2050 /* Value of kSecMatchLimit key if present. */
2053 /* Keybag handle to use for this item. */
2054 keybag_handle_t q_keybag
;
2055 keyclass_t q_keyclass
;
2056 //CFStringRef q_keyclass_s;
2061 /* Inline accessors to attr and match values in a query. */
2062 static inline CFIndex
query_attr_count(const Query
*q
)
2064 return q
->q_attr_end
;
2067 static inline Pair
query_attr_at(const Query
*q
, CFIndex ix
)
2069 return q
->q_pairs
[ix
];
2072 static inline CFIndex
query_match_count(const Query
*q
)
2074 return q
->q_match_end
- q
->q_match_begin
;
2077 static inline Pair
query_match_at(const Query
*q
, CFIndex ix
)
2079 return q
->q_pairs
[q
->q_match_begin
+ ix
];
2082 /* Private routines used to parse a query. */
2084 /* Sets q_keyclass based on value. */
2085 static void query_parse_keyclass(const void *value
, Query
*q
) {
2086 if (!isString(value
)) {
2087 q
->q_error
= errSecParam
;
2089 } else if (CFEqual(value
, kSecAttrAccessibleWhenUnlocked
)) {
2090 q
->q_keyclass
= key_class_ak
;
2091 } else if (CFEqual(value
, kSecAttrAccessibleAfterFirstUnlock
)) {
2092 q
->q_keyclass
= key_class_ck
;
2093 } else if (CFEqual(value
, kSecAttrAccessibleAlways
)) {
2094 q
->q_keyclass
= key_class_dk
;
2095 } else if (CFEqual(value
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
)) {
2096 q
->q_keyclass
= key_class_aku
;
2097 } else if (CFEqual(value
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
)) {
2098 q
->q_keyclass
= key_class_cku
;
2099 } else if (CFEqual(value
, kSecAttrAccessibleAlwaysThisDeviceOnly
)) {
2100 q
->q_keyclass
= key_class_dku
;
2102 q
->q_error
= errSecParam
;
2105 //q->q_keyclass_s = value;
2108 static CFStringRef
copyString(CFTypeRef obj
) {
2109 CFTypeID tid
= CFGetTypeID(obj
);
2110 if (tid
== CFStringGetTypeID())
2111 return CFStringCreateCopy(0, obj
);
2112 else if (tid
== CFDataGetTypeID())
2113 return CFStringCreateFromExternalRepresentation(0, obj
, kCFStringEncodingUTF8
);
2118 static CFDataRef
copyData(CFTypeRef obj
) {
2119 CFTypeID tid
= CFGetTypeID(obj
);
2120 if (tid
== CFDataGetTypeID()) {
2121 return CFDataCreateCopy(0, obj
);
2122 } else if (tid
== CFStringGetTypeID()) {
2123 return CFStringCreateExternalRepresentation(0, obj
, kCFStringEncodingUTF8
, 0);
2124 } else if (tid
== CFNumberGetTypeID()) {
2126 CFNumberGetValue(obj
, kCFNumberSInt32Type
, &value
);
2127 return CFDataCreate(0, (const UInt8
*)&value
, sizeof(value
));
2133 static CFTypeRef
copyBlob(CFTypeRef obj
) {
2134 CFTypeID tid
= CFGetTypeID(obj
);
2135 if (tid
== CFDataGetTypeID()) {
2136 return CFDataCreateCopy(0, obj
);
2137 } else if (tid
== CFStringGetTypeID()) {
2138 return CFStringCreateCopy(0, obj
);
2139 } else if (tid
== CFNumberGetTypeID()) {
2147 static CFTypeRef
copyNumber(CFTypeRef obj
) {
2148 CFTypeID tid
= CFGetTypeID(obj
);
2149 if (tid
== CFNumberGetTypeID()) {
2152 } else if (tid
== CFBooleanGetTypeID()) {
2153 SInt32 value
= CFBooleanGetValue(obj
);
2154 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
2155 } else if (tid
== CFStringGetTypeID()) {
2156 SInt32 value
= CFStringGetIntValue(obj
);
2157 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), value
);
2158 /* If a string converted to an int isn't equal to the int printed as
2159 a string, return a CFStringRef instead. */
2160 if (!CFEqual(t
, obj
)) {
2162 return CFStringCreateCopy(0, obj
);
2165 return CFNumberCreate(0, kCFNumberSInt32Type
, &value
);
2170 static CFDateRef
copyDate(CFTypeRef obj
) {
2171 CFTypeID tid
= CFGetTypeID(obj
);
2172 if (tid
== CFDateGetTypeID()) {
2179 /* AUDIT[securityd](done):
2180 key (ok) is a caller provided, string or number of length 4.
2181 value (ok) is a caller provided, non NULL CFTypeRef.
2183 static void query_add_attribute(const void *key
, const void *value
, Query
*q
)
2185 const kc_attr_desc
*desc
;
2186 if (!(desc
= kc_attr_desc_with_key(q
->q_class
, key
, &q
->q_error
)))
2189 CFTypeRef attr
= NULL
;
2190 switch (desc
->kind
) {
2192 attr
= copyData(value
);
2195 attr
= copyBlob(value
);
2198 case kc_creation_date_attr
:
2199 case kc_modification_date_attr
:
2200 attr
= copyDate(value
);
2202 case kc_number_attr
:
2203 attr
= copyNumber(value
);
2205 case kc_string_attr
:
2206 attr
= copyString(value
);
2211 q
->q_error
= errSecItemInvalidValue
;
2215 /* Store plaintext attr data in q_item. */
2217 CFDictionarySetValue(q
->q_item
, desc
->name
, attr
);
2220 if (CFEqual(desc
->name
, kSecAttrAccessible
)) {
2221 query_parse_keyclass(attr
, q
);
2224 /* Convert attr to (sha1) digest if requested. */
2225 if (desc
->flags
& kc_digest_attr
) {
2226 CFDataRef data
= copyData(attr
);
2229 q
->q_error
= errSecInternal
;
2233 CFMutableDataRef digest
= CFDataCreateMutable(0, CC_SHA1_DIGEST_LENGTH
);
2234 CFDataSetLength(digest
, CC_SHA1_DIGEST_LENGTH
);
2235 /* 64 bits cast: worst case is we generate the wrong hash */
2236 assert((unsigned long)CFDataGetLength(data
)<UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
2237 CC_SHA1(CFDataGetBytePtr(data
), (CC_LONG
)CFDataGetLength(data
),
2238 CFDataGetMutableBytePtr(digest
));
2243 /* Record the new attr key, value in q_pairs. */
2244 q
->q_pairs
[q
->q_attr_end
].key
= desc
->name
;
2245 q
->q_pairs
[q
->q_attr_end
++].value
= attr
;
2248 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
2249 static void query_set_attribute(const void *key
, const void *value
, Query
*q
) {
2250 if (CFDictionaryContainsKey(q
->q_item
, key
)) {
2252 for (ix
= 0; ix
< q
->q_attr_end
; ++ix
) {
2253 if (CFEqual(key
, q
->q_pairs
[ix
].key
)) {
2254 CFReleaseSafe(q
->q_pairs
[ix
].value
);
2256 for (; ix
< q
->q_attr_end
; ++ix
) {
2257 q
->q_pairs
[ix
] = q
->q_pairs
[ix
+ 1];
2259 CFDictionaryRemoveValue(q
->q_item
, key
);
2264 query_add_attribute(key
, value
, q
);
2267 /* AUDIT[securityd](done):
2268 key (ok) is a caller provided, string starting with 'm'.
2269 value (ok) is a caller provided, non NULL CFTypeRef.
2271 static void query_add_match(const void *key
, const void *value
, Query
*q
)
2273 /* Record the match key, value in q_pairs. */
2274 --(q
->q_match_begin
);
2275 q
->q_pairs
[q
->q_match_begin
].key
= key
;
2276 q
->q_pairs
[q
->q_match_begin
].value
= value
;
2278 if (CFEqual(kSecMatchLimit
, key
)) {
2279 /* Figure out what the value for kSecMatchLimit is if specified. */
2280 if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
2281 if (!CFNumberGetValue(value
, kCFNumberCFIndexType
, &q
->q_limit
))
2282 q
->q_error
= errSecItemInvalidValue
;
2283 } else if (CFEqual(kSecMatchLimitAll
, value
)) {
2284 q
->q_limit
= kSecMatchUnlimited
;
2285 } else if (CFEqual(kSecMatchLimitOne
, value
)) {
2288 q
->q_error
= errSecItemInvalidValue
;
2293 static bool query_set_class(Query
*q
, CFStringRef c_name
, OSStatus
*error
) {
2295 if (c_name
&& CFGetTypeID(c_name
) == CFStringGetTypeID() &&
2296 (value
= CFDictionaryGetValue(gClasses
, c_name
)) &&
2297 (q
->q_class
== 0 || q
->q_class
== value
)) {
2302 if (error
&& !*error
)
2303 *error
= c_name
? errSecNoSuchClass
: errSecItemClassMissing
;
2308 static const kc_class
*query_get_class(CFDictionaryRef query
, OSStatus
*error
) {
2309 CFStringRef c_name
= NULL
;
2310 const void *value
= CFDictionaryGetValue(query
, kSecClass
);
2311 if (isString(value
)) {
2314 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
2315 if (isData(value
)) {
2316 CFDataRef pref
= value
;
2317 _SecItemParsePersistentRef(pref
, &c_name
, 0);
2321 if (c_name
&& (value
= CFDictionaryGetValue(gClasses
, c_name
))) {
2324 if (error
&& !*error
)
2325 *error
= c_name
? errSecNoSuchClass
: errSecItemClassMissing
;
2330 /* AUDIT[securityd](done):
2331 key (ok) is a caller provided, string starting with 'c'.
2332 value (ok) is a caller provided, non NULL CFTypeRef.
2334 static void query_add_class(const void *key
, const void *value
, Query
*q
)
2336 if (CFEqual(key
, kSecClass
)) {
2337 query_set_class(q
, value
, &q
->q_error
);
2339 q
->q_error
= errSecItemInvalidKey
;
2343 /* AUDIT[securityd](done):
2344 key (ok) is a caller provided, string starting with 'r'.
2345 value (ok) is a caller provided, non NULL CFTypeRef.
2347 static void query_add_return(const void *key
, const void *value
, Query
*q
)
2349 ReturnTypeMask mask
;
2350 if (CFGetTypeID(value
) != CFBooleanGetTypeID()) {
2351 q
->q_error
= errSecItemInvalidValue
;
2355 int set_it
= CFEqual(value
, kCFBooleanTrue
);
2357 if (CFEqual(key
, kSecReturnData
))
2358 mask
= kSecReturnDataMask
;
2359 else if (CFEqual(key
, kSecReturnAttributes
))
2360 mask
= kSecReturnAttributesMask
;
2361 else if (CFEqual(key
, kSecReturnRef
))
2362 mask
= kSecReturnRefMask
;
2363 else if (CFEqual(key
, kSecReturnPersistentRef
))
2364 mask
= kSecReturnPersistentRefMask
;
2366 q
->q_error
= errSecItemInvalidKey
;
2370 if ((q
->q_return_type
& mask
) && !set_it
) {
2371 /* Clear out this bit (it's set so xor with the mask will clear it). */
2372 q
->q_return_type
^= mask
;
2373 } else if (!(q
->q_return_type
& mask
) && set_it
) {
2375 q
->q_return_type
|= mask
;
2379 /* AUDIT[securityd](done):
2380 key (ok) is a caller provided, string starting with 'u'.
2381 value (ok since q_use_item_list is unused) is a caller provided, non
2384 static void query_add_use(const void *key
, const void *value
, Query
*q
)
2386 if (CFEqual(key
, kSecUseItemList
)) {
2387 /* TODO: Add sanity checking when we start using this. */
2388 q
->q_use_item_list
= value
;
2390 #if defined(MULTIPLE_KEYCHAINS)
2391 else if (CFEqual(key
, kSecUseKeychain
))
2392 q
->q_use_keychain
= value
;
2393 else if (CFEqual(key
, kSecUseKeychainList
))
2394 q
->q_use_keychain_list
= value
;
2395 #endif /* !defined(MULTIPLE_KEYCHAINS) */
2397 q
->q_error
= errSecItemInvalidKey
;
2402 static void query_set_data(const void *value
, Query
*q
) {
2403 if (!isData(value
)) {
2404 q
->q_error
= errSecItemInvalidValue
;
2408 CFDictionarySetValue(q
->q_item
, kSecValueData
, value
);
2412 /* AUDIT[securityd](done):
2413 key (ok) is a caller provided, string starting with 'u'.
2414 value (ok) is a caller provided, non NULL CFTypeRef.
2416 static void query_add_value(const void *key
, const void *value
, Query
*q
)
2418 if (CFEqual(key
, kSecValueData
)) {
2419 query_set_data(value
, q
);
2421 } else if (CFEqual(key
, kSecValueRef
)) {
2423 /* TODO: Add value type sanity checking. */
2425 } else if (CFEqual(key
, kSecValuePersistentRef
)) {
2427 if (_SecItemParsePersistentRef(value
, &c_name
, &q
->q_row_id
))
2428 query_set_class(q
, c_name
, &q
->q_error
);
2430 q
->q_error
= errSecItemInvalidValue
;
2432 q
->q_error
= errSecItemInvalidKey
;
2437 /* AUDIT[securityd](done):
2438 key (ok) is a caller provided, unchecked.
2439 value (ok) is a caller provided, unchecked.
2441 static void query_update_applier(const void *key
, const void *value
,
2444 Query
*q
= (Query
*)context
;
2445 /* If something went wrong there is no point processing any more args. */
2449 /* Make sure we have a string key. */
2450 if (!isString(key
)) {
2451 q
->q_error
= errSecItemInvalidKeyType
;
2456 q
->q_error
= errSecItemInvalidValue
;
2460 if (CFEqual(key
, kSecValueData
)) {
2461 query_set_data(value
, q
);
2463 /* Make sure we have a value. */
2465 q
->q_error
= errSecItemInvalidValue
;
2469 query_add_attribute(key
, value
, q
);
2473 /* AUDIT[securityd](done):
2474 key (ok) is a caller provided, unchecked.
2475 value (ok) is a caller provided, unchecked.
2477 static void query_applier(const void *key
, const void *value
, void *context
)
2479 Query
*q
= (Query
*)context
;
2480 /* If something went wrong there is no point processing any more args. */
2484 /* Make sure we have a key. */
2486 q
->q_error
= errSecItemInvalidKeyType
;
2490 /* Make sure we have a value. */
2492 q
->q_error
= errSecItemInvalidValue
;
2496 /* Figure out what type of key we are dealing with. */
2497 CFTypeID key_id
= CFGetTypeID(key
);
2498 if (key_id
== CFStringGetTypeID()) {
2499 CFIndex key_len
= CFStringGetLength(key
);
2500 /* String keys can be different things. The subtype is determined by:
2501 length 4 strings are all attributes. Otherwise the first char
2502 determines the type:
2503 c: class must be kSecClass
2504 m: match like kSecMatchPolicy
2505 r: return like kSecReturnData
2511 query_add_attribute(key
, value
, q
);
2512 } else if (key_len
> 1) {
2513 UniChar k_first_char
= CFStringGetCharacterAtIndex(key
, 0);
2514 switch (k_first_char
)
2516 case 'c': /* class */
2517 query_add_class(key
, value
, q
);
2519 case 'm': /* match */
2520 query_add_match(key
, value
, q
);
2522 case 'r': /* return */
2523 query_add_return(key
, value
, q
);
2526 query_add_use(key
, value
, q
);
2528 case 'v': /* value */
2529 query_add_value(key
, value
, q
);
2532 q
->q_error
= errSecItemInvalidKey
;
2536 q
->q_error
= errSecItemInvalidKey
;
2538 } else if (key_id
== CFNumberGetTypeID()) {
2539 /* Numeric keys are always (extended) attributes. */
2540 /* TODO: Why is this here? query_add_attribute() doesn't take numbers. */
2541 query_add_attribute(key
, value
, q
);
2543 /* We only support string and number type keys. */
2544 q
->q_error
= errSecItemInvalidKeyType
;
2548 static CFStringRef
query_infer_keyclass(Query
*q
, CFStringRef agrp
) {
2549 /* apsd and lockdown are always dku. */
2550 if (CFEqual(agrp
, CFSTR("com.apple.apsd"))
2551 || CFEqual(agrp
, CFSTR("lockdown-identities"))) {
2552 return kSecAttrAccessibleAlwaysThisDeviceOnly
;
2554 /* All other certs or in the apple agrp is dk. */
2555 if (q
->q_class
== &cert_class
) {
2556 /* third party certs are always dk. */
2557 return kSecAttrAccessibleAlways
;
2559 /* The rest defaults to ak. */
2560 return kSecAttrAccessibleWhenUnlocked
;
2563 static void query_ensure_keyclass(Query
*q
, CFStringRef agrp
) {
2564 if (q
->q_keyclass
== 0) {
2565 CFStringRef accessible
= query_infer_keyclass(q
, agrp
);
2566 query_add_attribute(kSecAttrAccessible
, accessible
, q
);
2570 static void query_destroy(Query
*q
, OSStatus
*status
)
2572 if (status
&& *status
== 0 && q
->q_error
)
2573 *status
= q
->q_error
;
2575 CFIndex ix
, attr_count
= query_attr_count(q
);
2576 for (ix
= 0; ix
< attr_count
; ++ix
) {
2577 CFReleaseSafe(query_attr_at(q
, ix
).value
);
2579 CFReleaseSafe(q
->q_item
);
2583 /* Allocate and initialize a Query object for query. */
2584 static Query
*query_create(const kc_class
*qclass
, CFDictionaryRef query
,
2588 if (error
&& !*error
)
2589 *error
= errSecItemClassMissing
;
2593 /* Number of pairs we need is the number of attributes in this class
2594 plus the number of keys in the dictionary, minus one for each key in
2595 the dictionary that is a regular attribute. */
2596 CFIndex key_count
= qclass
->n_attrs
;
2598 key_count
+= CFDictionaryGetCount(query
);
2600 for (ix
= 0; ix
< qclass
->n_attrs
; ++ix
) {
2601 if (CFDictionaryContainsKey(query
, qclass
->attrs
[ix
].name
))
2606 if (key_count
> QUERY_KEY_LIMIT
) {
2607 if (error
&& !*error
)
2608 *error
= errSecItemIllegalQuery
;
2612 Query
*q
= calloc(1, sizeof(Query
) + sizeof(Pair
) * key_count
);
2614 if (error
&& !*error
)
2615 *error
= errSecAllocate
;
2619 q
->q_class
= qclass
;
2620 q
->q_match_begin
= q
->q_match_end
= key_count
;
2625 /* Parse query for a Query object q. */
2626 static bool query_parse_with_applier(Query
*q
, CFDictionaryRef query
,
2627 CFDictionaryApplierFunction applier
,
2629 if (q
->q_item
== NULL
) {
2630 q
->q_item
= CFDictionaryCreateMutable(0, 0,
2631 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2633 CFDictionaryApplyFunction(query
, applier
, q
);
2635 if (error
&& !*error
)
2636 *error
= q
->q_error
;
2642 /* Parse query for a Query object q. */
2643 static bool query_parse(Query
*q
, CFDictionaryRef query
,
2645 return query_parse_with_applier(q
, query
, query_applier
, error
);
2648 /* Parse query for a Query object q. */
2649 static bool query_update_parse(Query
*q
, CFDictionaryRef update
,
2651 return query_parse_with_applier(q
, update
, query_update_applier
, error
);
2654 static Query
*query_create_with_limit(CFDictionaryRef query
, CFIndex limit
,
2657 q
= query_create(query_get_class(query
, error
), query
, error
);
2660 if (!query_parse(q
, query
, error
)) {
2661 query_destroy(q
, error
);
2668 /* Make sure all attributes that are marked as not_null have a value. If
2669 force_date is false, only set mdat and cdat if they aren't already set. */
2671 query_pre_add(Query
*q
, bool force_date
) {
2672 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
2674 for (ix
= 0; ix
< q
->q_class
->n_attrs
; ++ix
) {
2675 const kc_attr_desc
*desc
= &q
->q_class
->attrs
[ix
];
2676 if (desc
->kind
== kc_creation_date_attr
||
2677 desc
->kind
== kc_modification_date_attr
) {
2679 query_set_attribute(desc
->name
, now
, q
);
2680 } else if (!CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
2681 query_add_attribute(desc
->name
, now
, q
);
2683 } else if ((desc
->flags
& kc_constrain_not_null
) &&
2684 !CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
2685 CFTypeRef value
= NULL
;
2686 if (desc
->flags
& kc_constrain_default_0
) {
2687 if (desc
->kind
== kc_date_attr
)
2688 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
2691 value
= CFNumberCreate(0, kCFNumberSInt32Type
, &vzero
);
2693 } else if (desc
->flags
& kc_constrain_default_empty
) {
2694 if (desc
->kind
== kc_data_attr
)
2695 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
2702 /* Safe to use query_add_attribute here since the attr wasn't
2704 query_add_attribute(desc
->name
, value
, q
);
2712 /* Update modification_date if needed. */
2714 query_pre_update(Query
*q
) {
2716 for (ix
= 0; ix
< q
->q_class
->n_attrs
; ++ix
) {
2717 const kc_attr_desc
*desc
= &q
->q_class
->attrs
[ix
];
2718 if (desc
->kind
== kc_modification_date_attr
) {
2719 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
2720 query_set_attribute(desc
->name
, now
, q
);
2726 /* AUDIT[securityd](done):
2727 accessGroup (ok) is a caller provided, non NULL CFTypeRef.
2729 Return true iff accessGroup is allowable according to accessGroups.
2731 static bool accessGroupsAllows(CFArrayRef accessGroups
,
2732 CFStringRef accessGroup
) {
2733 /* NULL accessGroups is wildcard. */
2736 /* Make sure we have a string. */
2737 if (!isString(accessGroup
))
2740 /* Having the special accessGroup "*" allows access to all accessGroups. */
2741 CFRange range
= { 0, CFArrayGetCount(accessGroups
) };
2743 (CFArrayContainsValue(accessGroups
, range
, accessGroup
) ||
2744 CFArrayContainsValue(accessGroups
, range
, CFSTR("*"))))
2750 static bool itemInAccessGroup(CFDictionaryRef item
, CFArrayRef accessGroups
) {
2751 return accessGroupsAllows(accessGroups
,
2752 CFDictionaryGetValue(item
, kSecAttrAccessGroup
));
2755 static void s3dl_merge_into_dict(const void *key
, const void *value
, void *context
) {
2756 CFDictionarySetValue(context
, key
, value
);
2759 /* Return whatever the caller requested based on the value of q->q_return_type.
2760 keys and values must be 3 larger than attr_count in size to accomadate the
2761 optional data, class and persistant ref results. This is so we can use
2762 the CFDictionaryCreate() api here rather than appending to a
2763 mutable dictionary. */
2764 static CFTypeRef
handle_result(Query
*q
, CFMutableDictionaryRef item
,
2765 sqlite_int64 rowid
) {
2768 data
= CFDictionaryGetValue(item
, kSecValueData
);
2769 if (q
->q_return_type
== 0) {
2770 /* Caller isn't interested in any results at all. */
2772 } else if (q
->q_return_type
== kSecReturnDataMask
) {
2777 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
2779 } else if (q
->q_return_type
== kSecReturnPersistentRefMask
) {
2780 a_result
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
2782 /* We need to return more than one value. */
2783 if (q
->q_return_type
& kSecReturnRefMask
) {
2784 CFDictionarySetValue(item
, kSecClass
, q
->q_class
->name
);
2785 } else if ((q
->q_return_type
& kSecReturnAttributesMask
)) {
2786 if (!(q
->q_return_type
& kSecReturnDataMask
)) {
2787 CFDictionaryRemoveValue(item
, kSecValueData
);
2792 CFDictionaryRemoveAllValues(item
);
2793 if ((q
->q_return_type
& kSecReturnDataMask
) && data
) {
2794 CFDictionarySetValue(item
, kSecValueData
, data
);
2798 if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
2799 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
2800 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
2811 static CFStringRef
s3dl_insert_sql(Query
*q
) {
2812 /* We always have at least one attribute, the agrp. */
2813 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
2814 CFStringAppendFormat(sql
, NULL
, CFSTR("INSERT INTO %@(data"), q
->q_class
->name
);
2816 CFIndex ix
, attr_count
= query_attr_count(q
);
2817 for (ix
= 0; ix
< attr_count
; ++ix
) {
2818 CFStringAppendFormat(sql
, NULL
, CFSTR(",%@"),
2819 query_attr_at(q
, ix
).key
);
2822 CFStringAppendFormat(sql
, NULL
, CFSTR(",rowid"));
2825 CFStringAppend(sql
, CFSTR(")VALUES(?"));
2827 for (ix
= 0; ix
< attr_count
; ++ix
) {
2828 CFStringAppend(sql
, CFSTR(",?"));
2831 CFStringAppendFormat(sql
, NULL
, CFSTR(",%qd"), q
->q_row_id
);
2833 CFStringAppend(sql
, CFSTR(");"));
2837 static CFDataRef
s3dl_encode_item(keybag_handle_t keybag
, keyclass_t keyclass
,
2838 CFDictionaryRef item
, OSStatus
*error
) {
2839 /* Encode to be encrypted item. */
2840 CFDataRef plain
= CFPropertyListCreateData(0, item
,
2841 kCFPropertyListBinaryFormat_v1_0
, 0, 0);
2842 CFDataRef edata
= NULL
;
2844 int s3e
= ks_encrypt_data(keybag
, keyclass
, plain
, &edata
);
2846 asl_log(NULL
, NULL
, ASL_LEVEL_CRIT
,
2847 "ks_encrypt_data: failed: %d", s3e
);
2848 if (error
&& !*error
)
2853 if (error
&& !*error
)
2854 *error
= errSecAllocate
;
2859 /* Bind the parameters to the INSERT statement. */
2860 static int s3dl_insert_bind(Query
*q
, sqlite3_stmt
*stmt
) {
2861 int s3e
= SQLITE_OK
;
2864 CFDataRef edata
= s3dl_encode_item(q
->q_keybag
, q
->q_keyclass
, q
->q_item
, &error
);
2866 s3e
= sqlite3_bind_blob_wrapper(stmt
, param
, CFDataGetBytePtr(edata
),
2867 CFDataGetLength(edata
), SQLITE_TRANSIENT
);
2868 secdebug("bind", "bind_blob: %.*s: %d",
2869 CFDataGetLength(edata
), CFDataGetBytePtr(edata
), s3e
);
2876 CFIndex ix
, attr_count
= query_attr_count(q
);
2877 for (ix
= 0; s3e
== SQLITE_OK
&& ix
< attr_count
; ++ix
) {
2878 s3e
= kc_bind_paramter(stmt
, param
++, query_attr_at(q
, ix
).value
);
2884 /* AUDIT[securityd](done):
2885 attributes (ok) is a caller provided dictionary, only its cf type has
2889 s3dl_query_add(s3dl_db_thread
*dbt
, Query
*q
, CFTypeRef
*result
)
2893 if (query_match_count(q
) != 0)
2894 return errSecItemMatchUnsupported
;
2896 /* Add requires a class to be specified unless we are adding a ref. */
2897 if (q
->q_use_item_list
)
2898 return errSecUseItemListUnsupported
;
2900 /* Actual work here. */
2901 sqlite3
*s3h
= dbt
->s3_handle
;
2903 CFStringRef sql
= s3dl_insert_sql(q
);
2904 sqlite3_stmt
*stmt
= NULL
;
2905 s3e
= kc_prepare_statement(s3h
, sql
, &stmt
);
2908 if (s3e
== SQLITE_OK
)
2909 s3e
= s3dl_insert_bind(q
, stmt
);
2911 /* Now execute the INSERT statement (step). */
2912 if (s3e
== SQLITE_OK
) {
2913 s3e
= sqlite3_step(stmt
);
2914 if (s3e
== SQLITE_DONE
) {
2916 if (q
->q_return_type
) {
2917 *result
= handle_result(q
, q
->q_item
, sqlite3_last_insert_rowid(s3h
));
2919 } else if (s3e
== SQLITE_ERROR
) {
2920 secdebug("sql", "insert: %s", sqlite3_errmsg(s3h
));
2921 /* The object already existed. */
2922 s3e
= errSecDuplicateItem
;
2926 /* Free the stmt. */
2928 int s3e2
= sqlite3_finalize(stmt
);
2929 if (s3e2
!= SQLITE_OK
&& s3e
== SQLITE_OK
)
2933 return s3e
== SQLITE_OK
? 0 : osstatus_for_s3e(s3e
);
2936 typedef void (*s3dl_handle_row
)(sqlite3_stmt
*stmt
, void *context
);
2938 static CFMutableDictionaryRef
2939 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
,
2940 CFArrayRef accessGroups
, keyclass_t
*keyclass
) {
2941 CFMutableDictionaryRef item
= NULL
;
2942 CFErrorRef error
= NULL
;
2943 CFDataRef edata
= NULL
;
2944 CFDataRef plain
= NULL
;
2946 require_action(edata
= CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt
, col
),
2947 sqlite3_column_bytes(stmt
, col
),
2949 out
, q
->q_error
= errSecDecode
);
2951 /* Decrypt and decode the item and check the decoded attributes against the query. */
2953 require_noerr((q
->q_error
= ks_decrypt_data(q
->q_keybag
, keyclass
, edata
, &plain
, &version
)), out
);
2958 CFPropertyListFormat format
;
2959 item
= (CFMutableDictionaryRef
)CFPropertyListCreateWithData(0, plain
,
2960 kCFPropertyListMutableContainers
, &format
, &error
);
2962 secerror("decode failed: %@ [item: %@]", error
, plain
);
2963 q
->q_error
= (OSStatus
)CFErrorGetCode(error
); /* possibly truncated error codes: whatever */
2965 } else if (!isDictionary(item
)) {
2968 } else if (!itemInAccessGroup(item
, accessGroups
)) {
2969 secerror("items accessGroup %@ not in %@",
2970 CFDictionaryGetValue(item
, kSecAttrAccessGroup
),
2975 /* TODO: Validate keyclass attribute. */
2978 CFReleaseSafe(edata
);
2979 CFReleaseSafe(plain
);
2983 struct s3dl_query_ctx
{
2985 CFArrayRef accessGroups
;
2990 static void s3dl_query_row(sqlite3_stmt
*stmt
, void *context
) {
2991 struct s3dl_query_ctx
*c
= context
;
2994 CFMutableDictionaryRef item
= s3dl_item_from_col(stmt
, q
, 1,
2995 c
->accessGroups
, NULL
);
2999 if (q
->q_class
== &identity_class
) {
3000 // TODO: Use col 2 for key rowid and use both rowids in persistant ref.
3001 CFMutableDictionaryRef key
= s3dl_item_from_col(stmt
, q
, 3,
3002 c
->accessGroups
, NULL
);
3006 CFDataRef certData
= CFDictionaryGetValue(item
, kSecValueData
);
3008 CFDictionarySetValue(key
, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL
),
3010 CFDictionaryRemoveValue(item
, kSecValueData
);
3012 CFDictionaryApplyFunction(item
, s3dl_merge_into_dict
, key
);
3017 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
3018 CFTypeRef a_result
= handle_result(q
, item
, rowid
);
3020 if (a_result
== kCFNull
) {
3021 /* Caller wasn't interested in a result, but we still
3022 count this row as found. */
3023 } else if (q
->q_limit
== 1) {
3024 c
->result
= a_result
;
3026 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, a_result
);
3027 CFRelease(a_result
);
3036 struct s3dl_update_row_ctx
{
3037 struct s3dl_query_ctx qc
;
3039 sqlite3_stmt
*update_stmt
;
3043 static void s3dl_update_row(sqlite3_stmt
*stmt
, void *context
)
3045 struct s3dl_update_row_ctx
*c
= context
;
3048 int s3e
= SQLITE_OK
;
3049 CFDataRef edata
= NULL
;
3050 keyclass_t keyclass
;
3051 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
3052 CFMutableDictionaryRef item
;
3053 require(item
= s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
,
3056 /* Update modified attributes in item and reencrypt. */
3058 CFDictionaryApplyFunction(u
->q_item
, s3dl_merge_into_dict
, item
);
3059 if (u
->q_keyclass
) {
3060 keyclass
= u
->q_keyclass
;
3063 require(edata
= s3dl_encode_item(q
->q_keybag
, keyclass
, item
, &q
->q_error
),
3066 /* Bind rowid and data to UPDATE statement and step. */
3067 /* Skip over already bound attribute values being updated, since we don't
3068 change them for each item. */
3069 CFIndex count
= 1 + query_attr_count(u
);
3070 /* 64 bits cast: worst case is if you try to have more than 2^32 attributes, we will drop some */
3071 assert(count
< INT_MAX
); /* Debug check */
3072 int param
= (int)count
;
3073 s3e
= sqlite3_bind_blob_wrapper(c
->update_stmt
, param
++,
3074 CFDataGetBytePtr(edata
), CFDataGetLength(edata
),
3076 if (s3e
== SQLITE_OK
)
3077 s3e
= sqlite3_bind_int64(c
->update_stmt
, param
++, rowid
);
3079 /* Now execute the UPDATE statement (step). */
3080 if (s3e
== SQLITE_OK
) {
3081 s3e
= sqlite3_step(c
->update_stmt
);
3082 if (s3e
== SQLITE_DONE
) {
3085 secdebug("sql", "updated row: %llu", rowid
);
3086 } else if (s3e
== SQLITE_ERROR
) {
3087 /* sqlite3_reset() below will return the real error. */
3093 if (s3e
) q
->q_error
= osstatus_for_s3e(s3e
);
3094 s3e
= sqlite3_reset(c
->update_stmt
); /* Reset state, but not bindings. */
3095 if (s3e
&& !q
->q_error
) q
->q_error
= osstatus_for_s3e(s3e
);
3096 if (q
->q_error
) { secdebug("sql", "update failed: %d", q
->q_error
); }
3098 CFReleaseSafe(item
);
3099 CFReleaseSafe(edata
);
3102 /* Append AND is needWhere is NULL or *needWhere is false. Append WHERE
3103 otherwise. Upon return *needWhere will be false. */
3105 sqlAppendWhereOrAnd(CFMutableStringRef sql
, bool *needWhere
) {
3106 if (!needWhere
|| !*needWhere
) {
3107 CFStringAppend(sql
, CFSTR(" AND "));
3109 CFStringAppend(sql
, CFSTR(" WHERE "));
3115 sqlAppendWhereBind(CFMutableStringRef sql
, CFStringRef col
, bool *needWhere
) {
3116 sqlAppendWhereOrAnd(sql
, needWhere
);
3117 CFStringAppend(sql
, col
);
3118 CFStringAppend(sql
, CFSTR("=?"));
3122 sqlAppendWhereROWID(CFMutableStringRef sql
,
3123 CFStringRef col
, sqlite_int64 row_id
,
3126 sqlAppendWhereOrAnd(sql
, needWhere
);
3127 CFStringAppendFormat(sql
, NULL
, CFSTR("%@=%lld"), col
, row_id
);
3132 sqlAppendWhereAttrs(CFMutableStringRef sql
, const Query
*q
, bool *needWhere
) {
3133 CFIndex ix
, attr_count
= query_attr_count(q
);
3134 for (ix
= 0; ix
< attr_count
; ++ix
) {
3135 sqlAppendWhereBind(sql
, query_attr_at(q
, ix
).key
, needWhere
);
3140 sqlAppendWhereAccessGroups(CFMutableStringRef sql
,
3142 CFArrayRef accessGroups
,
3144 CFIndex ix
, ag_count
;
3145 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
3149 sqlAppendWhereOrAnd(sql
, needWhere
);
3151 CFStringAppend(sql
, col
);
3152 CFStringAppend(sql
, CFSTR(" IN (?"));
3153 for (ix
= 1; ix
< ag_count
; ++ix
) {
3154 CFStringAppend(sql
, CFSTR(",?"));
3156 CFStringAppend(sql
, CFSTR(")"));
3158 CFStringAppendFormat(sql
, 0, CFSTR("(%@=?"), col
);
3159 for (ix
= 1; ix
< ag_count
; ++ix
) {
3160 CFStringAppendFormat(sql
, 0, CFSTR(" OR %@=?"), col
);
3162 CFStringAppend(sql
, CFSTR(")"));
3166 static void sqlAppendWhereClause(CFMutableStringRef sql
, const Query
*q
,
3167 CFArrayRef accessGroups
) {
3168 bool needWhere
= true;
3169 sqlAppendWhereROWID(sql
, CFSTR("ROWID"), q
->q_row_id
, &needWhere
);
3170 sqlAppendWhereAttrs(sql
, q
, &needWhere
);
3171 sqlAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
3174 static void sqlAppendLimit(CFMutableStringRef sql
, CFIndex limit
) {
3175 if (limit
!= kSecMatchUnlimited
)
3176 CFStringAppendFormat(sql
, NULL
, CFSTR(" LIMIT %d;"), limit
);
3178 CFStringAppend(sql
, CFSTR(";"));
3181 static CFStringRef
s3dl_select_sql(Query
*q
, int version
, CFArrayRef accessGroups
) {
3182 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
3183 if (q
->q_class
== &identity_class
) {
3184 CFStringAppendFormat(sql
, NULL
, CFSTR("SELECT crowid, "
3185 CERTIFICATE_DATA_COLUMN_LABEL
", rowid, data FROM "
3186 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
3187 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
3188 " keys.*, cert.data AS " CERTIFICATE_DATA_COLUMN_LABEL
3190 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"));
3191 sqlAppendWhereAccessGroups(sql
, CFSTR("cert.agrp"), accessGroups
, 0);
3192 /* The next 3 sqlAppendWhere calls are in the same order as in
3193 sqlAppendWhereClause(). This makes sqlBindWhereClause() work,
3194 as long as we do an extra sqlBindAccessGroups first. */
3195 sqlAppendWhereROWID(sql
, CFSTR("crowid"), q
->q_row_id
, 0);
3196 CFStringAppend(sql
, CFSTR(")"));
3197 bool needWhere
= true;
3198 sqlAppendWhereAttrs(sql
, q
, &needWhere
);
3199 sqlAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
3201 CFStringAppend(sql
, (version
< 5 ? CFSTR("SELECT * FROM ") :
3202 CFSTR("SELECT rowid, data FROM ")));
3203 CFStringAppend(sql
, q
->q_class
->name
);
3204 sqlAppendWhereClause(sql
, q
, accessGroups
);
3206 sqlAppendLimit(sql
, q
->q_limit
);
3211 static int sqlBindAccessGroups(sqlite3_stmt
*stmt
, CFArrayRef accessGroups
,
3213 int s3e
= SQLITE_OK
;
3214 int param
= *pParam
;
3215 CFIndex ix
, count
= accessGroups
? CFArrayGetCount(accessGroups
) : 0;
3216 for (ix
= 0; ix
< count
; ++ix
) {
3217 s3e
= kc_bind_paramter(stmt
, param
++,
3218 CFArrayGetValueAtIndex(accessGroups
, ix
));
3226 static int sqlBindWhereClause(sqlite3_stmt
*stmt
, const Query
*q
,
3227 CFArrayRef accessGroups
, int *pParam
) {
3228 int s3e
= SQLITE_OK
;
3229 int param
= *pParam
;
3230 CFIndex ix
, attr_count
= query_attr_count(q
);
3231 for (ix
= 0; ix
< attr_count
; ++ix
) {
3232 s3e
= kc_bind_paramter(stmt
, param
++, query_attr_at(q
, ix
).value
);
3237 /* Bind the access group to the sql. */
3238 if (s3e
== SQLITE_OK
) {
3239 s3e
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
);
3247 s3dl_query(s3dl_db_thread
*dbt
, s3dl_handle_row handle_row
, int version
,
3250 struct s3dl_query_ctx
*c
= context
;
3252 CFArrayRef accessGroups
= c
->accessGroups
;
3255 /* Sanity check the query. */
3257 return errSecValueRefUnsupported
;
3258 if (q
->q_row_id
&& query_attr_count(q
))
3259 return errSecItemIllegalQuery
;
3261 /* Actual work here. */
3262 sqlite3
*s3h
= dbt
->s3_handle
;
3264 CFStringRef sql
= s3dl_select_sql(q
, version
, accessGroups
);
3265 sqlite3_stmt
*stmt
= NULL
;
3266 s3e
= kc_prepare_statement(s3h
, sql
, &stmt
);
3269 /* Bind the values being searched for to the SELECT statement. */
3270 if (s3e
== SQLITE_OK
) {
3272 if (q
->q_class
== &identity_class
) {
3273 /* Bind the access groups to cert.agrp. */
3274 s3e
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
);
3276 if (s3e
== SQLITE_OK
)
3277 s3e
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
);
3280 /* Now execute the SELECT statement (step). */
3281 if (q
->q_limit
== 1) {
3284 c
->result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3286 while (s3e
== SQLITE_OK
&&
3287 (q
->q_limit
== kSecMatchUnlimited
|| c
->found
< q
->q_limit
)) {
3288 s3e
= sqlite3_step(stmt
);
3289 if (s3e
== SQLITE_ROW
) {
3290 handle_row(stmt
, context
);
3291 /* Extract the error returned by handle_row. */
3293 if (s3e
== errSecDecode
) {
3294 secerror("Ignoring undecryptable %@ item: ", q
->q_class
->name
);
3295 /* Ignore decode errors, since that means the item in question
3296 is unreadable forever, this allows export to skip these
3297 items and a backup/restore cycle to filter broken items
3299 Ideally we should mark the current rowid for removal since
3300 it's corrupt. The tricky bit is at this level we no longer
3301 know if the key or the cert failed to decode when dealing
3305 } else if (s3e
== SQLITE_DONE
) {
3306 if (c
->found
== 0) {
3307 /* We ran out of rows and didn't get any matches yet. */
3308 s3e
= errSecItemNotFound
;
3310 /* We got at least one match, so set the error status to ok. */
3314 } else if (s3e
!= SQLITE_OK
) {
3315 secdebug("sql", "select: %d: %s", s3e
, sqlite3_errmsg(s3h
));
3319 /* Free the stmt. */
3321 int s3e2
= sqlite3_finalize(stmt
);
3322 if (s3e2
!= SQLITE_OK
&& s3e
== SQLITE_OK
)
3327 if (s3e
) status
= osstatus_for_s3e(s3e
);
3328 else if (q
->q_error
) status
= q
->q_error
;
3331 CFReleaseNull(c
->result
);
3336 s3dl_copy_matching(s3dl_db_thread
*dbt
, Query
*q
, CFTypeRef
*result
,
3337 CFArrayRef accessGroups
)
3339 struct s3dl_query_ctx ctx
= {
3340 .q
= q
, .accessGroups
= accessGroups
,
3342 OSStatus status
= s3dl_query(dbt
, s3dl_query_row
, CURRENT_DB_VERSION
, &ctx
);
3344 *result
= ctx
.result
;
3346 CFReleaseSafe(ctx
.result
);
3350 static CFStringRef
s3dl_update_sql(Query
*q
) {
3351 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
3352 CFStringAppendFormat(sql
, NULL
, CFSTR("UPDATE %@ SET"), q
->q_class
->name
);
3354 CFIndex ix
, attr_count
= query_attr_count(q
);
3355 for (ix
= 0; ix
< attr_count
; ++ix
) {
3356 CFStringAppendFormat(sql
, NULL
, CFSTR(" %@=?,"),
3357 query_attr_at(q
, ix
).key
);
3359 CFStringAppend(sql
, CFSTR(" data=? WHERE ROWID=?;"));
3364 /* Bind the parameters to the UPDATE statement; data=? and ROWID=? are left
3365 unbound, since they are bound in s3dl_update_row when the update statement
3367 static int s3dl_update_bind(Query
*q
, sqlite3_stmt
*stmt
) {
3368 /* Bind the values being updated to the UPDATE statement. */
3369 int s3e
= SQLITE_OK
;
3371 CFIndex ix
, attr_count
= query_attr_count(q
);
3372 for (ix
= 0; s3e
== SQLITE_OK
&& ix
< attr_count
; ++ix
) {
3373 s3e
= kc_bind_paramter(stmt
, param
++, query_attr_at(q
, ix
).value
);
3378 /* AUDIT[securityd](done):
3379 attributesToUpdate (ok) is a caller provided dictionary,
3380 only its cf types have been checked.
3383 s3dl_query_update(s3dl_db_thread
*dbt
, Query
*q
,
3384 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
)
3386 /* Sanity check the query. */
3387 if (query_match_count(q
) != 0)
3388 return errSecItemMatchUnsupported
;
3390 return errSecValueRefUnsupported
;
3391 if (q
->q_row_id
&& query_attr_count(q
))
3392 return errSecItemIllegalQuery
;
3394 int s3e
= SQLITE_OK
;
3396 Query
*u
= query_create(q
->q_class
, attributesToUpdate
, &q
->q_error
);
3397 if (u
== NULL
) return q
->q_error
;
3398 if (!query_update_parse(u
, attributesToUpdate
, &q
->q_error
))
3400 query_pre_update(u
);
3402 /* Actual work here. */
3403 sqlite3
*s3h
= dbt
->s3_handle
;
3405 CFStringRef sql
= s3dl_update_sql(u
);
3406 sqlite3_stmt
*stmt
= NULL
;
3407 s3e
= kc_prepare_statement(s3h
, sql
, &stmt
);
3410 if (s3e
== SQLITE_OK
)
3411 s3e
= s3dl_update_bind(u
, stmt
);
3413 if (s3e
== SQLITE_OK
) {
3414 s3e
= s3dl_begin_transaction(dbt
);
3415 q
->q_return_type
= 0;
3416 struct s3dl_update_row_ctx ctx
= {
3418 .q
= q
, .accessGroups
= accessGroups
,
3423 u
->q_error
= s3dl_query(dbt
, s3dl_update_row
, CURRENT_DB_VERSION
, &ctx
);
3424 s3e
= s3dl_end_transaction(dbt
, s3e
);
3427 /* Free the stmt. */
3429 int s3e2
= sqlite3_finalize(stmt
);
3430 if (s3e2
!= SQLITE_OK
&& s3e
== SQLITE_OK
)
3435 query_destroy(u
, &q
->q_error
);
3437 if (s3e
) return osstatus_for_s3e(s3e
);
3438 if (q
->q_error
) return q
->q_error
;
3443 s3dl_query_delete(s3dl_db_thread
*dbt
, Query
*q
, CFArrayRef accessGroups
)
3445 sqlite3
*s3h
= dbt
->s3_handle
;
3446 sqlite3_stmt
*stmt
= NULL
;
3447 CFMutableStringRef sql
;
3450 sql
= CFStringCreateMutable(NULL
, 0);
3451 CFStringAppendFormat(sql
, NULL
, CFSTR("DELETE FROM %@"), q
->q_class
->name
);
3452 sqlAppendWhereClause(sql
, q
, accessGroups
);
3453 CFStringAppend(sql
, CFSTR(";"));
3454 s3e
= kc_prepare_statement(s3h
, sql
, &stmt
);
3457 /* Bind the parameters to the DELETE statement. */
3458 if (s3e
== SQLITE_OK
) {
3460 s3e
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
);
3463 /* Now execute the DELETE statement (step). */
3464 if (s3e
== SQLITE_OK
) {
3465 s3e
= sqlite3_step(stmt
);
3466 if (s3e
== SQLITE_DONE
) {
3467 int changes
= sqlite3_changes(s3h
);
3468 /* When doing a delete without a where clause sqlite reports 0
3469 changes since it drops and recreates the table rather than
3470 deleting all the records in it. */
3471 if (changes
== 0 && query_attr_count(q
) > 0) {
3472 s3e
= errSecItemNotFound
;
3475 secdebug("sql", "deleted: %d records", changes
);
3477 } else if (s3e
!= SQLITE_OK
) {
3478 secdebug("sql", "delete: %d: %s", s3e
, sqlite3_errmsg(s3h
));
3482 /* Free the stmt. */
3484 int s3e2
= sqlite3_finalize(stmt
);
3485 if (s3e2
!= SQLITE_OK
&& s3e
== SQLITE_OK
)
3492 /* Return true iff the item in question should not be backed up, nor restored,
3493 but when restoring a backup the original version of the item should be
3494 added back to the keychain again after the restore completes. */
3495 static bool SecItemIsSystemBound(CFDictionaryRef item
, const kc_class
*class) {
3496 CFStringRef agrp
= CFDictionaryGetValue(item
, kSecAttrAccessGroup
);
3497 if (!isString(agrp
))
3500 if (CFEqual(agrp
, CFSTR("lockdown-identities"))) {
3501 secdebug("backup", "found sys_bound item: %@", item
);
3505 if (CFEqual(agrp
, CFSTR("apple")) && class == &genp_class
) {
3506 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
3507 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
3508 if (isString(service
) && isString(account
) &&
3509 CFEqual(service
, CFSTR("com.apple.managedconfiguration")) &&
3510 (CFEqual(account
, CFSTR("Public")) ||
3511 CFEqual(account
, CFSTR("Private")))) {
3512 secdebug("backup", "found sys_bound item: %@", item
);
3516 secdebug("backup", "found non sys_bound item: %@", item
);
3520 /* Delete all items from the current keychain. If this is not an in
3521 place upgrade we don't delete items in the 'lockdown-identities'
3522 access group, this ensures that an import or restore of a backup
3523 will never overwrite an existing activation record. */
3524 static OSStatus
SecServerDeleteAll(s3dl_db_thread
*dbt
) {
3525 int s3e
= sqlite3_exec(dbt
->s3_handle
,
3529 "DELETE FROM keys;",
3531 return s3e
== SQLITE_OK
? 0 : osstatus_for_s3e(s3e
);
3534 struct s3dl_export_row_ctx
{
3535 struct s3dl_query_ctx qc
;
3536 keybag_handle_t dest_keybag
;
3537 enum SecItemFilter filter
;
3539 s3dl_db_thread
*dbt
;
3542 /* Return NULL if the current row isn't a match. Return kCFNull if the
3543 current row is a match and q->q_return_type == 0. Otherwise return
3544 whatever the caller requested based on the value of q->q_return_type. */
3545 static CFMutableDictionaryRef
3546 s3dl_item_from_pre_v5(sqlite3_stmt
*stmt
, Query
*q
, s3dl_db_thread
*dbt
,
3547 keyclass_t
*keyclass
, sqlite_int64
*rowid
) {
3548 int cix
= 0, cc
= sqlite3_column_count(stmt
);
3550 CFMutableDictionaryRef item
= CFDictionaryCreateMutable(0, 0,
3551 &kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
3552 for (cix
= 0; cix
< cc
; ++cix
) {
3553 const char *cname
= sqlite3_column_name(stmt
, cix
);
3554 CFStringRef key
= NULL
;
3555 CFTypeRef value
= NULL
;
3556 int ctype
= sqlite3_column_type(stmt
, cix
);
3558 case SQLITE_INTEGER
:
3560 sqlite_int64 i64Value
= sqlite3_column_int64(stmt
, cix
);
3561 if (!strcmp(cname
, "rowid")) {
3564 } else if (i64Value
> INT_MAX
|| i64Value
< INT_MIN
) {
3565 value
= CFNumberCreate(0, kCFNumberLongLongType
, &i64Value
);
3567 int iValue
= (int)i64Value
;
3568 value
= CFNumberCreate(0, kCFNumberIntType
, &iValue
);
3573 value
= CFDateCreate(0, sqlite3_column_double(stmt
, cix
));
3576 value
= CFStringCreateWithCString(0,
3577 (const char *)sqlite3_column_text(stmt
, cix
),
3578 kCFStringEncodingUTF8
);
3581 value
= CFDataCreate(0, sqlite3_column_blob(stmt
, cix
),
3582 sqlite3_column_bytes(stmt
, cix
));
3583 if (value
&& !strcmp(cname
, "data")) {
3585 if (q
->q_keybag
== KEYBAG_LEGACY
) {
3586 q
->q_error
= kc_decrypt_data(dbt
, value
, &plain
);
3588 q
->q_error
= ks_decrypt_data(q
->q_keybag
, keyclass
,
3593 secerror("failed to decrypt data: %d", q
->q_error
);
3597 key
= kSecValueData
;
3601 secwarning("Unsupported column type: %d", ctype
);
3604 /* Don't return NULL valued attributes to the caller. */
3612 CFDictionarySetValue(item
, key
, value
);
3614 key
= CFStringCreateWithCString(0, cname
, kCFStringEncodingUTF8
);
3616 CFDictionarySetValue(item
, key
, value
);
3625 CFReleaseSafe(item
);
3629 static void s3dl_export_row(sqlite3_stmt
*stmt
, void *context
) {
3630 struct s3dl_export_row_ctx
*c
= context
;
3632 keyclass_t keyclass
= 0;
3634 CFMutableDictionaryRef item
;
3635 sqlite_int64 rowid
= -1;
3636 if (c
->version
< 5) {
3637 item
= s3dl_item_from_pre_v5(stmt
, q
, c
->dbt
, &keyclass
, &rowid
);
3639 item
= s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
, &keyclass
);
3640 rowid
= sqlite3_column_int64(stmt
, 0);
3644 /* Only export sysbound items is do_sys_bound is true, only export non sysbound items otherwise. */
3645 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
3646 if (c
->filter
== kSecNoItemFilter
||
3647 SecItemIsSystemBound(item
, q
->q_class
) == do_sys_bound
) {
3648 /* Re-encode the item. */
3649 secdebug("item", "export rowid %llu item: %@", rowid
, item
);
3650 /* The code below could be moved into handle_row. */
3651 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
3653 if (c
->dest_keybag
!= KEYBAG_NONE
) {
3654 /* Encode and encrypt the item to the specified keybag. */
3655 CFDataRef plain
= CFPropertyListCreateData(0, item
, kCFPropertyListBinaryFormat_v1_0
, 0, 0);
3656 CFDictionaryRemoveAllValues(item
);
3658 CFDataRef edata
= NULL
;
3659 int s3e
= ks_encrypt_data(c
->dest_keybag
, keyclass
, plain
, &edata
);
3661 q
->q_error
= osstatus_for_s3e(s3e
);
3663 CFDictionarySetValue(item
, kSecValueData
, edata
);
3669 if (CFDictionaryGetCount(item
)) {
3670 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
3671 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, item
);
3681 static CFDictionaryRef
SecServerExportKeychainPlist(s3dl_db_thread
*dbt
,
3682 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
3683 enum SecItemFilter filter
, int version
, OSStatus
*error
) {
3684 CFMutableDictionaryRef keychain
;
3685 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3686 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
3688 if (error
&& !*error
)
3689 *error
= errSecAllocate
;
3693 Query q
= { .q_keybag
= src_keybag
};
3694 q
.q_return_type
= kSecReturnDataMask
| kSecReturnAttributesMask
| \
3695 kSecReturnPersistentRefMask
;
3696 q
.q_limit
= kSecMatchUnlimited
;
3698 /* Get rid of this duplicate. */
3699 const kc_class
*kc_classes
[] = {
3706 for (class_ix
= 0; class_ix
< sizeof(kc_classes
) / sizeof(*kc_classes
);
3708 q
.q_class
= kc_classes
[class_ix
];
3709 struct s3dl_export_row_ctx ctx
= {
3711 .dest_keybag
= dest_keybag
, .filter
= filter
, .version
= version
,
3714 ctx
.qc
.result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
3715 &kCFTypeArrayCallBacks
);
3716 if (ctx
.qc
.result
) {
3717 OSStatus status
= s3dl_query(dbt
, s3dl_export_row
, version
, &ctx
);
3718 if (status
== noErr
) {
3719 if (CFArrayGetCount(ctx
.qc
.result
))
3720 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
3721 CFRelease(ctx
.qc
.result
);
3722 } else if (status
!= errSecItemNotFound
) {
3723 if (error
&& !*error
)
3725 if (status
== errSecInteractionNotAllowed
) {
3726 secerror("Device locked during attempted keychain upgrade");
3727 CFReleaseNull(keychain
);
3737 static OSStatus
SecServerExportKeychain(s3dl_db_thread
*dbt
,
3738 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
3739 CFDataRef
*data_out
) {
3740 OSStatus status
= noErr
;
3741 /* Export everything except the items for which SecItemIsSystemBound()
3743 CFDictionaryRef keychain
= SecServerExportKeychainPlist(dbt
,
3744 src_keybag
, dest_keybag
, kSecBackupableItemFilter
, CURRENT_DB_VERSION
,
3747 CFErrorRef error
= NULL
;
3748 *data_out
= CFPropertyListCreateData(kCFAllocatorDefault
, keychain
,
3749 kCFPropertyListBinaryFormat_v1_0
,
3751 CFRelease(keychain
);
3753 secerror("Error encoding keychain: %@", error
);
3754 status
= (OSStatus
)CFErrorGetCode(error
); /* possibly truncated error code, whatever */
3762 struct SecServerImportClassState
{
3763 s3dl_db_thread
*dbt
;
3765 keybag_handle_t src_keybag
;
3766 keybag_handle_t dest_keybag
;
3767 enum SecItemFilter filter
;
3770 struct SecServerImportItemState
{
3771 const kc_class
*class;
3772 struct SecServerImportClassState
*s
;
3775 /* Infer a keyclass for 3.x items being imported from a backup. Return NULL
3776 to leave keyclass unchanged. */
3777 static void SecItemImportInferAccessible(Query
*q
) {
3778 CFStringRef agrp
= CFDictionaryGetValue(q
->q_item
, kSecAttrAccessGroup
);
3779 CFStringRef accessible
= NULL
;
3780 if (isString(agrp
)) {
3781 if (CFEqual(agrp
, CFSTR("apple"))) {
3782 if (q
->q_class
== &cert_class
) {
3783 /* apple certs are always dk. */
3784 accessible
= kSecAttrAccessibleAlways
;
3785 } else if (q
->q_class
== &genp_class
) {
3786 CFStringRef svce
= CFDictionaryGetValue(q
->q_item
, kSecAttrService
);
3787 if (isString(svce
)) {
3788 if (CFEqual(svce
, CFSTR("iTools"))) {
3789 /* iTools password is dk for now. */
3790 accessible
= kSecAttrAccessibleAlways
;
3791 } else if (CFEqual(svce
, CFSTR("BackupAgent"))) {
3792 /* We assume that acct == BackupPassword use aku. */
3793 accessible
= kSecAttrAccessibleWhenUnlockedThisDeviceOnly
;
3794 } else if (CFEqual(svce
, CFSTR("MobileBluetooth"))) {
3795 /* MobileBlueTooh uses cku. */
3796 accessible
= kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
;
3799 } else if (q
->q_class
== &inet_class
) {
3800 CFStringRef ptcl
= CFDictionaryGetValue(q
->q_item
, kSecAttrProtocol
);
3801 if (isString(ptcl
)) {
3802 /* LDAP is never needed without UI so ak. */
3803 if (CFEqual(ptcl
, kSecAttrProtocolLDAP
) ||
3804 CFEqual(ptcl
, kSecAttrProtocolLDAPS
)) {
3805 accessible
= kSecAttrAccessibleWhenUnlocked
;
3809 if (accessible
== NULL
) {
3810 /* Everything not covered by a special case in the apple
3811 access group (including keys) ends up in class Ck. */
3812 accessible
= kSecAttrAccessibleAfterFirstUnlock
;
3814 } else if (CFEqual(agrp
, CFSTR("com.apple.apsd"))
3815 || CFEqual(agrp
, CFSTR("lockdown-identities"))) {
3816 /* apsd and lockdown are always dku. */
3817 accessible
= kSecAttrAccessibleAlwaysThisDeviceOnly
;
3820 if (accessible
== NULL
) {
3821 if (q
->q_class
== &cert_class
) {
3822 /* third party certs are always dk. */
3823 accessible
= kSecAttrAccessibleAlways
;
3825 /* The rest defaults to ak. */
3826 accessible
= kSecAttrAccessibleWhenUnlocked
;
3829 query_add_attribute(kSecAttrAccessible
, accessible
, q
);
3832 /* Infer accessibility and access group for pre-v2 (iOS4.x and earlier) items
3833 being imported from a backup. */
3834 static void SecItemImportMigrate(Query
*q
) {
3835 CFStringRef agrp
= CFDictionaryGetValue(q
->q_item
, kSecAttrAccessGroup
);
3836 CFStringRef accessible
= CFDictionaryGetValue(q
->q_item
, kSecAttrAccessible
);
3838 if (!isString(agrp
) || !isString(accessible
))
3840 if (q
->q_class
== &genp_class
&& CFEqual(accessible
, kSecAttrAccessibleAlways
)) {
3841 CFStringRef svce
= CFDictionaryGetValue(q
->q_item
, kSecAttrService
);
3842 if (!isString(svce
)) return;
3843 if (CFEqual(agrp
, CFSTR("apple"))) {
3844 if (CFEqual(svce
, CFSTR("AirPort"))) {
3845 query_set_attribute(kSecAttrAccessible
, kSecAttrAccessibleAfterFirstUnlock
, q
);
3846 } else if (CFEqual(svce
, CFSTR("com.apple.airplay.password"))) {
3847 query_set_attribute(kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, q
);
3848 } else if (CFEqual(svce
, CFSTR("YouTube"))) {
3849 query_set_attribute(kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, q
);
3850 query_set_attribute(kSecAttrAccessGroup
, CFSTR("com.apple.youtube.credentials"), q
);
3852 CFStringRef desc
= CFDictionaryGetValue(q
->q_item
, kSecAttrDescription
);
3853 if (!isString(desc
)) return;
3854 if (CFEqual(desc
, CFSTR("IPSec Shared Secret")) || CFEqual(desc
, CFSTR("PPP Password"))) {
3855 query_set_attribute(kSecAttrAccessible
, kSecAttrAccessibleAfterFirstUnlock
, q
);
3859 } else if (q
->q_class
== &inet_class
&& CFEqual(accessible
, kSecAttrAccessibleAlways
)) {
3860 if (CFEqual(agrp
, CFSTR("PrintKitAccessGroup"))) {
3861 query_set_attribute(kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, q
);
3862 } else if (CFEqual(agrp
, CFSTR("apple"))) {
3863 CFTypeRef ptcl
= CFDictionaryGetValue(q
->q_item
, kSecAttrProtocol
);
3864 bool is_proxy
= false;
3865 if (isNumber(ptcl
)) {
3867 CFNumberGetValue(ptcl
, kCFNumberSInt32Type
, &iptcl
);
3868 is_proxy
= (iptcl
== FOUR_CHAR_CODE('htpx') ||
3869 iptcl
== FOUR_CHAR_CODE('htsx') ||
3870 iptcl
== FOUR_CHAR_CODE('ftpx') ||
3871 iptcl
== FOUR_CHAR_CODE('rtsx') ||
3872 iptcl
== FOUR_CHAR_CODE('xpth') ||
3873 iptcl
== FOUR_CHAR_CODE('xsth') ||
3874 iptcl
== FOUR_CHAR_CODE('xptf') ||
3875 iptcl
== FOUR_CHAR_CODE('xstr'));
3876 } else if (isString(ptcl
)) {
3877 is_proxy
= (CFEqual(ptcl
, kSecAttrProtocolHTTPProxy
) ||
3878 CFEqual(ptcl
, kSecAttrProtocolHTTPSProxy
) ||
3879 CFEqual(ptcl
, kSecAttrProtocolRTSPProxy
) ||
3880 CFEqual(ptcl
, kSecAttrProtocolFTPProxy
));
3883 query_set_attribute(kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, q
);
3888 static void SecServerImportItem(const void *value
, void *context
) {
3889 struct SecServerImportItemState
*state
=
3890 (struct SecServerImportItemState
*)context
;
3891 if (state
->s
->status
)
3893 if (!isDictionary(value
)) {
3894 state
->s
->status
= errSecParam
;
3898 CFDictionaryRef item
= (CFDictionaryRef
)value
;
3900 /* We don't filter non sys_bound items during import since we know we
3901 will never have any in this case, we use the kSecSysBoundItemFilter
3902 to indicate that we don't preserve rowid's during import instead. */
3903 if (state
->s
->filter
== kSecBackupableItemFilter
&&
3904 SecItemIsSystemBound(item
, state
->class))
3907 Query
*q
= query_create(state
->class, item
, &state
->s
->status
);
3911 /* TODO: We'd like to use query_update_applier here instead of
3912 query_parse(), since only attrs, kSecValueData and kSecValuePersistentRef. */
3913 query_parse(q
, item
, &state
->s
->status
);
3915 CFDataRef pdata
= NULL
;
3916 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
3917 /* Not strictly needed if we are restoring from cleartext db that is
3918 version 5 or later, but currently that never happens. Also the
3919 migrate code should be specific enough that it's still safe even
3920 in that case, it's just slower. */
3921 SecItemImportMigrate(q
);
3924 keyclass_t keyclass
;
3925 /* Decrypt the data using state->s->dest_keybag. */
3927 q
->q_error
= ks_decrypt_data(state
->s
->src_keybag
,
3928 &keyclass
, q
->q_data
, &pdata
, &version
);
3930 /* keyclass attribute doesn't match decoded value, fail. */
3931 q
->q_error
= errSecDecode
;
3932 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
3933 "keyclass attribute %d doesn't match keyclass in blob %d",
3934 q
->q_keyclass
, keyclass
);
3937 /* Old V4 style keychain backup being imported. */
3938 /* Make sure the keyclass in the dictionary matched what we got
3939 back from decoding the data blob. */
3940 if (q
->q_keyclass
&& q
->q_keyclass
!= keyclass
) {
3941 q
->q_error
= errSecDecode
;
3942 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
3943 "keyclass attribute %d doesn't match keyclass in blob %d",
3944 q
->q_keyclass
, keyclass
);
3946 query_set_data(pdata
, q
);
3947 SecItemImportMigrate(q
);
3950 /* version 2 or later backup. */
3951 CFErrorRef error
= NULL
;
3952 CFPropertyListFormat format
;
3953 item
= CFPropertyListCreateWithData(0, pdata
, kCFPropertyListImmutable
, &format
, &error
);
3955 query_update_parse(q
, item
, &state
->s
->status
);
3956 secdebug("item", "importing status: %ld %@",
3957 state
->s
->status
, item
);
3960 secerror("failed to decode v%d item data: %@",
3968 if (q
->q_keyclass
== 0)
3969 SecItemImportInferAccessible(q
);
3972 state
->s
->status
= q
->q_error
;
3974 if (q
->q_keyclass
== 0) {
3975 state
->s
->status
= errSecParam
;
3977 if (state
->s
->filter
== kSecSysBoundItemFilter
) {
3978 /* We don't set the rowid of sys_bound items we reimport
3979 after an import since that might fail if an item in the
3980 restored keychain already used that rowid. */
3983 query_pre_add(q
, false);
3984 state
->s
->status
= s3dl_query_add(state
->s
->dbt
, q
, NULL
);
3987 CFReleaseSafe(pdata
);
3988 query_destroy(q
, &state
->s
->status
);
3990 /* Reset error if we had one, since we just skip the current item
3991 and continue importing what we can. */
3992 if (state
->s
->status
) {
3993 secerror("Failed to import %@ item %ld ignoring error",
3994 state
->class->name
, state
->s
->status
);
3995 state
->s
->status
= 0;
3999 static void SecServerImportClass(const void *key
, const void *value
,
4001 struct SecServerImportClassState
*state
=
4002 (struct SecServerImportClassState
*)context
;
4005 if (!isString(key
)) {
4006 state
->status
= errSecParam
;
4009 const void *desc
= CFDictionaryGetValue(gClasses
, key
);
4010 const kc_class
*class = desc
;
4011 if (!class || class == &identity_class
) {
4012 state
->status
= errSecParam
;
4015 struct SecServerImportItemState item_state
= {
4016 .class = class, .s
= state
4018 if (isArray(value
)) {
4019 CFArrayRef items
= (CFArrayRef
)value
;
4020 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
4021 SecServerImportItem
, &item_state
);
4023 CFDictionaryRef item
= (CFDictionaryRef
)value
;
4024 SecServerImportItem(item
, &item_state
);
4028 static OSStatus
SecServerImportKeychainInPlist(s3dl_db_thread
*dbt
,
4029 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
4030 CFDictionaryRef keychain
, enum SecItemFilter filter
) {
4031 OSStatus status
= errSecSuccess
;
4033 CFDictionaryRef sys_bound
= NULL
;
4034 if (filter
== kSecBackupableItemFilter
) {
4035 /* Grab a copy of all the items for which SecItemIsSystemBound()
4037 require(sys_bound
= SecServerExportKeychainPlist(dbt
, KEYBAG_DEVICE
,
4038 KEYBAG_NONE
, kSecSysBoundItemFilter
, CURRENT_DB_VERSION
,
4042 /* Delete everything in the keychain. */
4043 require_noerr(status
= SecServerDeleteAll(dbt
), errOut
);
4045 struct SecServerImportClassState state
= {
4047 .src_keybag
= src_keybag
,
4048 .dest_keybag
= dest_keybag
,
4051 /* Import the provided items, preserving rowids. */
4052 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
4055 state
.src_keybag
= KEYBAG_NONE
;
4056 /* Import the items we preserved with random rowids. */
4057 state
.filter
= kSecSysBoundItemFilter
;
4058 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
4059 CFRelease(sys_bound
);
4061 status
= state
.status
;
4067 static OSStatus
SecServerImportKeychain(s3dl_db_thread
*dbt
,
4068 keybag_handle_t src_keybag
,
4069 keybag_handle_t dest_keybag
, CFDataRef data
) {
4070 int s3e
= s3dl_begin_transaction(dbt
);
4071 OSStatus status
= errSecSuccess
;
4072 if (s3e
!= SQLITE_OK
) {
4073 status
= osstatus_for_s3e(s3e
);
4075 CFDictionaryRef keychain
;
4076 CFPropertyListFormat format
;
4077 CFErrorRef error
= NULL
;
4078 keychain
= CFPropertyListCreateWithData(kCFAllocatorDefault
, data
,
4079 kCFPropertyListImmutable
, &format
,
4082 if (isDictionary(keychain
)) {
4083 status
= SecServerImportKeychainInPlist(dbt
, src_keybag
,
4084 dest_keybag
, keychain
,
4085 kSecBackupableItemFilter
);
4087 status
= errSecParam
;
4089 CFRelease(keychain
);
4091 secerror("Error decoding keychain: %@", error
);
4092 status
= (OSStatus
)CFErrorGetCode(error
); /* possibly truncated error code, whatever */
4097 s3e
= s3dl_end_transaction(dbt
, status
);
4098 return status
? status
: osstatus_for_s3e(s3e
);
4102 SecServerMigrateKeychain(s3dl_db_thread
*dbt
,
4103 int32_t handle_in
, CFDataRef data_in
,
4104 int32_t *handle_out
, CFDataRef
*data_out
) {
4107 if (handle_in
== kSecMigrateKeychainImport
) {
4108 if (data_in
== NULL
) {
4111 /* Import data_in. */
4112 status
= SecServerImportKeychain(dbt
, KEYBAG_NONE
, KEYBAG_DEVICE
, data_in
);
4115 } else if (handle_in
== kSecMigrateKeychainExport
) {
4116 if (data_in
!= NULL
) {
4119 /* Export the keychain and return the result in data_out. */
4120 status
= SecServerExportKeychain(dbt
, KEYBAG_DEVICE
, KEYBAG_NONE
, data_out
);
4123 status
= errSecParam
;
4129 static keybag_handle_t
ks_open_keybag(CFDataRef keybag
, CFDataRef password
) {
4131 uint64_t outputs
[] = { KEYBAG_NONE
};
4132 uint32_t num_outputs
= sizeof(outputs
) / sizeof(*outputs
);
4133 IOReturn kernResult
;
4135 kernResult
= IOConnectCallMethod(keystore
,
4136 kAppleKeyStoreKeyBagCreateWithData
, NULL
, 0, CFDataGetBytePtr(keybag
),
4137 CFDataGetLength(keybag
), outputs
, &num_outputs
, NULL
, 0);
4139 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
4140 "kAppleKeyStoreKeyBagCreateWithData: %x", kernResult
);
4144 kernResult
= IOConnectCallMethod(keystore
, kAppleKeyStoreKeyBagUnlock
,
4145 outputs
, 1, CFDataGetBytePtr(password
), CFDataGetLength(password
),
4146 NULL
, 0, NULL
, NULL
);
4148 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
4149 "kAppleKeyStoreKeyBagCreateWithData: %x", kernResult
);
4153 return (keybag_handle_t
)outputs
[0];
4161 static void ks_close_keybag(keybag_handle_t keybag
) {
4163 uint64_t inputs
[] = { keybag
};
4164 IOReturn kernResult
= IOConnectCallMethod(keystore
,
4165 kAppleKeyStoreKeyBagRelease
, inputs
, 1, NULL
, 0, NULL
, NULL
, NULL
, 0);
4167 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
,
4168 "kAppleKeyStoreKeyBagRelease: %d: %x", keybag
, kernResult
);
4173 static OSStatus
SecServerKeychainBackup(s3dl_db_thread
*dbt
, CFDataRef keybag
,
4174 CFDataRef password
, CFDataRef
*backup
) {
4176 keybag_handle_t backup_keybag
= ks_open_keybag(keybag
, password
);
4177 /* Export from system keybag to backup keybag. */
4178 status
= SecServerExportKeychain(dbt
, KEYBAG_DEVICE
, backup_keybag
,
4180 ks_close_keybag(backup_keybag
);
4184 static OSStatus
SecServerKeychainRestore(s3dl_db_thread
*dbt
, CFDataRef backup
,
4185 CFDataRef keybag
, CFDataRef password
) {
4187 keybag_handle_t backup_keybag
= ks_open_keybag(keybag
, password
);
4188 /* Import from backup keybag to system keybag. */
4189 status
= SecServerImportKeychain(dbt
, backup_keybag
, KEYBAG_DEVICE
,
4191 ks_close_keybag(backup_keybag
);
4195 /* External SPI support code. */
4197 /* Pthread_once protecting the kc_dbhandle and the singleton kc_dbhandle. */
4198 static pthread_once_t kc_dbhandle_init_once
= PTHREAD_ONCE_INIT
;
4199 static db_handle kc_dbhandle
= NULL
;
4201 /* This function is called only once and should initialize kc_dbhandle. */
4202 static void kc_dbhandle_init(void)
4205 CFTypeRef kc_attributes
[] = {
4207 kSecAttrAccessGroup
,
4208 kSecAttrCreationDate
,
4209 kSecAttrModificationDate
,
4210 kSecAttrDescription
,
4215 kSecAttrIsInvisible
,
4220 kSecAttrSecurityDomain
,
4223 kSecAttrAuthenticationType
,
4228 kSecAttrSerialNumber
,
4229 kSecAttrSubjectKeyID
,
4230 kSecAttrPublicKeyHash
,
4231 kSecAttrCertificateType
,
4232 kSecAttrCertificateEncoding
,
4234 kSecAttrApplicationLabel
,
4235 kSecAttrIsPermanent
,
4236 kSecAttrApplicationTag
,
4238 kSecAttrKeySizeInBits
,
4239 kSecAttrEffectiveKeySize
,
4249 kSecAttrHasCustomIcon
,
4252 kSecAttrAFPServerSignature
,
4254 kSecAttrCRLEncoding
,
4257 kSecAttrIsModifiable
,
4260 kSecAttrIsSensitive
,
4261 kSecAttrWasAlwaysSensitive
,
4262 kSecAttrIsExtractable
,
4263 kSecAttrWasNeverExtractable
,
4264 kSecAttrCanSignRecover
,
4265 kSecAttrCanVerifyRecover
4268 CFTypeRef kc_class_names
[] = {
4269 kSecClassGenericPassword
,
4270 kSecClassInternetPassword
,
4271 kSecClassCertificate
,
4275 const void *kc_classes
[] = {
4284 gAttributes
= CFSetCreate(kCFAllocatorDefault
, kc_attributes
,
4285 sizeof(kc_attributes
) / sizeof(*kc_attributes
), &kCFTypeSetCallBacks
);
4287 gClasses
= CFDictionaryCreate(kCFAllocatorDefault
, kc_class_names
,
4289 sizeof(kc_classes
) / sizeof(*kc_classes
),
4290 &kCFTypeDictionaryKeyCallBacks
, 0);
4292 const char *kcRelPath
;
4294 bool use_hwaes
= hwaes_key_available();
4296 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "using hwaes key");
4297 kcRelPath
= "/Library/Keychains/keychain-2.db";
4299 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "unable to access hwaes key");
4300 kcRelPath
= "/Library/Keychains/keychain-2-debug.db";
4303 bool autocommit
= true;
4307 /* Added this block of code back to keep the tests happy for now. */
4308 const char *home
= getenv("HOME");
4309 char path
[PATH_MAX
];
4310 size_t homeLen
= strlen(home
);
4311 size_t kcRelPathLen
= strlen(kcRelPath
);
4312 if (homeLen
+ kcRelPathLen
> sizeof(path
))
4314 strlcpy(path
, home
, sizeof(path
));
4315 strlcat(path
, kcRelPath
, sizeof(path
));
4318 s3dl_create_db_handle(kcRelPath
, &kc_dbhandle
, NULL
/* dbt */, autocommit
,
4323 void kc_dbhandle_reset(void);
4324 void kc_dbhandle_reset(void)
4326 s3dl_close_db_handle(kc_dbhandle
);
4332 /* Return a per thread dbt handle for the keychain. If create is true create
4333 the database if it does not yet exist. If it is false, just return an
4334 error if it fails to auto-create. */
4335 static int kc_get_dbt(s3dl_db_thread
**dbt
, bool create
)
4337 return s3dl_get_dbt(kc_dbhandle
, dbt
);
4340 static int kc_release_dbt(s3dl_db_thread
*dbt
)
4343 s3dl_dbt_destructor(dbt
);
4344 pthread_setspecific(dbt
->db
->key
, NULL
);
4345 //int s3e = s3dl_close_db_handle(dbt->db);
4352 /****************************************************************************
4353 **************** Beginning of Externally Callable Interface ****************
4354 ****************************************************************************/
4357 /* AUDIT[securityd](done):
4358 query (ok) is a caller provided dictionary, only its cf type has been checked.
4361 _SecItemCopyMatching(CFDictionaryRef query
, CFTypeRef
*result
,
4362 CFArrayRef accessGroups
)
4364 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4366 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
)))
4367 return errSecMissingEntitlement
;
4369 /* Having the special accessGroup "*" allows access to all accessGroups. */
4370 if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*")))
4371 accessGroups
= NULL
;
4374 Query
*q
= query_create_with_limit(query
, 1, &error
);
4376 CFStringRef agrp
= CFDictionaryGetValue(q
->q_item
, kSecAttrAccessGroup
);
4377 if (agrp
&& accessGroupsAllows(accessGroups
, agrp
)) {
4378 const void *val
= agrp
;
4379 accessGroups
= CFArrayCreate(0, &val
, 1, &kCFTypeArrayCallBacks
);
4381 CFRetainSafe(accessGroups
);
4384 /* Sanity check the query. */
4385 if (q
->q_use_item_list
) {
4386 error
= errSecUseItemListUnsupported
;
4387 #if defined(MULTIPLE_KEYCHAINS)
4388 } else if (q
->q_use_keychain
) {
4389 error
= errSecUseKeychainUnsupported
;
4391 } else if (q
->q_return_type
!= 0 && result
== NULL
) {
4392 error
= errSecReturnMissingPointer
;
4393 } else if (!q
->q_error
) {
4394 s3dl_db_thread
*dbt
;
4395 int s3e
= s3dl_get_dbt(kc_dbhandle
, &dbt
);
4396 if (s3e
== SQLITE_OK
) {
4397 s3e
= s3dl_copy_matching(dbt
, q
, result
, accessGroups
);
4398 /* TODO: Check error of this function if s3e is noErr. */
4399 kc_release_dbt(dbt
);
4402 error
= osstatus_for_s3e(s3e
);
4405 CFReleaseSafe(accessGroups
);
4406 query_destroy(q
, &error
);
4412 /* AUDIT[securityd](done):
4413 attributes (ok) is a caller provided dictionary, only its cf type has
4417 _SecItemAdd(CFDictionaryRef attributes
, CFTypeRef
*result
,
4418 CFArrayRef accessGroups
)
4420 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4422 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
)))
4423 return errSecMissingEntitlement
;
4426 Query
*q
= query_create_with_limit(attributes
, 0, &error
);
4428 /* Access group sanity checking. */
4429 CFStringRef agrp
= (CFStringRef
)CFDictionaryGetValue(attributes
,
4430 kSecAttrAccessGroup
);
4432 CFArrayRef ag
= accessGroups
;
4433 /* Having the special accessGroup "*" allows access to all accessGroups. */
4434 if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*")))
4435 accessGroups
= NULL
;
4438 /* The user specified an explicit access group, validate it. */
4439 if (!accessGroupsAllows(accessGroups
, agrp
))
4440 return errSecNoAccessForItem
;
4442 agrp
= (CFStringRef
)CFArrayGetValueAtIndex(ag
, 0);
4444 /* We are using an implicit access group, add it as if the user
4445 specified it as an attribute. */
4446 query_add_attribute(kSecAttrAccessGroup
, agrp
, q
);
4449 query_ensure_keyclass(q
, agrp
);
4452 error
= errSecValuePersistentRefUnsupported
;
4453 #if defined(MULTIPLE_KEYCHAINS)
4454 else if (q
->q_use_keychain_list
)
4455 error
= errSecUseKeychainListUnsupported
;
4457 else if (!q
->q_error
) {
4458 s3dl_db_thread
*dbt
;
4459 int s3e
= s3dl_get_dbt(kc_dbhandle
, &dbt
);
4460 if (s3e
== SQLITE_OK
) {
4461 s3e
= s3dl_begin_transaction(dbt
);
4462 if (s3e
== SQLITE_OK
) {
4463 query_pre_add(q
, true);
4464 s3e
= s3dl_query_add(dbt
, q
, result
);
4466 s3e
= s3dl_end_transaction(dbt
, s3e
);
4469 /* TODO: Check error on this function if s3e is 0. */
4470 kc_release_dbt(dbt
);
4473 error
= osstatus_for_s3e(s3e
);
4475 query_destroy(q
, &error
);
4480 /* AUDIT[securityd](done):
4481 query (ok) and attributesToUpdate (ok) are a caller provided dictionaries,
4482 only their cf types have been checked.
4485 _SecItemUpdate(CFDictionaryRef query
,
4486 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
)
4488 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4490 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
)))
4491 return errSecMissingEntitlement
;
4493 /* Having the special accessGroup "*" allows access to all accessGroups. */
4494 if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*")))
4495 accessGroups
= NULL
;
4498 Query
*q
= query_create_with_limit(query
, kSecMatchUnlimited
, &error
);
4500 /* Sanity check the query. */
4501 if (q
->q_use_item_list
) {
4502 error
= errSecUseItemListUnsupported
;
4503 } else if (q
->q_return_type
& kSecReturnDataMask
) {
4504 /* Update doesn't return anything so don't ask for it. */
4505 error
= errSecReturnDataUnsupported
;
4506 } else if (q
->q_return_type
& kSecReturnAttributesMask
) {
4507 error
= errSecReturnAttributesUnsupported
;
4508 } else if (q
->q_return_type
& kSecReturnRefMask
) {
4509 error
= errSecReturnRefUnsupported
;
4511 /* Access group sanity checking. */
4512 CFStringRef agrp
= (CFStringRef
)CFDictionaryGetValue(attributesToUpdate
,
4513 kSecAttrAccessGroup
);
4515 /* The user is attempting to modify the access group column,
4516 validate it to make sure the new value is allowable. */
4517 if (!accessGroupsAllows(accessGroups
, agrp
))
4518 error
= errSecNoAccessForItem
;
4522 s3dl_db_thread
*dbt
;
4523 int s3e
= s3dl_get_dbt(kc_dbhandle
, &dbt
);
4524 if (s3e
== SQLITE_OK
) {
4525 s3e
= s3dl_query_update(dbt
, q
, attributesToUpdate
, accessGroups
);
4527 /* TODO: Check error on this function if s3e is 0. */
4528 kc_release_dbt(dbt
);
4532 error
= osstatus_for_s3e(s3e
);
4535 query_destroy(q
, &error
);
4540 /* AUDIT[securityd](done):
4541 query (ok) is a caller provided dictionary, only its cf type has been checked.
4544 _SecItemDelete(CFDictionaryRef query
, CFArrayRef accessGroups
)
4546 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4548 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
)))
4549 return errSecMissingEntitlement
;
4551 /* Having the special accessGroup "*" allows access to all accessGroups. */
4552 if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*")))
4553 accessGroups
= NULL
;
4556 Query
*q
= query_create_with_limit(query
, kSecMatchUnlimited
, &error
);
4558 /* Sanity check the query. */
4559 if (q
->q_limit
!= kSecMatchUnlimited
)
4560 error
= errSecMatchLimitUnsupported
;
4561 else if (query_match_count(q
) != 0)
4562 error
= errSecItemMatchUnsupported
;
4564 error
= errSecValueRefUnsupported
;
4565 else if (q
->q_row_id
&& query_attr_count(q
))
4566 error
= errSecItemIllegalQuery
;
4568 s3dl_db_thread
*dbt
;
4569 int s3e
= s3dl_get_dbt(kc_dbhandle
, &dbt
);
4570 if (s3e
== SQLITE_OK
) {
4571 s3e
= s3dl_query_delete(dbt
, q
, accessGroups
);
4573 /* TODO: Check error on this function if s3e is 0. */
4574 kc_release_dbt(dbt
);
4577 error
= osstatus_for_s3e(s3e
);
4579 query_destroy(q
, &error
);
4584 /* AUDIT[securityd](done):
4585 No caller provided inputs.
4588 _SecItemDeleteAll(void)
4590 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4591 static const char deleteAllSQL
[] = "BEGIN EXCLUSIVE TRANSACTION; "
4592 "DELETE from inet; DELETE from cert; DELETE from keys; DELETE from genp; "
4593 "COMMIT TRANSACTION; VACUUM;";
4595 s3dl_db_thread
*dbt
;
4596 int s3e
= kc_get_dbt(&dbt
, true);
4597 if (s3e
== SQLITE_OK
) {
4598 s3e
= sqlite3_exec(dbt
->s3_handle
, deleteAllSQL
, NULL
, NULL
, NULL
);
4599 kc_release_dbt(dbt
);
4601 return (s3e
== SQLITE_OK
);
4604 /* TODO: Move to a location shared between securityd and Security framework. */
4605 static const char *restore_keychain_location
= "/Library/Keychains/keychain.restoring";
4607 /* AUDIT[securityd](done):
4608 No caller provided inputs.
4611 _SecServerRestoreKeychain(void)
4613 static db_handle restore_dbhandle
= NULL
;
4614 s3dl_db_thread
*restore_dbt
= NULL
, *dbt
= NULL
;
4615 CFDataRef backup
= NULL
;
4616 OSStatus status
= errSecSuccess
;
4619 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4620 require_noerr(s3e
= s3dl_get_dbt(kc_dbhandle
, &dbt
), errOut
);
4622 /* Export everything from the keychain we are restoring, this upgrades it
4623 to whatever version is current first if needed. */
4624 bool use_hwaes
= hwaes_key_available();
4625 require_noerr(s3e
= s3dl_create_db_handle(restore_keychain_location
,
4626 &restore_dbhandle
, &restore_dbt
, true, false, use_hwaes
), errOut
);
4627 require_noerr(status
= SecServerExportKeychain(restore_dbt
, KEYBAG_DEVICE
,
4628 KEYBAG_NONE
, &backup
), errOut
);
4629 require_noerr(status
= SecServerImportKeychain(dbt
, KEYBAG_NONE
,
4630 KEYBAG_DEVICE
, backup
), errOut
);
4633 s3e
= s3dl_close_db_handle(restore_dbhandle
);
4634 CFReleaseSafe(backup
);
4635 kc_release_dbt(restore_dbt
);
4636 kc_release_dbt(dbt
);
4638 if (s3e
!= SQLITE_OK
)
4639 return osstatus_for_s3e(s3e
);
4643 /* AUDIT[securityd](done):
4644 args_in (ok) is a caller provided, CFArrayRef.
4647 _SecServerMigrateKeychain(CFArrayRef args_in
, CFTypeRef
*args_out
)
4649 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4650 CFMutableArrayRef args
= NULL
;
4651 CFNumberRef hin
= NULL
, hout
= NULL
;
4652 int32_t handle_in
, handle_out
= 0;
4653 CFDataRef data_in
, data_out
= NULL
;
4654 OSStatus status
= errSecParam
;
4655 CFIndex argc
= CFArrayGetCount(args_in
);
4657 s3dl_db_thread
*dbt
;
4658 int s3e
= s3dl_get_dbt(kc_dbhandle
, &dbt
);
4659 if (s3e
!= SQLITE_OK
)
4660 return osstatus_for_s3e(s3e
);
4662 require_quiet(argc
== 1 || argc
== 2, errOut
);
4663 hin
= (CFNumberRef
)CFArrayGetValueAtIndex(args_in
, 0);
4664 require_quiet(isNumberOfType(hin
, kCFNumberSInt32Type
), errOut
);
4665 require_quiet(CFNumberGetValue(hin
, kCFNumberSInt32Type
, &handle_in
), errOut
);
4667 data_in
= (CFDataRef
)CFArrayGetValueAtIndex(args_in
, 1);
4668 require_quiet(data_in
, errOut
);
4669 require_quiet(CFGetTypeID(data_in
) == CFDataGetTypeID(), errOut
);
4674 secdebug("migrate", "migrate: %d %d", handle_in
, data_in
);
4676 status
= SecServerMigrateKeychain(dbt
, handle_in
, data_in
, &handle_out
, &data_out
);
4678 require_quiet(args
= CFArrayCreateMutable(kCFAllocatorDefault
, 2, &kCFTypeArrayCallBacks
), errOut
);
4679 require_quiet(hout
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &handle_out
), errOut
);
4680 CFArrayAppendValue(args
, hout
);
4682 CFArrayAppendValue(args
, data_out
);
4687 kc_release_dbt(dbt
);
4688 CFReleaseSafe(args
);
4689 CFReleaseSafe(hout
);
4690 CFReleaseSafe(data_out
);
4695 _SecServerKeychainBackup(CFArrayRef args_in
, CFTypeRef
*args_out
) {
4696 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4697 OSStatus status
= errSecParam
;
4698 CFIndex argc
= args_in
? CFArrayGetCount(args_in
) : 0;
4699 CFDataRef backup
= NULL
;
4701 s3dl_db_thread
*dbt
;
4702 int s3e
= s3dl_get_dbt(kc_dbhandle
, &dbt
);
4703 if (s3e
!= SQLITE_OK
)
4704 return osstatus_for_s3e(s3e
);
4706 require_quiet(args_out
!= NULL
, errOut
);
4709 require_noerr_quiet(status
= SecServerExportKeychain(dbt
, KEYBAG_DEVICE
, backup_keybag_handle
, &backup
), errOut
);
4714 else if (argc
== 1 || argc
== 2) {
4715 CFDataRef keybag
= (CFDataRef
)CFArrayGetValueAtIndex(args_in
, 0);
4716 require_quiet(isData(keybag
), errOut
);
4719 password
= (CFDataRef
)CFArrayGetValueAtIndex(args_in
, 1);
4720 require_quiet(isData(password
), errOut
);
4724 require_noerr_quiet(status
= SecServerKeychainBackup(dbt
, keybag
, password
, &backup
), errOut
);
4729 kc_release_dbt(dbt
);
4734 _SecServerKeychainRestore(CFArrayRef args_in
, CFTypeRef
*dummy
) {
4735 pthread_once(&kc_dbhandle_init_once
, kc_dbhandle_init
);
4736 OSStatus status
= errSecParam
;
4737 CFIndex argc
= CFArrayGetCount(args_in
);
4739 s3dl_db_thread
*dbt
;
4740 int s3e
= s3dl_get_dbt(kc_dbhandle
, &dbt
);
4741 if (s3e
!= SQLITE_OK
)
4742 return osstatus_for_s3e(s3e
);
4744 require_quiet(argc
== 2 || argc
== 3, errOut
);
4745 CFDataRef backup
= (CFDataRef
)CFArrayGetValueAtIndex(args_in
, 0);
4746 require_quiet(isData(backup
), errOut
);
4747 CFDataRef keybag
= (CFDataRef
)CFArrayGetValueAtIndex(args_in
, 1);
4748 require_quiet(isData(keybag
), errOut
);
4751 password
= (CFDataRef
)CFArrayGetValueAtIndex(args_in
, 2);
4752 require_quiet(isData(password
), errOut
);
4757 status
= SecServerKeychainRestore(dbt
, backup
, keybag
, password
);
4764 status
= errSecSuccess
;
4766 kc_release_dbt(dbt
);