2 * Copyright (c) 2006-2013 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>
31 #include <securityd/SecDbItem.h>
33 #include <Security/SecItem.h>
34 #include <Security/SecItemPriv.h>
35 #include <Security/SecItemInternal.h>
36 #include <Security/SecKey.h>
37 #include <Security/SecKeyPriv.h>
38 #include <Security/SecCertificateInternal.h>
39 #include <Security/SecIdentity.h>
40 #include <Security/SecIdentityPriv.h>
41 #include <Security/SecFramework.h>
42 #include <Security/SecRandom.h>
43 #include <Security/SecBasePriv.h>
44 #include <utilities/SecIOFormat.h>
45 #include <utilities/SecCFWrappers.h>
46 #include <utilities/SecCFError.h>
47 #include <utilities/der_plist.h>
52 #include <sys/param.h>
54 #include <Security/SecBase.h>
55 #include <CoreFoundation/CFData.h>
56 #include <CoreFoundation/CFDate.h>
57 #include <CoreFoundation/CFArray.h>
58 #include <CoreFoundation/CFDictionary.h>
59 #include <CoreFoundation/CFNumber.h>
60 #include <CoreFoundation/CFString.h>
61 #include <CoreFoundation/CFURL.h>
62 #include <CommonCrypto/CommonDigest.h>
63 #include <CommonCrypto/CommonDigestSPI.h>
64 #include <CommonCrypto/CommonCryptor.h>
65 #include <CommonCrypto/CommonCryptorSPI.h>
66 #include <libkern/OSByteOrder.h>
67 #include <utilities/debugging.h>
69 #include <Security/SecInternal.h>
70 #include "securityd_client.h"
71 #include "utilities/sqlutils.h"
72 #include "utilities/SecIOFormat.h"
73 #include "utilities/SecFileLocations.h"
74 #include <utilities/iCloudKeychainTrace.h>
75 #include <AssertMacros.h>
78 #include <utilities/array_size.h>
79 #include <utilities/SecDb.h>
80 #include <securityd/SOSCloudCircleServer.h>
82 #include "OTATrustUtilities.h"
85 #include <IOKit/IOKitLib.h>
87 #if TARGET_OS_EMBEDDED
88 #include <MobileKeyBag/MobileKeyBag.h>
90 #endif /* USE_KEYSTORE */
93 /* g_keychain_handle is the keybag handle used for encrypting item in the keychain.
94 For testing purposes, it can be set to something other than the default, with SecItemServerSetKeychainKeybag */
96 #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED
97 static keybag_handle_t g_keychain_keybag
= session_keybag_handle
;
99 static keybag_handle_t g_keychain_keybag
= device_keybag_handle
;
101 #else /* !USE_KEYSTORE */
102 static int32_t g_keychain_keybag
= 0; /* 0 == device_keybag_handle, constant dictated by AKS */
103 #endif /* USE_KEYSTORE */
105 void SecItemServerSetKeychainKeybag(int32_t keybag
)
107 g_keychain_keybag
=keybag
;
110 void SecItemServerResetKeychainKeybag(void)
113 #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED
114 g_keychain_keybag
= session_keybag_handle
;
116 g_keychain_keybag
= device_keybag_handle
;
118 #else /* !USE_KEYSTORE */
119 g_keychain_keybag
= 0; /* 0 == device_keybag_handle, constant dictated by AKS */
120 #endif /* USE_KEYSTORE */
123 /* KEYBAG_NONE is private to security and have special meaning.
124 They should not collide with AppleKeyStore constants, but are only referenced
127 #define KEYBAG_NONE (-1) /* Set q_keybag to KEYBAG_NONE to obtain cleartext data. */
128 #define KEYBAG_DEVICE (g_keychain_keybag) /* actual keybag used to encrypt items */
130 /* Changed the name of the keychain changed notification, for testing */
131 static const char *g_keychain_changed_notification
= kSecServerKeychainChangedNotification
;
133 void SecItemServerSetKeychainChangedNotification(const char *notification_name
)
135 g_keychain_changed_notification
= notification_name
;
138 /* label when certificate data is joined with key data */
139 #define CERTIFICATE_DATA_COLUMN_LABEL "certdata"
141 #define CURRENT_DB_VERSION 6
145 /* Forward declaration of import export SPIs. */
148 kSecSysBoundItemFilter
,
149 kSecBackupableItemFilter
,
152 static CF_RETURNS_RETAINED CFDictionaryRef
SecServerExportKeychainPlist(SecDbConnectionRef dbt
,
153 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
154 enum SecItemFilter filter
, CFErrorRef
*error
);
155 static bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
,
156 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
157 CFDictionaryRef keychain
, enum SecItemFilter filter
, CFErrorRef
*error
);
161 static bool hwaes_key_available(void)
163 keybag_handle_t handle
= bad_keybag_handle
;
164 keybag_handle_t special_handle
= bad_keybag_handle
;
165 #if TARGET_OS_MAC && !TARGET_OS_EMBEDDED
166 special_handle
= session_keybag_handle
;
167 #elif TARGET_OS_EMBEDDED
168 special_handle
= device_keybag_handle
;
170 kern_return_t kr
= aks_get_system(special_handle
, &handle
);
171 if (kr
!= kIOReturnSuccess
) {
172 #if TARGET_OS_EMBEDDED
173 /* TODO: Remove this once the kext runs the daemon on demand if
174 there is no system keybag. */
175 int kb_state
= MKBGetDeviceLockState(NULL
);
176 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "AppleKeyStore lock state: %d", kb_state
);
182 #else /* !USE_KEYSTORE */
184 static bool hwaes_key_available(void)
189 #endif /* USE_KEYSTORE */
192 // MARK Keychain version 6 schema
194 #define __FLAGS(ARG, ...) SECDBFLAGS(__VA_ARGS__)
195 #define SECDBFLAGS(ARG, ...) __FLAGS_##ARG | __FLAGS(__VA_ARGS__)
197 #define SecDbFlags(P,L,I,S,A,D,R,C,H,B,Z,E,N) (__FLAGS_##P|__FLAGS_##L|__FLAGS_##I|__FLAGS_##S|__FLAGS_##A|__FLAGS_##D|__FLAGS_##R|__FLAGS_##C|__FLAGS_##H|__FLAGS_##B|__FLAGS_##Z|__FLAGS_##E|__FLAGS_##N)
200 #define __FLAGS_P kSecDbPrimaryKeyFlag
201 #define __FLAGS_L kSecDbInFlag
202 #define __FLAGS_I kSecDbIndexFlag
203 #define __FLAGS_S kSecDbSHA1ValueInFlag
204 #define __FLAGS_A kSecDbReturnAttrFlag
205 #define __FLAGS_D kSecDbReturnDataFlag
206 #define __FLAGS_R kSecDbReturnRefFlag
207 #define __FLAGS_C kSecDbInCryptoDataFlag
208 #define __FLAGS_H kSecDbInHashFlag
209 #define __FLAGS_B kSecDbInBackupFlag
210 #define __FLAGS_Z kSecDbDefault0Flag
211 #define __FLAGS_E kSecDbDefaultEmptyFlag
212 #define __FLAGS_N kSecDbNotNullFlag
214 // ,------------- P : Part of primary key
215 // / ,------------ L : Stored in local database
216 // / / ,----------- I : Attribute wants an index in the database
217 // / / / ,---------- S : SHA1 hashed attribute value in database (implies L)
218 // / / / / ,--------- A : Returned to client as attribute in queries
219 // / / / / / ,-------- D : Returned to client as data in queries
220 // / / / / / / ,------- R : Returned to client as ref/persistant ref in queries
221 // / / / / / / / ,------ C : Part of encrypted blob
222 // / / / / / / / / ,----- H : Attribute is part of item SHA1 hash (Implied by C)
223 // / / / / / / / / / ,---- B : Attribute is part of iTunes/iCloud backup bag
224 // / / / / / / / / / / ,--- Z : Attribute has a default value of 0
225 // / / / / / / / / / / / ,-- E : Attribute has a default value of "" or empty data
226 // / / / / / / / / / / / / ,- N : Attribute must have a value
227 // / / / / / / / / / / / / /
228 // / / / / / / / / / / / / /
229 // | | | | | | | | | | | | |
230 // common to all | | | | | | | | | | | | |
231 SECDB_ATTR(v6rowid
, "rowid", RowId
, SecDbFlags( ,L
, , , , ,R
, , ,B
, , , ));
232 SECDB_ATTR(v6cdat
, "cdat", CreationDate
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
233 SECDB_ATTR(v6mdat
, "mdat",ModificationDate
,SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
234 SECDB_ATTR(v6labl
, "labl", Blob
, SecDbFlags( ,L
, ,S
,A
, , ,C
,H
, , , , ));
235 SECDB_ATTR(v6data
, "data", EncryptedData
, SecDbFlags( ,L
, , , , , , , ,B
, , , ));
236 SECDB_ATTR(v6agrp
, "agrp", String
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, , , , ));
237 SECDB_ATTR(v6pdmn
, "pdmn", Access
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
238 SECDB_ATTR(v6sync
, "sync", Sync
, SecDbFlags(P
,L
,I
, ,A
, , ,C
,H
, ,Z
, ,N
));
239 SECDB_ATTR(v6tomb
, "tomb", Tomb
, SecDbFlags( ,L
, , , , , ,C
,H
, ,Z
, ,N
));
240 SECDB_ATTR(v6sha1
, "sha1", SHA1
, SecDbFlags( ,L
,I
, ,A
, ,R
, , , , , , ));
241 SECDB_ATTR(v6v_Data
, "v_Data", Data
, SecDbFlags( , , , , ,D
, ,C
,H
, , , , ));
242 SECDB_ATTR(v6v_pk
, "v_pk", PrimaryKey
, SecDbFlags( , , , , , , , , , , , , ));
243 // genp and inet and keys | | | | | | | | | | | | |
244 SECDB_ATTR(v6crtr
, "crtr", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
245 SECDB_ATTR(v6alis
, "alis", Blob
, SecDbFlags( ,L
, ,S
,A
, , ,C
,H
, , , , ));
246 // genp and inet | | | | | | | | | | | | |
247 SECDB_ATTR(v6desc
, "desc", Blob
, SecDbFlags( ,L
, ,S
,A
, , ,C
,H
, , , , ));
248 SECDB_ATTR(v6icmt
, "icmt", Blob
, SecDbFlags( ,L
, ,S
,A
, , ,C
,H
, , , , ));
249 SECDB_ATTR(v6type
, "type", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
250 SECDB_ATTR(v6invi
, "invi", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
251 SECDB_ATTR(v6nega
, "nega", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
252 SECDB_ATTR(v6cusi
, "cusi", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
253 SECDB_ATTR(v6prot
, "prot", Blob
, SecDbFlags( ,L
, ,S
,A
, , ,C
,H
, , , , ));
254 SECDB_ATTR(v6scrp
, "scrp", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
255 SECDB_ATTR(v6acct
, "acct", Blob
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
256 // genp only | | | | | | | | | | | | |
257 SECDB_ATTR(v6svce
, "svce", Blob
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
258 SECDB_ATTR(v6gena
, "gena", Blob
, SecDbFlags( ,L
, ,S
,A
, , ,C
,H
, , , , ));
259 // inet only | | | | | | | | | | | | |
260 SECDB_ATTR(v6sdmn
, "sdmn", Blob
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
261 SECDB_ATTR(v6srvr
, "srvr", Blob
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
262 SECDB_ATTR(v6ptcl
, "ptcl", Number
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
263 SECDB_ATTR(v6atyp
, "atyp", Blob
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
264 SECDB_ATTR(v6port
, "port", Number
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
265 SECDB_ATTR(v6path
, "path", Blob
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
266 // cert only | | | | | | | | | | | | |
267 SECDB_ATTR(v6ctyp
, "ctyp", Number
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
268 SECDB_ATTR(v6cenc
, "cenc", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
269 SECDB_ATTR(v6subj
, "subj", Data
, SecDbFlags( ,L
,I
,S
,A
, , ,C
,H
, , , , ));
270 SECDB_ATTR(v6issr
, "issr", Data
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
271 SECDB_ATTR(v6slnr
, "slnr", Data
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
272 SECDB_ATTR(v6skid
, "skid", Data
, SecDbFlags( ,L
,I
,S
,A
, , ,C
,H
, , , , ));
273 SECDB_ATTR(v6pkhh
, "pkhh", Data
, SecDbFlags( ,L
,I
, ,A
, , ,C
,H
, , , , ));
274 // cert attributes that share names with common ones but have different flags
275 SECDB_ATTR(v6certalis
, "alis", Blob
, SecDbFlags( ,L
,I
,S
,A
, , ,C
,H
, , , , ));
276 // keys only | | | | | | | | | | | | |
277 SECDB_ATTR(v6kcls
, "kcls", Number
, SecDbFlags(P
,L
,I
,S
,A
, , ,C
,H
, ,Z
, ,N
));
278 SECDB_ATTR(v6perm
, "perm", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
279 SECDB_ATTR(v6priv
, "priv", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
280 SECDB_ATTR(v6modi
, "modi", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
281 SECDB_ATTR(v6klbl
, "klbl", Data
, SecDbFlags(P
,L
,I
, ,A
, , ,C
,H
, , ,E
,N
));
282 SECDB_ATTR(v6atag
, "atag", Blob
, SecDbFlags(P
,L
, ,S
,A
, , ,C
,H
, , ,E
,N
));
283 SECDB_ATTR(v6bsiz
, "bsiz", Number
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
284 SECDB_ATTR(v6esiz
, "esiz", Number
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
285 SECDB_ATTR(v6sdat
, "sdat", Date
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
286 SECDB_ATTR(v6edat
, "edat", Date
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
287 SECDB_ATTR(v6sens
, "sens", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
288 SECDB_ATTR(v6asen
, "asen", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
289 SECDB_ATTR(v6extr
, "extr", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
290 SECDB_ATTR(v6next
, "next", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
291 SECDB_ATTR(v6encr
, "encr", Number
, SecDbFlags( ,L
,I
, ,A
, , ,C
,H
, , , , ));
292 SECDB_ATTR(v6decr
, "decr", Number
, SecDbFlags( ,L
,I
, ,A
, , ,C
,H
, , , , ));
293 SECDB_ATTR(v6drve
, "drve", Number
, SecDbFlags( ,L
,I
, ,A
, , ,C
,H
, , , , ));
294 SECDB_ATTR(v6sign
, "sign", Number
, SecDbFlags( ,L
,I
, ,A
, , ,C
,H
, , , , ));
295 SECDB_ATTR(v6vrfy
, "vrfy", Number
, SecDbFlags( ,L
,I
, ,A
, , ,C
,H
, , , , ));
296 SECDB_ATTR(v6snrc
, "snrc", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
297 SECDB_ATTR(v6vyrc
, "vyrc", Number
, SecDbFlags( ,L
, , ,A
, , ,C
,H
, , , , ));
298 SECDB_ATTR(v6wrap
, "wrap", Number
, SecDbFlags( ,L
,I
, ,A
, , ,C
,H
, , , , ));
299 SECDB_ATTR(v6unwp
, "unwp", Number
, SecDbFlags( ,L
,I
, ,A
, , ,C
,H
, , , , ));
300 // keys attributes that share names with common ones but have different flags
301 SECDB_ATTR(v6keytype
, "type", Number
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
302 SECDB_ATTR(v6keycrtr
, "crtr", Number
, SecDbFlags(P
,L
, , ,A
, , ,C
,H
, ,Z
, ,N
));
304 static const SecDbClass genp_class
= {
305 .name
= CFSTR("genp"),
336 static const SecDbClass inet_class
= {
337 .name
= CFSTR("inet"),
372 static const SecDbClass cert_class
= {
373 .name
= CFSTR("cert"),
399 static const SecDbClass keys_class
= {
400 .name
= CFSTR("keys"),
444 /* An identity which is really a cert + a key, so all cert and keys attrs are
446 static const SecDbClass identity_class
= {
447 .name
= CFSTR("idnt"),
453 static const SecDbAttr
*SecDbAttrWithKey(const SecDbClass
*c
,
456 /* Special case: identites can have all attributes of either cert
458 if (c
== &identity_class
) {
459 const SecDbAttr
*desc
;
460 if (!(desc
= SecDbAttrWithKey(&cert_class
, key
, 0)))
461 desc
= SecDbAttrWithKey(&keys_class
, key
, error
);
466 SecDbForEachAttr(c
, a
) {
467 if (CFEqual(a
->name
, key
))
472 SecError(errSecNoSuchAttr
, error
, CFSTR("attribute %@ not found in class %@"), key
, c
->name
);
477 static bool kc_transaction(SecDbConnectionRef dbt
, CFErrorRef
*error
, bool(^perform
)()) {
478 __block
bool ok
= true;
479 return ok
&& SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
480 ok
= *commit
= perform();
484 static CFStringRef
SecDbGetKindSQL(SecDbAttrKind kind
) {
489 case kSecDbPrimaryKeyAttr
:
490 case kSecDbEncryptedDataAttr
:
491 return CFSTR("BLOB");
492 case kSecDbAccessAttr
:
493 case kSecDbStringAttr
:
494 return CFSTR("TEXT");
495 case kSecDbNumberAttr
:
498 return CFSTR("INTEGER");
500 case kSecDbCreationDateAttr
:
501 case kSecDbModificationDateAttr
:
502 return CFSTR("REAL");
503 case kSecDbRowIdAttr
:
504 return CFSTR("INTEGER PRIMARY KEY AUTOINCREMENT");
508 static void SecDbAppendUnqiue(CFMutableStringRef sql
, CFStringRef value
, bool *haveUnique
) {
511 CFStringAppend(sql
, CFSTR("UNIQUE("));
513 SecDbAppendElement(sql
, value
, haveUnique
);
516 static void SecDbAppendCreateTableWithClass(CFMutableStringRef sql
, const SecDbClass
*c
) {
517 CFStringAppendFormat(sql
, 0, CFSTR("CREATE TABLE %@("), c
->name
);
518 SecDbForEachAttrWithMask(c
,desc
,kSecDbInFlag
) {
519 CFStringAppendFormat(sql
, 0, CFSTR("%@ %@"), desc
->name
, SecDbGetKindSQL(desc
->kind
));
520 if (desc
->flags
& kSecDbNotNullFlag
)
521 CFStringAppend(sql
, CFSTR(" NOT NULL"));
522 if (desc
->flags
& kSecDbDefault0Flag
)
523 CFStringAppend(sql
, CFSTR(" DEFAULT 0"));
524 if (desc
->flags
& kSecDbDefaultEmptyFlag
)
525 CFStringAppend(sql
, CFSTR(" DEFAULT ''"));
526 CFStringAppend(sql
, CFSTR(","));
529 bool haveUnique
= false;
530 SecDbForEachAttrWithMask(c
,desc
,kSecDbPrimaryKeyFlag
| kSecDbInFlag
) {
531 SecDbAppendUnqiue(sql
, desc
->name
, &haveUnique
);
534 CFStringAppend(sql
, CFSTR(")"));
536 CFStringAppend(sql
, CFSTR(");"));
539 static const char * const s3dl_upgrade_sql
[] = {
544 /* Create indices. */
545 "CREATE INDEX igsha ON genp(sha1);"
546 "CREATE INDEX iisha ON inet(sha1);"
547 "CREATE INDEX icsha ON cert(sha1);"
548 "CREATE INDEX iksha ON keys(sha1);"
549 "CREATE INDEX ialis ON cert(alis);"
550 "CREATE INDEX isubj ON cert(subj);"
551 "CREATE INDEX iskid ON cert(skid);"
552 "CREATE INDEX ipkhh ON cert(pkhh);"
553 "CREATE INDEX ikcls ON keys(kcls);"
554 "CREATE INDEX iklbl ON keys(klbl);"
555 "CREATE INDEX iencr ON keys(encr);"
556 "CREATE INDEX idecr ON keys(decr);"
557 "CREATE INDEX idrve ON keys(drve);"
558 "CREATE INDEX isign ON keys(sign);"
559 "CREATE INDEX ivrfy ON keys(vrfy);"
560 "CREATE INDEX iwrap ON keys(wrap);"
561 "CREATE INDEX iunwp ON keys(unwp);",
567 /* Rename version 2 or version 3 tables and drop version table since
568 step 0 creates it. */
569 "ALTER TABLE genp RENAME TO ogenp;"
570 "ALTER TABLE inet RENAME TO oinet;"
571 "ALTER TABLE cert RENAME TO ocert;"
572 "ALTER TABLE keys RENAME TO okeys;"
573 "DROP TABLE tversion;",
585 /* Move data from version 5 tables to new ones and drop old ones. */
586 "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;"
587 "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;"
588 "INSERT INTO cert (rowid,cdat,mdat,ctyp,cenc,labl,alis,subj,issr,slnr,skid,pkhh,data,agrp,pdmn) SELECT rowid,cdat,mdat,ctyp,cenc,labl,alis,subj,issr,slnr,skid,pkhh,data,agrp,pdmn from ocert;"
589 "INSERT INTO keys (rowid,cdat,mdat,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,cdat,mdat,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;"
594 "CREATE INDEX igsha ON genp(sha1);"
595 "CREATE INDEX iisha ON inet(sha1);"
596 "CREATE INDEX icsha ON cert(sha1);"
597 "CREATE INDEX iksha ON keys(sha1);",
604 bool init_pdmn
; // If true do a full export followed by an import of the entire database so all items are re-encoded.
607 /* On disk database format version upgrade scripts.
608 If pre is 0, version is unsupported and db is considered corrupt for having that version.
609 First entry creates the current db, each susequent entry upgrade to current from the version
610 represented by the index of the slot. Each script is either -1 (disabled) of the number of
611 the script in the main table.
612 {pre,main,post, reencode} */
613 static struct sql_stages s3dl_upgrade_script
[] = {
614 { -1, 0, 1, false },/* 0->current: Create version 6 database. */
615 {}, /* 1->current: Upgrade to version 6 from version 1 (LittleBear) -- Unsupported. */
616 {}, /* 2->current: Upgrade to version 6 from version 2 (BigBearBeta) -- Unsupported */
617 {}, /* 3->current: Upgrade to version 6 from version 3 (Apex) -- Unsupported */
618 {}, /* 4->current: Upgrade to version 6 from version 4 (Telluride) -- Unsupported */
619 { 3, 0, 7, true }, /* 5->current: Upgrade to version 6 from version 5 (TellurideGM). */
622 static bool sql_run_script(SecDbConnectionRef dbt
, int number
, CFErrorRef
*error
)
624 /* Script -1 == skip this step. */
628 /* If we are attempting to run a script we don't have, fail. */
629 if ((size_t)number
>= array_size(s3dl_upgrade_sql
))
630 return SecDbError(SQLITE_CORRUPT
, error
, CFSTR("script %d exceeds maximum %d"),
631 number
, (int)(array_size(s3dl_upgrade_sql
)));
632 __block
bool ok
= true;
634 CFMutableStringRef sql
= CFStringCreateMutable(0, 0);
635 SecDbAppendCreateTableWithClass(sql
, &genp_class
);
636 SecDbAppendCreateTableWithClass(sql
, &inet_class
);
637 SecDbAppendCreateTableWithClass(sql
, &cert_class
);
638 SecDbAppendCreateTableWithClass(sql
, &keys_class
);
639 CFStringAppend(sql
, CFSTR("CREATE TABLE tversion(version INTEGER);INSERT INTO tversion(version) VALUES(6);"));
640 CFStringPerformWithCString(sql
, ^(const char *sql_string
) {
641 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), sql_string
, NULL
, NULL
, NULL
),
642 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), sql_string
);
646 ok
= SecDbErrorWithDb(sqlite3_exec(SecDbHandle(dbt
), s3dl_upgrade_sql
[number
], NULL
, NULL
, NULL
),
647 SecDbHandle(dbt
), error
, CFSTR("sqlite3_exec: %s"), s3dl_upgrade_sql
[number
]);
652 /* Return the current database version in *version. Returns a
654 static bool s3dl_dbt_get_version(SecDbConnectionRef dbt
, int *version
, CFErrorRef
*error
)
656 CFStringRef sql
= CFSTR("SELECT version FROM tversion LIMIT 1");
657 return SecDbWithSQL(dbt
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
658 __block
bool found_version
= false;
659 bool step_ok
= SecDbForEach(stmt
, error
, ^(int row_index __unused
) {
660 if (!found_version
) {
661 *version
= sqlite3_column_int(stmt
, 0);
662 found_version
= true;
664 return found_version
;
666 if (!found_version
) {
667 /* We have a tversion table but we didn't find a single version
668 value, now what? I suppose we pretend the db is corrupted
669 since this isn't supposed to ever happen. */
670 step_ok
= SecDbError(SQLITE_CORRUPT
, error
, CFSTR("Failed to read version: database corrupt"));
671 secwarning("SELECT version step: %@", error
? *error
: NULL
);
677 static bool s3dl_dbt_upgrade_from_version(SecDbConnectionRef dbt
, int version
, CFErrorRef
*error
)
679 /* We need to go from db version to CURRENT_DB_VERSION, let's do so. */
680 __block
bool ok
= true;
681 /* O, guess we're done already. */
682 if (version
== CURRENT_DB_VERSION
)
685 if (ok
&& version
< 6) {
686 // Pre v6 keychains need to have WAL enabled, since SecDb only
687 // does this at db creation time.
688 // NOTE: This has to be run outside of a transaction.
689 ok
= (SecDbExec(dbt
, CFSTR("PRAGMA auto_vacuum = FULL"), error
) &&
690 SecDbExec(dbt
, CFSTR("PRAGMA journal_mode = WAL"), error
));
693 // Start a transaction to do the upgrade within
694 if (ok
) { ok
= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
695 // Be conservative and get the version again once we start a transaction.
696 int cur_version
= version
;
697 s3dl_dbt_get_version(dbt
, &cur_version
, NULL
);
699 /* If we are attempting to upgrade to a version greater than what we have
700 an upgrade script for, fail. */
701 if (ok
&& (cur_version
< 0 ||
702 (size_t)cur_version
>= array_size(s3dl_upgrade_script
))) {
703 ok
= SecDbError(SQLITE_CORRUPT
, error
, CFSTR("no upgrade script for version: %d"), cur_version
);
704 secerror("no upgrade script for version %d", cur_version
);
707 struct sql_stages
*script
;
709 script
= &s3dl_upgrade_script
[cur_version
];
710 if (script
->pre
== 0)
711 ok
= SecDbError(SQLITE_CORRUPT
, error
, CFSTR("unsupported db version %d"), cur_version
);
714 ok
= sql_run_script(dbt
, script
->pre
, error
);
716 ok
= sql_run_script(dbt
, script
->main
, error
);
718 ok
= sql_run_script(dbt
, script
->post
, error
);
719 if (ok
&& script
->init_pdmn
) {
720 CFErrorRef localError
= NULL
;
721 CFDictionaryRef backup
= SecServerExportKeychainPlist(dbt
,
722 KEYBAG_DEVICE
, KEYBAG_NONE
, kSecNoItemFilter
, &localError
);
725 secerror("Ignoring export error: %@ during upgrade export", localError
);
726 CFReleaseNull(localError
);
728 ok
= SecServerImportKeychainInPlist(dbt
, KEYBAG_NONE
,
729 KEYBAG_DEVICE
, backup
, kSecNoItemFilter
, &localError
);
734 if (localError
&& CFErrorGetCode(localError
) == errSecInteractionNotAllowed
) {
735 SecError(errSecUpgradePending
, error
,
736 CFSTR("unable to complete upgrade due to device lock state"));
737 secerror("unable to complete upgrade due to device lock state");
739 secerror("unable to complete upgrade for unknown reason, marking DB as corrupt: %@", localError
);
745 if (error
&& !*error
)
748 CFRelease(localError
);
751 secerror("unable to complete upgrade scripts, marking DB as corrupt: %@", error
? *error
: NULL
);
756 secerror("unable to complete upgrade scripts, marking DB as corrupt: %@", error
? *error
: NULL
);
764 /* This function is called if the db doesn't have the proper version. We
765 start an exclusive transaction and recheck the version, and then perform
766 the upgrade within that transaction. */
767 static bool s3dl_dbt_upgrade(SecDbConnectionRef dbt
, CFErrorRef
*error
)
769 // Already in a transaction
770 //return kc_transaction(dbt, error, ^{
771 int version
= 0; // Upgrade from version 0 == create new db
772 s3dl_dbt_get_version(dbt
, &version
, NULL
);
773 return s3dl_dbt_upgrade_from_version(dbt
, version
, error
);
777 const uint32_t v0KeyWrapOverHead
= 8;
779 /* Wrap takes a 128 - 256 bit key as input and returns output of
781 In bytes this means that a
782 16 byte (128 bit) key returns a 24 byte wrapped key
783 24 byte (192 bit) key returns a 32 byte wrapped key
784 32 byte (256 bit) key returns a 40 byte wrapped key */
785 static bool ks_crypt(uint32_t selector
, keybag_handle_t keybag
,
786 keyclass_t keyclass
, uint32_t textLength
, const uint8_t *source
, uint8_t *dest
, size_t *dest_len
, CFErrorRef
*error
) {
788 kern_return_t kernResult
= kIOReturnBadArgument
;
790 if (selector
== kAppleKeyStoreKeyWrap
) {
791 kernResult
= aks_wrap_key(source
, textLength
, keyclass
, keybag
, dest
, (int*)dest_len
);
792 } else if (selector
== kAppleKeyStoreKeyUnwrap
) {
793 kernResult
= aks_unwrap_key(source
, textLength
, keyclass
, keybag
, dest
, (int*)dest_len
);
796 if (kernResult
!= KERN_SUCCESS
) {
797 if ((kernResult
== kIOReturnNotPermitted
) || (kernResult
== kIOReturnNotPrivileged
)) {
798 /* Access to item attempted while keychain is locked. */
799 return SecError(errSecInteractionNotAllowed
, error
, CFSTR("ks_crypt: %x failed to %s item (class %"PRId32
", bag: %"PRId32
") Access to item attempted while keychain is locked."),
800 kernResult
, (selector
== kAppleKeyStoreKeyWrap
? "wrap" : "unwrap"), keyclass
, keybag
);
801 } else if (kernResult
== kIOReturnError
) {
802 /* Item can't be decrypted on this device, ever, so drop the item. */
803 return SecError(errSecDecode
, error
, CFSTR("ks_crypt: %x failed to %s item (class %"PRId32
", bag: %"PRId32
") Item can't be decrypted on this device, ever, so drop the item."),
804 kernResult
, (selector
== kAppleKeyStoreKeyWrap
? "wrap" : "unwrap"), keyclass
, keybag
);
806 return SecError(errSecNotAvailable
, error
, CFSTR("ks_crypt: %x failed to %s item (class %"PRId32
", bag: %"PRId32
")"),
807 kernResult
, (selector
== kAppleKeyStoreKeyWrap
? "wrap" : "unwrap"), keyclass
, keybag
);
811 #else /* !USE_KEYSTORE */
812 if (selector
== kAppleKeyStoreKeyWrap
) {
813 /* The no encryption case. */
814 if (*dest_len
>= textLength
+ 8) {
815 memcpy(dest
, source
, textLength
);
816 memset(dest
+ textLength
, 8, 8);
817 *dest_len
= textLength
+ 8;
819 return SecError(errSecNotAvailable
, error
, CFSTR("ks_crypt: failed to wrap item (class %"PRId32
")"), keyclass
);
820 } else if (selector
== kAppleKeyStoreKeyUnwrap
) {
821 if (*dest_len
+ 8 >= textLength
) {
822 memcpy(dest
, source
, textLength
- 8);
823 *dest_len
= textLength
- 8;
825 return SecError(errSecNotAvailable
, error
, CFSTR("ks_crypt: failed to unwrap item (class %"PRId32
")"), keyclass
);
828 #endif /* USE_KEYSTORE */
832 CFDataRef
kc_plist_copy_der(CFPropertyListRef plist
, CFErrorRef
*error
) {
833 size_t len
= der_sizeof_plist(plist
, error
);
834 CFMutableDataRef encoded
= CFDataCreateMutable(0, len
);
835 CFDataSetLength(encoded
, len
);
836 uint8_t *der_end
= CFDataGetMutableBytePtr(encoded
);
837 const uint8_t *der
= der_end
;
839 der_end
= der_encode_plist(plist
, error
, der
, der_end
);
841 CFReleaseNull(encoded
);
843 assert(!der_end
|| der_end
== der
);
848 static CFDataRef
kc_copy_digest(const struct ccdigest_info
*di
, size_t len
,
849 const void *data
, CFErrorRef
*error
) {
850 CFMutableDataRef digest
= CFDataCreateMutable(0, di
->output_size
);
851 CFDataSetLength(digest
, di
->output_size
);
852 ccdigest(di
, len
, data
, CFDataGetMutableBytePtr(digest
));
856 CFDataRef
kc_copy_sha1(size_t len
, const void *data
, CFErrorRef
*error
) {
857 return kc_copy_digest(ccsha1_di(), len
, data
, error
);
860 CFDataRef
kc_copy_plist_sha1(CFPropertyListRef plist
, CFErrorRef
*error
) {
861 CFDataRef der
= kc_plist_copy_der(plist
, error
);
862 CFDataRef digest
= NULL
;
864 digest
= kc_copy_sha1(CFDataGetLength(der
), CFDataGetBytePtr(der
), error
);
870 /* Given plainText create and return a CFDataRef containing:
871 BULK_KEY = RandomKey()
872 version || keyclass || KeyStore_WRAP(keyclass, BULK_KEY) ||
873 AES(BULK_KEY, NULL_IV, plainText || padding)
875 bool ks_encrypt_data(keybag_handle_t keybag
,
876 keyclass_t keyclass
, CFDataRef plainText
, CFDataRef
*pBlob
, CFErrorRef
*error
) {
877 CFMutableDataRef blob
= NULL
;
879 //check(keybag >= 0);
881 /* Precalculate output blob length. */
882 const uint32_t bulkKeySize
= 32; /* Use 256 bit AES key for bulkKey. */
883 const uint32_t maxKeyWrapOverHead
= 8 + 32;
884 uint8_t bulkKey
[bulkKeySize
];
885 uint8_t bulkKeyWrapped
[bulkKeySize
+ maxKeyWrapOverHead
];
886 size_t bulkKeyWrappedSize
= sizeof(bulkKeyWrapped
);
887 uint32_t key_wrapped_size
;
889 if (!plainText
|| CFGetTypeID(plainText
) != CFDataGetTypeID()
891 ok
= SecError(errSecParam
, error
, CFSTR("ks_encrypt_data: invalid plain text"));
895 size_t ptLen
= CFDataGetLength(plainText
);
896 size_t ctLen
= ptLen
;
898 uint32_t version
= 3;
900 if (SecRandomCopyBytes(kSecRandomDefault
, bulkKeySize
, bulkKey
)) {
901 ok
= SecError(errSecAllocate
, error
, CFSTR("ks_encrypt_data: SecRandomCopyBytes failed"));
905 /* Now that we're done using the bulkKey, in place encrypt it. */
906 require_quiet(ok
= ks_crypt(kAppleKeyStoreKeyWrap
, keybag
, keyclass
,
907 bulkKeySize
, bulkKey
, bulkKeyWrapped
,
908 &bulkKeyWrappedSize
, error
), out
);
909 key_wrapped_size
= (uint32_t)bulkKeyWrappedSize
;
911 size_t blobLen
= sizeof(version
) + sizeof(keyclass
) +
912 sizeof(key_wrapped_size
) + key_wrapped_size
+ ctLen
+ tagLen
;
914 require_quiet(blob
= CFDataCreateMutable(NULL
, blobLen
), out
);
915 CFDataSetLength(blob
, blobLen
);
916 UInt8
*cursor
= CFDataGetMutableBytePtr(blob
);
918 *((uint32_t *)cursor
) = version
;
919 cursor
+= sizeof(version
);
921 *((keyclass_t
*)cursor
) = keyclass
;
922 cursor
+= sizeof(keyclass
);
924 *((uint32_t *)cursor
) = key_wrapped_size
;
925 cursor
+= sizeof(key_wrapped_size
);
927 memcpy(cursor
, bulkKeyWrapped
, key_wrapped_size
);
928 cursor
+= key_wrapped_size
;
930 /* Encrypt the plainText with the bulkKey. */
931 CCCryptorStatus ccerr
= CCCryptorGCM(kCCEncrypt
, kCCAlgorithmAES128
,
932 bulkKey
, bulkKeySize
,
934 NULL
, 0, /* auth data */
935 CFDataGetBytePtr(plainText
), ptLen
,
937 cursor
+ ctLen
, &tagLen
);
939 ok
= SecError(errSecInternal
, error
, CFSTR("ks_encrypt_data: CCCryptorGCM failed: %d"), ccerr
);
943 ok
= SecError(errSecInternal
, error
, CFSTR("ks_encrypt_data: CCCryptorGCM expected: 16 got: %ld byte tag"), tagLen
);
948 memset(bulkKey
, 0, sizeof(bulkKey
));
957 /* Given cipherText containing:
958 version || keyclass || KeyStore_WRAP(keyclass, BULK_KEY) ||
959 AES(BULK_KEY, NULL_IV, plainText || padding)
960 return the plainText. */
961 bool ks_decrypt_data(keybag_handle_t keybag
,
962 keyclass_t
*pkeyclass
, CFDataRef blob
, CFDataRef
*pPlainText
,
963 uint32_t *version_p
, CFErrorRef
*error
) {
964 const uint32_t bulkKeySize
= 32; /* Use 256 bit AES key for bulkKey. */
965 uint8_t bulkKey
[bulkKeySize
];
966 size_t bulkKeyCapacity
= sizeof(bulkKey
);
969 CFMutableDataRef plainText
= NULL
;
974 check((keybag
>= 0) || (keybag
== session_keybag_handle
));
979 ok
= SecError(errSecParam
, error
, CFSTR("ks_decrypt_data: invalid blob"));
983 size_t blobLen
= CFDataGetLength(blob
);
984 const uint8_t *cursor
= CFDataGetBytePtr(blob
);
987 uint32_t wrapped_key_size
;
989 /* Check for underflow, ensuring we have at least one full AES block left. */
990 if (blobLen
< sizeof(version
) + sizeof(keyclass
) +
991 bulkKeySize
+ v0KeyWrapOverHead
+ 16) {
992 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: Check for underflow"));
996 version
= *((uint32_t *)cursor
);
997 cursor
+= sizeof(version
);
999 keyclass
= *((keyclass_t
*)cursor
);
1001 *pkeyclass
= keyclass
;
1002 cursor
+= sizeof(keyclass
);
1004 size_t minimum_blob_len
= sizeof(version
) + sizeof(keyclass
) + 16;
1005 size_t ctLen
= blobLen
- sizeof(version
) - sizeof(keyclass
);
1009 wrapped_key_size
= bulkKeySize
+ v0KeyWrapOverHead
;
1013 /* v2 and v3 have the same crypto, just different dictionary encodings. */
1016 minimum_blob_len
-= 16; // Remove PKCS7 padding block requirement
1017 ctLen
-= tagLen
; // Remove tagLen from ctLen
1020 wrapped_key_size
= *((uint32_t *)cursor
);
1021 cursor
+= sizeof(wrapped_key_size
);
1022 minimum_blob_len
+= sizeof(wrapped_key_size
);
1023 ctLen
-= sizeof(wrapped_key_size
);
1026 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: invalid version %d"), version
);
1030 /* Validate key wrap length against total length */
1031 require(blobLen
- minimum_blob_len
- tagLen
>= wrapped_key_size
, out
);
1032 ctLen
-= wrapped_key_size
;
1033 if (version
< 2 && (ctLen
& 0xF) != 0) {
1034 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: invalid version"));
1038 /* Now unwrap the bulk key using a key in the keybag. */
1039 require_quiet(ok
= ks_crypt(kAppleKeyStoreKeyUnwrap
, keybag
,
1040 keyclass
, wrapped_key_size
, cursor
, bulkKey
, &bulkKeyCapacity
, error
), out
);
1041 cursor
+= wrapped_key_size
;
1043 plainText
= CFDataCreateMutable(NULL
, ctLen
);
1045 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: failed to allocate data for plain text"));
1048 CFDataSetLength(plainText
, ctLen
);
1050 /* Decrypt the cipherText with the bulkKey. */
1051 CCCryptorStatus ccerr
;
1053 uint8_t tag
[tagLen
];
1054 ccerr
= CCCryptorGCM(kCCDecrypt
, kCCAlgorithmAES128
,
1055 bulkKey
, bulkKeySize
,
1057 NULL
, 0, /* auth data */
1059 CFDataGetMutableBytePtr(plainText
),
1062 /* TODO: Should this be errSecDecode once AppleKeyStore correctly
1063 identifies uuid unwrap failures? */
1064 /* errSecInteractionNotAllowed; */
1065 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: CCCryptorGCM failed: %d"), ccerr
);
1069 ok
= SecError(errSecInternal
, error
, CFSTR("ks_decrypt_data: CCCryptorGCM expected: 16 got: %ld byte tag"), tagLen
);
1073 if (memcmp(tag
, cursor
, tagLen
)) {
1074 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: CCCryptorGCM computed tag not same as tag in blob"));
1079 ccerr
= CCCrypt(kCCDecrypt
, kCCAlgorithmAES128
, kCCOptionPKCS7Padding
,
1080 bulkKey
, bulkKeySize
, NULL
, cursor
, ctLen
,
1081 CFDataGetMutableBytePtr(plainText
), ctLen
, &ptLen
);
1083 /* TODO: Should this be errSecDecode once AppleKeyStore correctly
1084 identifies uuid unwrap failures? */
1085 /* errSecInteractionNotAllowed; */
1086 ok
= SecError(errSecDecode
, error
, CFSTR("ks_decrypt_data: CCCrypt failed: %d"), ccerr
);
1089 CFDataSetLength(plainText
, ptLen
);
1091 if (version_p
) *version_p
= version
;
1093 memset(bulkKey
, 0, bulkKeySize
);
1095 CFReleaseSafe(plainText
);
1097 *pPlainText
= plainText
;
1102 static bool use_hwaes() {
1103 static bool use_hwaes
;
1104 static dispatch_once_t check_once
;
1105 dispatch_once(&check_once
, ^{
1106 use_hwaes
= hwaes_key_available();
1108 asl_log(NULL
, NULL
, ASL_LEVEL_INFO
, "using hwaes key");
1110 asl_log(NULL
, NULL
, ASL_LEVEL_ERR
, "unable to access hwaes key");
1116 /* Upper limit for number of keys in a QUERY dictionary. */
1117 #define QUERY_KEY_LIMIT_BASE (128)
1119 #define QUERY_KEY_LIMIT (31 + QUERY_KEY_LIMIT_BASE)
1121 #define QUERY_KEY_LIMIT QUERY_KEY_LIMIT_BASE
1124 /* Inline accessors to attr and match values in a query. */
1125 static inline CFIndex
query_attr_count(const Query
*q
)
1127 return q
->q_attr_end
;
1130 static inline Pair
query_attr_at(const Query
*q
, CFIndex ix
)
1132 return q
->q_pairs
[ix
];
1135 static inline CFIndex
query_match_count(const Query
*q
)
1137 return q
->q_match_end
- q
->q_match_begin
;
1140 static inline Pair
query_match_at(const Query
*q
, CFIndex ix
)
1142 return q
->q_pairs
[q
->q_match_begin
+ ix
];
1145 /* Private routines used to parse a query. */
1147 /* Sets q_keyclass based on value. */
1148 static void query_parse_keyclass(const void *value
, Query
*q
) {
1149 if (!isString(value
)) {
1150 SecError(errSecParam
, &q
->q_error
, CFSTR("accessible attribute %@ not a string"), value
);
1152 } else if (CFEqual(value
, kSecAttrAccessibleWhenUnlocked
)) {
1153 q
->q_keyclass
= key_class_ak
;
1154 } else if (CFEqual(value
, kSecAttrAccessibleAfterFirstUnlock
)) {
1155 q
->q_keyclass
= key_class_ck
;
1156 } else if (CFEqual(value
, kSecAttrAccessibleAlways
)) {
1157 q
->q_keyclass
= key_class_dk
;
1158 } else if (CFEqual(value
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
)) {
1159 q
->q_keyclass
= key_class_aku
;
1160 } else if (CFEqual(value
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
)) {
1161 q
->q_keyclass
= key_class_cku
;
1162 } else if (CFEqual(value
, kSecAttrAccessibleAlwaysThisDeviceOnly
)) {
1163 q
->q_keyclass
= key_class_dku
;
1165 SecError(errSecParam
, &q
->q_error
, CFSTR("accessible attribute %@ unknown"), value
);
1168 //q->q_keyclass_s = value;
1171 static const SecDbClass
*kc_class_with_name(CFStringRef name
) {
1172 if (isString(name
)) {
1174 // TODO Iterate kc_db_classes and look for name == class->name.
1175 // Or get clever and switch on first letter of class name and compare to verify
1176 static const void *kc_db_classes
[] = {
1184 if (CFEqual(name
, kSecClassGenericPassword
))
1186 else if (CFEqual(name
, kSecClassInternetPassword
))
1188 else if (CFEqual(name
, kSecClassCertificate
))
1190 else if (CFEqual(name
, kSecClassKey
))
1192 else if (CFEqual(name
, kSecClassIdentity
))
1193 return &identity_class
;
1198 /* AUDIT[securityd](done):
1199 key (ok) is a caller provided, string or number of length 4.
1200 value (ok) is a caller provided, non NULL CFTypeRef.
1202 static void query_add_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
)
1204 if (CFEqual(desc
->name
, kSecAttrSynchronizable
)) {
1206 if (CFEqual(value
, kSecAttrSynchronizableAny
))
1207 return; /* skip the attribute so it isn't part of the search */
1210 CFTypeRef attr
= NULL
;
1211 switch (desc
->kind
) {
1212 case kSecDbDataAttr
:
1213 attr
= copyData(value
);
1215 case kSecDbBlobAttr
:
1216 attr
= copyBlob(value
);
1218 case kSecDbDateAttr
:
1219 case kSecDbCreationDateAttr
:
1220 case kSecDbModificationDateAttr
:
1221 attr
= copyDate(value
);
1223 case kSecDbNumberAttr
:
1224 case kSecDbSyncAttr
:
1225 case kSecDbTombAttr
:
1226 attr
= copyNumber(value
);
1228 case kSecDbAccessAttr
:
1229 case kSecDbStringAttr
:
1230 attr
= copyString(value
);
1232 case kSecDbSHA1Attr
:
1233 attr
= copySHA1(value
);
1235 case kSecDbRowIdAttr
:
1236 case kSecDbPrimaryKeyAttr
:
1237 case kSecDbEncryptedDataAttr
:
1242 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("attribute %@: value: %@ failed to convert"), desc
->name
, value
);
1246 /* Store plaintext attr data in q_item unless it's a kSecDbSHA1Attr. */
1247 if (q
->q_item
&& desc
->kind
!= kSecDbSHA1Attr
) {
1248 CFDictionarySetValue(q
->q_item
, desc
->name
, attr
);
1251 if (CFEqual(desc
->name
, kSecAttrAccessible
)) {
1252 query_parse_keyclass(attr
, q
);
1255 /* Convert attr to (sha1) digest if requested. */
1256 if (desc
->flags
& kSecDbSHA1ValueInFlag
) {
1257 CFDataRef data
= copyData(attr
);
1260 SecError(errSecInternal
, &q
->q_error
, CFSTR("failed to get attribute %@ data"), desc
->name
);
1264 CFMutableDataRef digest
= CFDataCreateMutable(0, CC_SHA1_DIGEST_LENGTH
);
1265 CFDataSetLength(digest
, CC_SHA1_DIGEST_LENGTH
);
1266 /* 64 bits cast: worst case is we generate the wrong hash */
1267 assert((unsigned long)CFDataGetLength(data
)<UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1268 CCDigest(kCCDigestSHA1
, CFDataGetBytePtr(data
), (CC_LONG
)CFDataGetLength(data
),
1269 CFDataGetMutableBytePtr(digest
));
1274 /* Record the new attr key, value in q_pairs. */
1275 q
->q_pairs
[q
->q_attr_end
].key
= desc
->name
;
1276 q
->q_pairs
[q
->q_attr_end
++].value
= attr
;
1279 static void query_add_attribute(const void *key
, const void *value
, Query
*q
)
1281 const SecDbAttr
*desc
= SecDbAttrWithKey(q
->q_class
, key
, &q
->q_error
);
1283 query_add_attribute_with_desc(desc
, value
, q
);
1286 /* First remove key from q->q_pairs if it's present, then add the attribute again. */
1287 static void query_set_attribute_with_desc(const SecDbAttr
*desc
, const void *value
, Query
*q
) {
1288 if (CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1290 for (ix
= 0; ix
< q
->q_attr_end
; ++ix
) {
1291 if (CFEqual(desc
->name
, q
->q_pairs
[ix
].key
)) {
1292 CFReleaseSafe(q
->q_pairs
[ix
].value
);
1294 for (; ix
< q
->q_attr_end
; ++ix
) {
1295 q
->q_pairs
[ix
] = q
->q_pairs
[ix
+ 1];
1297 CFDictionaryRemoveValue(q
->q_item
, desc
->name
);
1302 query_add_attribute_with_desc(desc
, value
, q
);
1305 /* AUDIT[securityd](done):
1306 key (ok) is a caller provided, string starting with 'm'.
1307 value (ok) is a caller provided, non NULL CFTypeRef.
1309 static void query_add_match(const void *key
, const void *value
, Query
*q
)
1311 /* Record the match key, value in q_pairs. */
1312 --(q
->q_match_begin
);
1313 q
->q_pairs
[q
->q_match_begin
].key
= key
;
1314 q
->q_pairs
[q
->q_match_begin
].value
= value
;
1316 if (CFEqual(kSecMatchLimit
, key
)) {
1317 /* Figure out what the value for kSecMatchLimit is if specified. */
1318 if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
1319 if (!CFNumberGetValue(value
, kCFNumberCFIndexType
, &q
->q_limit
))
1320 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("failed to convert match limit %@ to CFIndex"), value
);
1321 } else if (CFEqual(kSecMatchLimitAll
, value
)) {
1322 q
->q_limit
= kSecMatchUnlimited
;
1323 } else if (CFEqual(kSecMatchLimitOne
, value
)) {
1326 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("unsupported match limit %@"), value
);
1328 } else if (CFEqual(kSecMatchIssuers
, key
) &&
1329 (CFGetTypeID(value
) == CFArrayGetTypeID()))
1331 CFMutableArrayRef canonical_issuers
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1332 if (canonical_issuers
) {
1333 CFIndex i
, count
= CFArrayGetCount(value
);
1334 for (i
= 0; i
< count
; i
++) {
1335 CFTypeRef issuer_data
= CFArrayGetValueAtIndex(value
, i
);
1336 CFDataRef issuer_canonical
= NULL
;
1337 if (CFDataGetTypeID() == CFGetTypeID(issuer_data
))
1338 issuer_canonical
= SecDistinguishedNameCopyNormalizedContent((CFDataRef
)issuer_data
);
1339 if (issuer_canonical
) {
1340 CFArrayAppendValue(canonical_issuers
, issuer_canonical
);
1341 CFRelease(issuer_canonical
);
1345 if (CFArrayGetCount(canonical_issuers
) > 0) {
1346 q
->q_match_issuer
= canonical_issuers
;
1348 CFRelease(canonical_issuers
);
1353 static bool query_set_class(Query
*q
, CFStringRef c_name
, CFErrorRef
*error
) {
1354 const SecDbClass
*value
;
1355 if (c_name
&& CFGetTypeID(c_name
) == CFStringGetTypeID() &&
1356 (value
= kc_class_with_name(c_name
)) &&
1357 (q
->q_class
== 0 || q
->q_class
== value
)) {
1362 if (error
&& !*error
)
1363 SecError((c_name
? errSecNoSuchClass
: errSecItemClassMissing
), error
, CFSTR("can find class named: %@"), c_name
);
1369 static const SecDbClass
*query_get_class(CFDictionaryRef query
, CFErrorRef
*error
) {
1370 CFStringRef c_name
= NULL
;
1371 const void *value
= CFDictionaryGetValue(query
, kSecClass
);
1372 if (isString(value
)) {
1375 value
= CFDictionaryGetValue(query
, kSecValuePersistentRef
);
1376 if (isData(value
)) {
1377 CFDataRef pref
= value
;
1378 _SecItemParsePersistentRef(pref
, &c_name
, 0);
1382 if (c_name
&& (value
= kc_class_with_name(c_name
))) {
1386 SecError(errSecNoSuchClass
, error
, CFSTR("can't find class named: %@"), c_name
);
1388 SecError(errSecItemClassMissing
, error
, CFSTR("query missing class name"));
1393 /* AUDIT[securityd](done):
1394 key (ok) is a caller provided, string starting with 'c'.
1395 value (ok) is a caller provided, non NULL CFTypeRef.
1397 static void query_add_class(const void *key
, const void *value
, Query
*q
)
1399 if (CFEqual(key
, kSecClass
)) {
1400 query_set_class(q
, value
, &q
->q_error
);
1402 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_class: key %@ is not %@"), key
, kSecClass
);
1406 /* AUDIT[securityd](done):
1407 key (ok) is a caller provided, string starting with 'r'.
1408 value (ok) is a caller provided, non NULL CFTypeRef.
1410 static void query_add_return(const void *key
, const void *value
, Query
*q
)
1412 ReturnTypeMask mask
;
1413 if (CFGetTypeID(value
) != CFBooleanGetTypeID()) {
1414 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_return: value %@ is not CFBoolean"), value
);
1418 int set_it
= CFEqual(value
, kCFBooleanTrue
);
1420 if (CFEqual(key
, kSecReturnData
))
1421 mask
= kSecReturnDataMask
;
1422 else if (CFEqual(key
, kSecReturnAttributes
))
1423 mask
= kSecReturnAttributesMask
;
1424 else if (CFEqual(key
, kSecReturnRef
))
1425 mask
= kSecReturnRefMask
;
1426 else if (CFEqual(key
, kSecReturnPersistentRef
))
1427 mask
= kSecReturnPersistentRefMask
;
1429 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_return: unknown key %@"), key
);
1433 if ((q
->q_return_type
& mask
) && !set_it
) {
1434 /* Clear out this bit (it's set so xor with the mask will clear it). */
1435 q
->q_return_type
^= mask
;
1436 } else if (!(q
->q_return_type
& mask
) && set_it
) {
1438 q
->q_return_type
|= mask
;
1442 /* AUDIT[securityd](done):
1443 key (ok) is a caller provided, string starting with 'u'.
1444 value (ok since q_use_item_list is unused) is a caller provided, non
1447 static void query_add_use(const void *key
, const void *value
, Query
*q
)
1449 if (CFEqual(key
, kSecUseItemList
)) {
1450 /* TODO: Add sanity checking when we start using this. */
1451 q
->q_use_item_list
= value
;
1452 } else if (CFEqual(key
, kSecUseTombstones
)) {
1453 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
1454 q
->q_use_tomb
= value
;
1455 } else if (CFGetTypeID(value
) == CFNumberGetTypeID()) {
1456 q
->q_use_tomb
= CFBooleanGetValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1457 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
1458 q
->q_use_tomb
= CFStringGetIntValue(value
) ? kCFBooleanTrue
: kCFBooleanFalse
;
1460 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_use: value %@ for key %@ is neither CFBoolean nor CFNumber"), value
, key
);
1463 #if defined(MULTIPLE_KEYCHAINS)
1464 } else if (CFEqual(key
, kSecUseKeychain
)) {
1465 q
->q_use_keychain
= value
;
1466 } else if (CFEqual(key
, kSecUseKeychainList
)) {
1467 q
->q_use_keychain_list
= value
;
1468 #endif /* !defined(MULTIPLE_KEYCHAINS) */
1470 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_use: unknown key %@"), key
);
1475 static void query_set_data(const void *value
, Query
*q
) {
1476 if (!isData(value
)) {
1477 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("set_data: value %@ is not type data"), value
);
1481 CFDictionarySetValue(q
->q_item
, kSecValueData
, value
);
1485 /* AUDIT[securityd](done):
1486 key (ok) is a caller provided, string starting with 'u'.
1487 value (ok) is a caller provided, non NULL CFTypeRef.
1489 static void query_add_value(const void *key
, const void *value
, Query
*q
)
1491 if (CFEqual(key
, kSecValueData
)) {
1492 query_set_data(value
, q
);
1494 } else if (CFEqual(key
, kSecValueRef
)) {
1496 /* TODO: Add value type sanity checking. */
1498 } else if (CFEqual(key
, kSecValuePersistentRef
)) {
1500 if (_SecItemParsePersistentRef(value
, &c_name
, &q
->q_row_id
))
1501 query_set_class(q
, c_name
, &q
->q_error
);
1503 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("add_value: value %@ is not a valid persitent ref"), value
);
1505 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("add_value: unknown key %@"), key
);
1510 /* AUDIT[securityd](done):
1511 key (ok) is a caller provided, unchecked.
1512 value (ok) is a caller provided, unchecked.
1514 static void query_update_applier(const void *key
, const void *value
,
1517 Query
*q
= (Query
*)context
;
1518 /* If something went wrong there is no point processing any more args. */
1522 /* Make sure we have a string key. */
1523 if (!isString(key
)) {
1524 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("update_applier: unknown key type %@"), key
);
1529 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("update_applier: key %@ has NULL value"), key
);
1533 if (CFEqual(key
, kSecValueData
)) {
1534 query_set_data(value
, q
);
1536 query_add_attribute(key
, value
, q
);
1540 /* AUDIT[securityd](done):
1541 key (ok) is a caller provided, unchecked.
1542 value (ok) is a caller provided, unchecked.
1544 static void query_applier(const void *key
, const void *value
, void *context
)
1546 Query
*q
= (Query
*)context
;
1547 /* If something went wrong there is no point processing any more args. */
1551 /* Make sure we have a key. */
1553 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: NULL key"));
1557 /* Make sure we have a value. */
1559 SecError(errSecItemInvalidValue
, &q
->q_error
, CFSTR("applier: key %@ has NULL value"), key
);
1563 /* Figure out what type of key we are dealing with. */
1564 CFTypeID key_id
= CFGetTypeID(key
);
1565 if (key_id
== CFStringGetTypeID()) {
1566 CFIndex key_len
= CFStringGetLength(key
);
1567 /* String keys can be different things. The subtype is determined by:
1568 length 4 strings are all attributes. Otherwise the first char
1569 determines the type:
1570 c: class must be kSecClass
1571 m: match like kSecMatchPolicy
1572 r: return like kSecReturnData
1578 query_add_attribute(key
, value
, q
);
1579 } else if (key_len
> 1) {
1580 UniChar k_first_char
= CFStringGetCharacterAtIndex(key
, 0);
1581 switch (k_first_char
)
1583 case 'c': /* class */
1584 query_add_class(key
, value
, q
);
1586 case 'm': /* match */
1587 query_add_match(key
, value
, q
);
1589 case 'r': /* return */
1590 query_add_return(key
, value
, q
);
1593 query_add_use(key
, value
, q
);
1595 case 'v': /* value */
1596 query_add_value(key
, value
, q
);
1599 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid"), key
);
1603 SecError(errSecItemInvalidKey
, &q
->q_error
, CFSTR("applier: key %@ invalid length"), key
);
1605 } else if (key_id
== CFNumberGetTypeID()) {
1606 /* Numeric keys are always (extended) attributes. */
1607 /* TODO: Why is this here? query_add_attribute() doesn't take numbers. */
1608 query_add_attribute(key
, value
, q
);
1610 /* We only support string and number type keys. */
1611 SecError(errSecItemInvalidKeyType
, &q
->q_error
, CFSTR("applier: key %@ neither string nor number"), key
);
1615 static CFStringRef
query_infer_keyclass(Query
*q
, CFStringRef agrp
) {
1616 /* apsd and lockdown are always dku. */
1617 if (CFEqual(agrp
, CFSTR("com.apple.apsd"))
1618 || CFEqual(agrp
, CFSTR("lockdown-identities"))) {
1619 return kSecAttrAccessibleAlwaysThisDeviceOnly
;
1621 /* All other certs or in the apple agrp is dk. */
1622 if (q
->q_class
== &cert_class
) {
1623 /* third party certs are always dk. */
1624 return kSecAttrAccessibleAlways
;
1626 /* The rest defaults to ak. */
1627 return kSecAttrAccessibleWhenUnlocked
;
1630 static void query_ensure_keyclass(Query
*q
, CFStringRef agrp
) {
1631 if (q
->q_keyclass
== 0) {
1632 CFStringRef accessible
= query_infer_keyclass(q
, agrp
);
1633 query_add_attribute(kSecAttrAccessible
, accessible
, q
);
1637 static bool query_error(Query
*q
, CFErrorRef
*error
) {
1639 CFErrorRef tmp
= q
->q_error
;
1641 if (error
&& !*error
) {
1651 bool query_destroy(Query
*q
, CFErrorRef
*error
) {
1652 bool ok
= query_error(q
, error
);
1653 CFIndex ix
, attr_count
= query_attr_count(q
);
1654 for (ix
= 0; ix
< attr_count
; ++ix
) {
1655 CFReleaseSafe(query_attr_at(q
, ix
).value
);
1657 CFReleaseSafe(q
->q_item
);
1658 CFReleaseSafe(q
->q_primary_key_digest
);
1659 CFReleaseSafe(q
->q_match_issuer
);
1665 static void SecKeychainChanged(bool syncWithPeers
) {
1666 uint32_t result
= notify_post(g_keychain_changed_notification
);
1668 SOSCCSyncWithAllPeers();
1669 if (result
== NOTIFY_STATUS_OK
)
1670 secnotice("item", "Sent %s%s", syncWithPeers
? "SyncWithAllPeers and " : "", g_keychain_changed_notification
);
1672 secerror("%snotify_post %s returned: %" PRIu32
, syncWithPeers
? "Sent SyncWithAllPeers, " : "", g_keychain_changed_notification
, result
);
1675 static bool query_notify_and_destroy(Query
*q
, bool ok
, CFErrorRef
*error
) {
1676 if (ok
&& !q
->q_error
&& q
->q_sync_changed
) {
1677 SecKeychainChanged(true);
1679 return query_destroy(q
, error
) && ok
;
1682 /* Allocate and initialize a Query object for query. */
1683 Query
*query_create(const SecDbClass
*qclass
, CFDictionaryRef query
,
1687 if (error
&& !*error
)
1688 SecError(errSecItemClassMissing
, error
, CFSTR("Missing class"));
1692 /* Number of pairs we need is the number of attributes in this class
1693 plus the number of keys in the dictionary, minus one for each key in
1694 the dictionary that is a regular attribute. */
1695 CFIndex key_count
= SecDbClassAttrCount(qclass
);
1696 if (key_count
== 0) {
1697 // Identities claim to have 0 attributes, but they really support any keys or cert attribute.
1698 key_count
= SecDbClassAttrCount(&cert_class
) + SecDbClassAttrCount(&keys_class
);
1702 key_count
+= CFDictionaryGetCount(query
);
1703 SecDbForEachAttr(qclass
, attr
) {
1704 if (CFDictionaryContainsKey(query
, attr
->name
))
1709 if (key_count
> QUERY_KEY_LIMIT
) {
1710 if (error
&& !*error
)
1712 secerror("key_count: %ld, QUERY_KEY_LIMIT: %d", (long)key_count
, QUERY_KEY_LIMIT
);
1713 SecError(errSecItemIllegalQuery
, error
, CFSTR("Past query key limit"));
1718 Query
*q
= calloc(1, sizeof(Query
) + sizeof(Pair
) * key_count
);
1720 if (error
&& !*error
)
1721 SecError(errSecAllocate
, error
, CFSTR("Out of memory"));
1725 q
->q_keybag
= KEYBAG_DEVICE
;
1726 q
->q_class
= qclass
;
1727 q
->q_match_begin
= q
->q_match_end
= key_count
;
1728 q
->q_item
= CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1733 /* Parse query for a Query object q. */
1734 static bool query_parse_with_applier(Query
*q
, CFDictionaryRef query
,
1735 CFDictionaryApplierFunction applier
,
1736 CFErrorRef
*error
) {
1737 CFDictionaryApplyFunction(query
, applier
, q
);
1738 return query_error(q
, error
);
1741 /* Parse query for a Query object q. */
1742 static bool query_parse(Query
*q
, CFDictionaryRef query
,
1743 CFErrorRef
*error
) {
1744 return query_parse_with_applier(q
, query
, query_applier
, error
);
1747 /* Parse query for a Query object q. */
1748 static bool query_update_parse(Query
*q
, CFDictionaryRef update
,
1749 CFErrorRef
*error
) {
1750 return query_parse_with_applier(q
, update
, query_update_applier
, error
);
1753 static Query
*query_create_with_limit(CFDictionaryRef query
, CFIndex limit
,
1754 CFErrorRef
*error
) {
1756 q
= query_create(query_get_class(query
, error
), query
, error
);
1759 if (!query_parse(q
, query
, error
)) {
1760 query_destroy(q
, error
);
1763 if (!q
->q_sync
&& !q
->q_row_id
) {
1764 /* query did not specify a kSecAttrSynchronizable attribute,
1765 * and did not contain a persistent reference. */
1766 query_add_attribute(kSecAttrSynchronizable
, kCFBooleanFalse
, q
);
1773 //TODO: Move this to SecDbItemRef
1775 /* Make sure all attributes that are marked as not_null have a value. If
1776 force_date is false, only set mdat and cdat if they aren't already set. */
1778 query_pre_add(Query
*q
, bool force_date
) {
1779 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1780 SecDbForEachAttrWithMask(q
->q_class
, desc
, kSecDbInFlag
) {
1781 if (desc
->kind
== kSecDbCreationDateAttr
||
1782 desc
->kind
== kSecDbModificationDateAttr
) {
1784 query_set_attribute_with_desc(desc
, now
, q
);
1785 } else if (!CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1786 query_add_attribute_with_desc(desc
, now
, q
);
1788 } else if ((desc
->flags
& kSecDbNotNullFlag
) &&
1789 !CFDictionaryContainsKey(q
->q_item
, desc
->name
)) {
1790 CFTypeRef value
= NULL
;
1791 if (desc
->flags
& kSecDbDefault0Flag
) {
1792 if (desc
->kind
== kSecDbDateAttr
)
1793 value
= CFDateCreate(kCFAllocatorDefault
, 0.0);
1796 value
= CFNumberCreate(0, kCFNumberSInt32Type
, &vzero
);
1798 } else if (desc
->flags
& kSecDbDefaultEmptyFlag
) {
1799 if (desc
->kind
== kSecDbDataAttr
)
1800 value
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
1807 /* Safe to use query_add_attribute here since the attr wasn't
1809 query_add_attribute_with_desc(desc
, value
, q
);
1817 //TODO: Move this to SecDbItemRef
1819 /* Update modification_date if needed. */
1821 query_pre_update(Query
*q
) {
1822 SecDbForEachAttr(q
->q_class
, desc
) {
1823 if (desc
->kind
== kSecDbModificationDateAttr
) {
1824 CFDateRef now
= CFDateCreate(0, CFAbsoluteTimeGetCurrent());
1825 query_set_attribute_with_desc(desc
, now
, q
);
1831 /* AUDIT[securityd](done):
1832 accessGroup (ok) is a caller provided, non NULL CFTypeRef.
1834 Return true iff accessGroup is allowable according to accessGroups.
1836 static bool accessGroupsAllows(CFArrayRef accessGroups
,
1837 CFStringRef accessGroup
) {
1838 /* NULL accessGroups is wildcard. */
1841 /* Make sure we have a string. */
1842 if (!isString(accessGroup
))
1845 /* Having the special accessGroup "*" allows access to all accessGroups. */
1846 CFRange range
= { 0, CFArrayGetCount(accessGroups
) };
1848 (CFArrayContainsValue(accessGroups
, range
, accessGroup
) ||
1849 CFArrayContainsValue(accessGroups
, range
, CFSTR("*"))))
1855 static bool itemInAccessGroup(CFDictionaryRef item
, CFArrayRef accessGroups
) {
1856 return accessGroupsAllows(accessGroups
,
1857 CFDictionaryGetValue(item
, kSecAttrAccessGroup
));
1860 static void s3dl_merge_into_dict(const void *key
, const void *value
, void *context
) {
1861 CFDictionarySetValue(context
, key
, value
);
1864 /* Return whatever the caller requested based on the value of q->q_return_type.
1865 keys and values must be 3 larger than attr_count in size to accomadate the
1866 optional data, class and persistent ref results. This is so we can use
1867 the CFDictionaryCreate() api here rather than appending to a
1868 mutable dictionary. */
1869 static CFTypeRef
handle_result(Query
*q
, CFMutableDictionaryRef item
,
1870 sqlite_int64 rowid
) {
1873 data
= CFDictionaryGetValue(item
, kSecValueData
);
1874 if (q
->q_return_type
== 0) {
1875 /* Caller isn't interested in any results at all. */
1877 } else if (q
->q_return_type
== kSecReturnDataMask
) {
1882 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
1884 } else if (q
->q_return_type
== kSecReturnPersistentRefMask
) {
1885 a_result
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
1887 /* We need to return more than one value. */
1888 if (q
->q_return_type
& kSecReturnRefMask
) {
1889 CFDictionarySetValue(item
, kSecClass
, q
->q_class
->name
);
1890 } else if ((q
->q_return_type
& kSecReturnAttributesMask
)) {
1891 if (!(q
->q_return_type
& kSecReturnDataMask
)) {
1892 CFDictionaryRemoveValue(item
, kSecValueData
);
1897 CFDictionaryRemoveAllValues(item
);
1898 if ((q
->q_return_type
& kSecReturnDataMask
) && data
) {
1899 CFDictionarySetValue(item
, kSecValueData
, data
);
1903 if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
1904 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
1905 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
1916 static CFDataRef
SecDbItemMakePersistentRef(SecDbItemRef item
, CFErrorRef
*error
) {
1917 sqlite3_int64 row_id
= SecDbItemGetRowId(item
, error
);
1919 return _SecItemMakePersistentRef(SecDbItemGetClass(item
)->name
, row_id
);
1923 static CFTypeRef
SecDbItemCopyResult(SecDbItemRef item
, ReturnTypeMask return_type
, CFErrorRef
*error
) {
1926 if (return_type
== 0) {
1927 /* Caller isn't interested in any results at all. */
1929 } else if (return_type
== kSecReturnDataMask
) {
1930 a_result
= SecDbItemGetCachedValueWithName(item
, kSecValueData
);
1932 CFRetainSafe(a_result
);
1934 a_result
= CFDataCreate(kCFAllocatorDefault
, NULL
, 0);
1936 } else if (return_type
== kSecReturnPersistentRefMask
) {
1937 a_result
= SecDbItemMakePersistentRef(item
, error
);
1939 CFMutableDictionaryRef dict
= CFDictionaryCreateMutableForCFTypes(CFGetAllocator(item
));
1940 /* We need to return more than one value. */
1941 if (return_type
& kSecReturnRefMask
) {
1942 CFDictionarySetValue(dict
, kSecClass
, SecDbItemGetClass(item
)->name
);
1944 CFOptionFlags mask
= (((return_type
& kSecReturnDataMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnDataFlag
: 0) |
1945 ((return_type
& kSecReturnAttributesMask
|| return_type
& kSecReturnRefMask
) ? kSecDbReturnAttrFlag
: 0));
1946 SecDbForEachAttr(SecDbItemGetClass(item
), desc
) {
1947 if ((desc
->flags
& mask
) != 0) {
1948 CFTypeRef value
= SecDbItemGetValue(item
, desc
, error
);
1949 if (value
&& !CFEqual(kCFNull
, value
)) {
1950 CFDictionarySetValue(dict
, desc
->name
, value
);
1951 } else if (value
== NULL
) {
1952 CFReleaseNull(dict
);
1957 if (return_type
& kSecReturnPersistentRefMask
) {
1958 CFDataRef pref
= SecDbItemMakePersistentRef(item
, error
);
1959 CFDictionarySetValue(dict
, kSecValuePersistentRef
, pref
);
1971 // MARK: Forward declarations
1973 static CFMutableDictionaryRef
1974 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
,
1975 CFArrayRef accessGroups
, keyclass_t
*keyclass
, CFErrorRef
*error
);
1977 /* AUDIT[securityd](done):
1978 attributes (ok) is a caller provided dictionary, only its cf type has
1982 s3dl_query_add(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
, CFErrorRef
*error
)
1984 if (query_match_count(q
) != 0)
1985 return errSecItemMatchUnsupported
;
1987 /* Add requires a class to be specified unless we are adding a ref. */
1988 if (q
->q_use_item_list
)
1989 return errSecUseItemListUnsupported
;
1991 /* Actual work here. */
1992 SecDbItemRef item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, q
->q_class
, q
->q_item
, KEYBAG_DEVICE
, error
);
1998 ok
= SecDbItemSetValueWithName(item
, CFSTR("v_Data"), q
->q_data
, error
);
2000 ok
= SecDbItemSetRowId(item
, q
->q_row_id
, error
);
2003 ok
= SecDbItemInsert(item
, dbt
, error
);
2005 if (result
&& q
->q_return_type
) {
2006 *result
= SecDbItemCopyResult(item
, q
->q_return_type
, error
);
2009 if (!ok
&& error
&& *error
) {
2010 if (CFEqual(CFErrorGetDomain(*error
), kSecDbErrorDomain
) && CFErrorGetCode(*error
) == SQLITE_CONSTRAINT
) {
2011 CFReleaseNull(*error
);
2012 SecError(errSecDuplicateItem
, error
, CFSTR("duplicate item %@"), item
);
2017 q
->q_changed
= true;
2018 if (SecDbItemIsSyncable(item
))
2019 q
->q_sync_changed
= true;
2022 secdebug("dbitem", "inserting item %@%s%@", item
, ok
? "" : "failed: ", ok
|| error
== NULL
? (CFErrorRef
)CFSTR("") : *error
);
2029 typedef void (*s3dl_handle_row
)(sqlite3_stmt
*stmt
, void *context
);
2031 /* Return a (mutable) dictionary if plist is a dictionary, return NULL and set error otherwise. Does nothing if plist is already NULL. */
2032 static CFMutableDictionaryRef
dictionaryFromPlist(CFPropertyListRef plist
, CFErrorRef
*error
) {
2033 if (plist
&& !isDictionary(plist
)) {
2034 CFStringRef typeName
= CFCopyTypeIDDescription(CFGetTypeID((CFTypeRef
)plist
));
2035 SecError(errSecDecode
, error
, CFSTR("plist is a %@, expecting a dictionary"), typeName
);
2036 CFReleaseSafe(typeName
);
2037 CFReleaseNull(plist
);
2039 return (CFMutableDictionaryRef
)plist
;
2042 static CFMutableDictionaryRef
s3dl_item_v2_decode(CFDataRef plain
, CFErrorRef
*error
) {
2043 CFPropertyListRef item
;
2044 item
= CFPropertyListCreateWithData(0, plain
, kCFPropertyListMutableContainers
, NULL
, error
);
2045 return dictionaryFromPlist(item
, error
);
2048 static CFMutableDictionaryRef
s3dl_item_v3_decode(CFDataRef plain
, CFErrorRef
*error
) {
2049 CFPropertyListRef item
= NULL
;
2050 const uint8_t *der
= CFDataGetBytePtr(plain
);
2051 const uint8_t *der_end
= der
+ CFDataGetLength(plain
);
2052 der
= der_decode_plist(0, kCFPropertyListMutableContainers
, &item
, error
, der
, der_end
);
2053 if (der
&& der
!= der_end
) {
2054 SecCFCreateError(errSecDecode
, kSecErrorDomain
, CFSTR("trailing garbage at end of decrypted item"), NULL
, error
);
2055 CFReleaseNull(item
);
2057 return dictionaryFromPlist(item
, error
);
2060 static CFMutableDictionaryRef
2061 s3dl_item_from_data(CFDataRef edata
, Query
*q
, CFArrayRef accessGroups
, keyclass_t
*keyclass
, CFErrorRef
*error
) {
2062 CFMutableDictionaryRef item
= NULL
;
2063 CFDataRef plain
= NULL
;
2065 /* Decrypt and decode the item and check the decoded attributes against the query. */
2067 require_quiet((ks_decrypt_data(q
->q_keybag
, keyclass
, edata
, &plain
, &version
, error
)), out
);
2073 item
= s3dl_item_v2_decode(plain
, error
);
2075 item
= s3dl_item_v3_decode(plain
, error
);
2078 if (!item
&& plain
) {
2079 secerror("decode v%d failed: %@ [item: %@]", version
, error
? *error
: NULL
, plain
);
2081 if (item
&& !itemInAccessGroup(item
, accessGroups
)) {
2082 secerror("items accessGroup %@ not in %@",
2083 CFDictionaryGetValue(item
, kSecAttrAccessGroup
),
2085 CFReleaseNull(item
);
2087 /* TODO: Validate keyclass attribute. */
2090 CFReleaseSafe(plain
);
2095 s3dl_copy_data_from_col(sqlite3_stmt
*stmt
, int col
, CFErrorRef
*error
) {
2096 return CFDataCreateWithBytesNoCopy(0, sqlite3_column_blob(stmt
, col
),
2097 sqlite3_column_bytes(stmt
, col
),
2101 static CFMutableDictionaryRef
2102 s3dl_item_from_col(sqlite3_stmt
*stmt
, Query
*q
, int col
,
2103 CFArrayRef accessGroups
, keyclass_t
*keyclass
, CFErrorRef
*error
) {
2104 CFMutableDictionaryRef item
= NULL
;
2105 CFDataRef edata
= NULL
;
2106 require(edata
= s3dl_copy_data_from_col(stmt
, col
, error
), out
);
2107 item
= s3dl_item_from_data(edata
, q
, accessGroups
, keyclass
, error
);
2110 CFReleaseSafe(edata
);
2114 struct s3dl_query_ctx
{
2116 CFArrayRef accessGroups
;
2121 static bool match_item(Query
*q
, CFArrayRef accessGroups
, CFDictionaryRef item
);
2123 static void s3dl_query_row(sqlite3_stmt
*stmt
, void *context
) {
2124 struct s3dl_query_ctx
*c
= context
;
2127 CFMutableDictionaryRef item
= s3dl_item_from_col(stmt
, q
, 1,
2128 c
->accessGroups
, NULL
, &q
->q_error
);
2129 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
2131 secerror("decode %@,rowid=%" PRId64
" failed (%ld): %@", q
->q_class
->name
, rowid
, CFErrorGetCode(q
->q_error
), q
->q_error
);
2132 // errSecDecode means the tiem is corrupted, stash it for delete.
2133 if(CFErrorGetCode(q
->q_error
)==errSecDecode
)
2135 secerror("We should attempt to delete this row (%lld)", rowid
);
2138 CFDataRef edata
= s3dl_copy_data_from_col(stmt
, 1, NULL
);
2139 CFMutableStringRef edatastring
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
2141 CFStringAppendEncryptedData(edatastring
, edata
);
2142 secnotice("item", "corrupted edata=%@", edatastring
);
2144 CFReleaseSafe(edata
);
2145 CFReleaseSafe(edatastring
);
2148 if(q
->corrupted_rows
==NULL
) {
2149 q
->corrupted_rows
=CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
);
2152 if(q
->corrupted_rows
==NULL
) {
2153 secerror("Could not create a mutable array to store corrupted row! No memory ?");
2155 long long row
=rowid
;
2156 CFNumberRef number
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberLongLongType
, &row
);
2158 secerror("Could not create a CFNumber to store corrupted row! No memory ?");
2160 CFArrayAppendValue(q
->corrupted_rows
, number
);
2161 CFReleaseNull(number
);
2162 /* Hide this error, this will just pretend the item didnt exist at all */
2163 CFReleaseNull(q
->q_error
);
2167 // q->q_error will be released appropriately by a call to query_error
2171 if (q
->q_class
== &identity_class
) {
2172 // TODO: Use col 2 for key rowid and use both rowids in persistent ref.
2173 CFMutableDictionaryRef key
= s3dl_item_from_col(stmt
, q
, 3,
2174 c
->accessGroups
, NULL
, &q
->q_error
);
2176 /* TODO : if there is a errSecDecode error here, we should cleanup */
2180 CFDataRef certData
= CFDictionaryGetValue(item
, kSecValueData
);
2182 CFDictionarySetValue(key
, CFSTR(CERTIFICATE_DATA_COLUMN_LABEL
),
2184 CFDictionaryRemoveValue(item
, kSecValueData
);
2186 CFDictionaryApplyFunction(item
, s3dl_merge_into_dict
, key
);
2191 if (!match_item(q
, c
->accessGroups
, item
))
2194 CFTypeRef a_result
= handle_result(q
, item
, rowid
);
2196 if (a_result
== kCFNull
) {
2197 /* Caller wasn't interested in a result, but we still
2198 count this row as found. */
2199 } else if (q
->q_limit
== 1) {
2200 c
->result
= a_result
;
2202 CFArrayAppendValue((CFMutableArrayRef
)c
->result
, a_result
);
2203 CFRelease(a_result
);
2213 SecDbAppendWhereROWID(CFMutableStringRef sql
,
2214 CFStringRef col
, sqlite_int64 row_id
,
2217 SecDbAppendWhereOrAnd(sql
, needWhere
);
2218 CFStringAppendFormat(sql
, NULL
, CFSTR("%@=%lld"), col
, row_id
);
2223 SecDbAppendWhereAttrs(CFMutableStringRef sql
, const Query
*q
, bool *needWhere
) {
2224 CFIndex ix
, attr_count
= query_attr_count(q
);
2225 for (ix
= 0; ix
< attr_count
; ++ix
) {
2226 SecDbAppendWhereOrAndEquals(sql
, query_attr_at(q
, ix
).key
, needWhere
);
2231 SecDbAppendWhereAccessGroups(CFMutableStringRef sql
,
2233 CFArrayRef accessGroups
,
2235 CFIndex ix
, ag_count
;
2236 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
2240 SecDbAppendWhereOrAnd(sql
, needWhere
);
2241 CFStringAppend(sql
, col
);
2242 CFStringAppend(sql
, CFSTR(" IN (?"));
2243 for (ix
= 1; ix
< ag_count
; ++ix
) {
2244 CFStringAppend(sql
, CFSTR(",?"));
2246 CFStringAppend(sql
, CFSTR(")"));
2249 static void SecDbAppendWhereClause(CFMutableStringRef sql
, const Query
*q
,
2250 CFArrayRef accessGroups
) {
2251 bool needWhere
= true;
2252 SecDbAppendWhereROWID(sql
, CFSTR("ROWID"), q
->q_row_id
, &needWhere
);
2253 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
2254 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
2257 static void SecDbAppendLimit(CFMutableStringRef sql
, CFIndex limit
) {
2258 if (limit
!= kSecMatchUnlimited
)
2259 CFStringAppendFormat(sql
, NULL
, CFSTR(" LIMIT %" PRIdCFIndex
), limit
);
2262 static CFStringRef
s3dl_select_sql(Query
*q
, CFArrayRef accessGroups
) {
2263 CFMutableStringRef sql
= CFStringCreateMutable(NULL
, 0);
2264 if (q
->q_class
== &identity_class
) {
2265 CFStringAppendFormat(sql
, NULL
, CFSTR("SELECT crowid, "
2266 CERTIFICATE_DATA_COLUMN_LABEL
", rowid,data FROM "
2267 "(SELECT cert.rowid AS crowid, cert.labl AS labl,"
2268 " cert.issr AS issr, cert.slnr AS slnr, cert.skid AS skid,"
2269 " keys.*,cert.data AS " CERTIFICATE_DATA_COLUMN_LABEL
2271 " WHERE keys.priv == 1 AND cert.pkhh == keys.klbl"));
2272 SecDbAppendWhereAccessGroups(sql
, CFSTR("cert.agrp"), accessGroups
, 0);
2273 /* The next 3 SecDbAppendWhere calls are in the same order as in
2274 SecDbAppendWhereClause(). This makes sqlBindWhereClause() work,
2275 as long as we do an extra sqlBindAccessGroups first. */
2276 SecDbAppendWhereROWID(sql
, CFSTR("crowid"), q
->q_row_id
, 0);
2277 CFStringAppend(sql
, CFSTR(")"));
2278 bool needWhere
= true;
2279 SecDbAppendWhereAttrs(sql
, q
, &needWhere
);
2280 SecDbAppendWhereAccessGroups(sql
, CFSTR("agrp"), accessGroups
, &needWhere
);
2282 CFStringAppend(sql
, CFSTR("SELECT rowid, data FROM "));
2283 CFStringAppend(sql
, q
->q_class
->name
);
2284 SecDbAppendWhereClause(sql
, q
, accessGroups
);
2286 SecDbAppendLimit(sql
, q
->q_limit
);
2291 static bool sqlBindAccessGroups(sqlite3_stmt
*stmt
, CFArrayRef accessGroups
,
2292 int *pParam
, CFErrorRef
*error
) {
2294 int param
= *pParam
;
2295 CFIndex ix
, count
= accessGroups
? CFArrayGetCount(accessGroups
) : 0;
2296 for (ix
= 0; ix
< count
; ++ix
) {
2297 result
= SecDbBindObject(stmt
, param
++,
2298 CFArrayGetValueAtIndex(accessGroups
, ix
),
2307 static bool sqlBindWhereClause(sqlite3_stmt
*stmt
, const Query
*q
,
2308 CFArrayRef accessGroups
, int *pParam
, CFErrorRef
*error
) {
2310 int param
= *pParam
;
2311 CFIndex ix
, attr_count
= query_attr_count(q
);
2312 for (ix
= 0; ix
< attr_count
; ++ix
) {
2313 result
= SecDbBindObject(stmt
, param
++, query_attr_at(q
, ix
).value
, error
);
2318 /* Bind the access group to the sql. */
2320 result
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
2327 static bool SecDbItemQuery(SecDbQueryRef query
, CFArrayRef accessGroups
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
2328 void (^handle_row
)(SecDbItemRef item
, bool *stop
)) {
2329 __block
bool ok
= true;
2330 /* Sanity check the query. */
2332 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
2334 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
2335 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
;
2338 CFStringRef sql
= s3dl_select_sql(query
, accessGroups
);
2341 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
2342 /* Bind the values being searched for to the SELECT statement. */
2344 if (query
->q_class
== &identity_class
) {
2345 /* Bind the access groups to cert.agrp. */
2346 ok
&= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
2349 ok
&= sqlBindWhereClause(stmt
, query
, accessGroups
, ¶m
, error
);
2351 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
) {
2352 SecDbItemRef item
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, query
->q_class
, stmt
, query
->q_keybag
, error
, return_attr
);
2354 if (match_item(query
, accessGroups
, item
->attributes
))
2355 handle_row(item
, stop
);
2358 secerror("failed to create item from stmt: %@", error
? *error
: (CFErrorRef
)"no error");
2360 CFReleaseNull(*error
);
2375 s3dl_query(SecDbConnectionRef dbt
, s3dl_handle_row handle_row
,
2376 void *context
, CFErrorRef
*error
)
2378 struct s3dl_query_ctx
*c
= context
;
2380 CFArrayRef accessGroups
= c
->accessGroups
;
2382 /* Sanity check the query. */
2384 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by queries"));
2386 /* Actual work here. */
2387 if (q
->q_limit
== 1) {
2390 c
->result
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2392 CFStringRef sql
= s3dl_select_sql(q
, accessGroups
);
2393 bool ok
= SecDbWithSQL(dbt
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
2395 /* Bind the values being searched for to the SELECT statement. */
2397 if (q
->q_class
== &identity_class
) {
2398 /* Bind the access groups to cert.agrp. */
2399 sql_ok
= sqlBindAccessGroups(stmt
, accessGroups
, ¶m
, error
);
2402 sql_ok
= sqlBindWhereClause(stmt
, q
, accessGroups
, ¶m
, error
);
2404 SecDbForEach(stmt
, error
, ^bool (int row_index
) {
2405 handle_row(stmt
, context
);
2406 return (!q
->q_error
) && (q
->q_limit
== kSecMatchUnlimited
|| c
->found
< q
->q_limit
);
2414 // First get the error from the query, since errSecDuplicateItem from an
2415 // update query should superceed the errSecItemNotFound below.
2416 if (!query_error(q
, error
))
2418 if (ok
&& c
->found
== 0)
2419 ok
= SecError(errSecItemNotFound
, error
, CFSTR("no matching items found"));
2425 /* Gross hack to recover from item corruption */
2427 s3dl_cleanup_corrupted(SecDbConnectionRef dbt
, Query
*q
, CFErrorRef
*error
)
2430 if(q
->corrupted_rows
==NULL
)
2433 __security_simulatecrash(CFSTR("Corrupted items found in keychain"));
2435 if (q
->q_class
== &identity_class
) {
2436 /* TODO: how to cleanup in that case */
2437 secerror("Cleaning up corrupted identities is not implemented yet");
2441 CFArrayForEach(q
->corrupted_rows
, ^(const void *value
) {
2442 CFMutableStringRef sql
=CFStringCreateMutable(kCFAllocatorDefault
, 0);
2445 secerror("Could not allocate CFString for sql, out of memory ?");
2447 CFStringAppend(sql
, CFSTR("DELETE FROM "));
2448 CFStringAppend(sql
, q
->q_class
->name
);
2449 CFStringAppendFormat(sql
, NULL
, CFSTR(" WHERE rowid=%@"), value
);
2451 secerror("Attempting cleanup with %@", sql
);
2453 if(!SecDbExec(dbt
, sql
, error
)) {
2454 secerror("Cleanup Failed using %@, error: %@", sql
, error
?NULL
:*error
);
2456 secerror("Cleanup Succeeded using %@", sql
);
2464 CFReleaseNull(q
->corrupted_rows
);
2469 s3dl_copy_matching(SecDbConnectionRef dbt
, Query
*q
, CFTypeRef
*result
,
2470 CFArrayRef accessGroups
, CFErrorRef
*error
)
2472 struct s3dl_query_ctx ctx
= {
2473 .q
= q
, .accessGroups
= accessGroups
,
2475 if (q
->q_row_id
&& query_attr_count(q
))
2476 return SecError(errSecItemIllegalQuery
, error
,
2477 CFSTR("attributes to query illegal; both row_id and other attributes can't be searched at the same time"));
2479 // Only copy things that aren't tombstones unless the client explicitly asks otherwise.
2480 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
2481 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
2482 bool ok
= s3dl_query(dbt
, s3dl_query_row
, &ctx
, error
);
2484 *result
= ctx
.result
;
2486 CFReleaseSafe(ctx
.result
);
2488 // s3dl_cleanup_corrupted(dbt, q, error);
2493 /* AUDIT[securityd](done):
2494 attributesToUpdate (ok) is a caller provided dictionary,
2495 only its cf types have been checked.
2498 s3dl_query_update(SecDbConnectionRef dbt
, Query
*q
,
2499 CFDictionaryRef attributesToUpdate
, CFArrayRef accessGroups
, CFErrorRef
*error
)
2501 /* Sanity check the query. */
2502 if (query_match_count(q
) != 0)
2503 return SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported in attributes to update"));
2505 return SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported in attributes to update"));
2506 if (q
->q_row_id
&& query_attr_count(q
))
2507 return SecError(errSecItemIllegalQuery
, error
, CFSTR("attributes to update illegal; both row_id and other attributes can't be updated at the same time"));
2509 __block
bool result
= true;
2510 Query
*u
= query_create(q
->q_class
, attributesToUpdate
, error
);
2511 if (u
== NULL
) return false;
2512 require_action_quiet(query_update_parse(u
, attributesToUpdate
, error
), errOut
, result
= false);
2513 query_pre_update(u
);
2514 result
&= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) {
2515 // Make sure we only update real items, not tombstones, unless the client explicitly asks otherwise.
2516 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
2517 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
2518 result
&= SecDbItemQuery(q
, accessGroups
, dbt
, error
, ^(SecDbItemRef item
, bool *stop
) {
2519 //We always need to know the error here.
2520 CFErrorRef localError
= NULL
;
2521 SecDbItemRef new_item
= SecDbItemCopyWithUpdates(item
, u
->q_item
, &localError
);
2522 if(SecErrorGetOSStatus(localError
)==errSecDecode
) {
2523 // We just ignore this, and treat as if item is not found
2524 secerror("Trying to update to a corrupted item");
2525 CFReleaseSafe(localError
);
2529 if (error
&& *error
== NULL
) {
2530 *error
= localError
;
2533 CFReleaseSafe(localError
);
2537 bool item_is_sync
= SecDbItemIsSyncable(item
);
2538 bool makeTombstone
= q
->q_use_tomb
? CFBooleanGetValue(q
->q_use_tomb
) : (item_is_sync
&& !SecDbItemIsTombstone(item
));
2539 result
= SecDbItemUpdate(item
, new_item
, dbt
, makeTombstone
, error
);
2541 q
->q_changed
= true;
2542 if (item_is_sync
|| SecDbItemIsSyncable(new_item
))
2543 q
->q_sync_changed
= true;
2545 CFRelease(new_item
);
2553 if (result
&& !q
->q_changed
)
2554 result
= SecError(errSecItemNotFound
, error
, CFSTR("No items updated"));
2556 if (!query_destroy(u
, error
))
2562 s3dl_query_delete(SecDbConnectionRef dbt
, Query
*q
, CFArrayRef accessGroups
, CFErrorRef
*error
)
2564 __block
bool ok
= true;
2565 // Only delete things that aren't tombstones, unless the client explicitly asks otherwise.
2566 if (!CFDictionaryContainsKey(q
->q_item
, kSecAttrTombstone
))
2567 query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
);
2568 ok
&= SecDbItemSelect(q
, dbt
, error
, ^bool(const SecDbAttr
*attr
) {
2570 },^bool(CFMutableStringRef sql
, bool *needWhere
) {
2571 SecDbAppendWhereClause(sql
, q
, accessGroups
);
2573 },^bool(sqlite3_stmt
* stmt
, int col
) {
2574 return sqlBindWhereClause(stmt
, q
, accessGroups
, &col
, error
);
2575 }, ^(SecDbItemRef item
, bool *stop
) {
2576 bool item_is_sync
= SecDbItemIsSyncable(item
);
2577 bool makeTombstone
= q
->q_use_tomb
? CFBooleanGetValue(q
->q_use_tomb
) : (item_is_sync
&& !SecDbItemIsTombstone(item
));
2578 ok
= SecDbItemDelete(item
, dbt
, makeTombstone
, error
);
2580 q
->q_changed
= true;
2582 q
->q_sync_changed
= true;
2585 if (ok
&& !q
->q_changed
) {
2586 ok
= SecError(errSecItemNotFound
, error
, CFSTR("Delete failed to delete anything"));
2591 /* Return true iff the item in question should not be backed up, nor restored,
2592 but when restoring a backup the original version of the item should be
2593 added back to the keychain again after the restore completes. */
2594 static bool SecItemIsSystemBound(CFDictionaryRef item
, const SecDbClass
*class) {
2595 CFStringRef agrp
= CFDictionaryGetValue(item
, kSecAttrAccessGroup
);
2596 if (!isString(agrp
))
2599 if (CFEqual(agrp
, CFSTR("lockdown-identities"))) {
2600 secdebug("backup", "found sys_bound item: %@", item
);
2604 if (CFEqual(agrp
, CFSTR("apple")) && class == &genp_class
) {
2605 CFStringRef service
= CFDictionaryGetValue(item
, kSecAttrService
);
2606 CFStringRef account
= CFDictionaryGetValue(item
, kSecAttrAccount
);
2607 if (isString(service
) && isString(account
) &&
2608 CFEqual(service
, CFSTR("com.apple.managedconfiguration")) &&
2609 (CFEqual(account
, CFSTR("Public")) ||
2610 CFEqual(account
, CFSTR("Private")))) {
2611 secdebug("backup", "found sys_bound item: %@", item
);
2615 secdebug("backup", "found non sys_bound item: %@", item
);
2619 /* Delete all items from the current keychain. If this is not an in
2620 place upgrade we don't delete items in the 'lockdown-identities'
2621 access group, this ensures that an import or restore of a backup
2622 will never overwrite an existing activation record. */
2623 static bool SecServerDeleteAll(SecDbConnectionRef dbt
, CFErrorRef
*error
) {
2624 return kc_transaction(dbt
, error
, ^{
2625 bool ok
= (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) &&
2626 SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) &&
2627 SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) &&
2628 SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
));
2633 struct s3dl_export_row_ctx
{
2634 struct s3dl_query_ctx qc
;
2635 keybag_handle_t dest_keybag
;
2636 enum SecItemFilter filter
;
2637 SecDbConnectionRef dbt
;
2640 static void s3dl_export_row(sqlite3_stmt
*stmt
, void *context
) {
2641 struct s3dl_export_row_ctx
*c
= context
;
2643 keyclass_t keyclass
= 0;
2644 CFErrorRef localError
= NULL
;
2646 sqlite_int64 rowid
= sqlite3_column_int64(stmt
, 0);
2647 CFMutableDictionaryRef item
= s3dl_item_from_col(stmt
, q
, 1, c
->qc
.accessGroups
, &keyclass
, &localError
);
2650 /* Only export sysbound items is do_sys_bound is true, only export non sysbound items otherwise. */
2651 bool do_sys_bound
= c
->filter
== kSecSysBoundItemFilter
;
2652 if (c
->filter
== kSecNoItemFilter
||
2653 SecItemIsSystemBound(item
, q
->q_class
) == do_sys_bound
) {
2654 /* Re-encode the item. */
2655 secdebug("item", "export rowid %llu item: %@", rowid
, item
);
2656 /* The code below could be moved into handle_row. */
2657 CFDataRef pref
= _SecItemMakePersistentRef(q
->q_class
->name
, rowid
);
2659 if (c
->dest_keybag
!= KEYBAG_NONE
) {
2660 /* Encode and encrypt the item to the specified keybag. */
2661 CFDataRef plain
= kc_plist_copy_der(item
, &q
->q_error
);
2662 CFDictionaryRemoveAllValues(item
);
2664 CFDataRef edata
= NULL
;
2665 if (ks_encrypt_data(c
->dest_keybag
, keyclass
, plain
, &edata
, &q
->q_error
)) {
2666 CFDictionarySetValue(item
, kSecValueData
, edata
);
2667 CFReleaseSafe(edata
);
2669 seccritical("ks_encrypt_data %@,rowid=%" PRId64
": failed: %@", q
->q_class
->name
, rowid
, q
->q_error
);
2670 CFReleaseNull(q
->q_error
);
2675 if (CFDictionaryGetCount(item
)) {
2676 CFDictionarySetValue(item
, kSecValuePersistentRef
, pref
);
2677 CFArrayAppendValue((CFMutableArrayRef
)c
->qc
.result
, item
);
2680 CFReleaseSafe(pref
);
2685 /* This happens a lot when trying to migrate keychain before first unlock, so only a notice */
2686 /* If the error is "corrupted item" then we just ignore it, otherwise we save it in the query */
2687 secnotice("item","Could not export item for rowid %llu: %@", rowid
, localError
);
2688 if(SecErrorGetOSStatus(localError
)==errSecDecode
) {
2689 CFReleaseNull(localError
);
2691 CFReleaseSafe(q
->q_error
);
2692 q
->q_error
=localError
;
2697 static CF_RETURNS_RETAINED CFDictionaryRef
SecServerExportKeychainPlist(SecDbConnectionRef dbt
,
2698 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
2699 enum SecItemFilter filter
, CFErrorRef
*error
) {
2700 CFMutableDictionaryRef keychain
;
2701 keychain
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2702 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2704 if (error
&& !*error
)
2705 SecError(errSecAllocate
, error
, CFSTR("Can't create keychain dictionary"));
2709 Query q
= { .q_keybag
= src_keybag
};
2710 q
.q_return_type
= kSecReturnDataMask
| kSecReturnAttributesMask
| \
2711 kSecReturnPersistentRefMask
;
2712 q
.q_limit
= kSecMatchUnlimited
;
2714 /* Get rid of this duplicate. */
2715 const SecDbClass
*SecDbClasses
[] = {
2722 for (class_ix
= 0; class_ix
< array_size(SecDbClasses
);
2724 q
.q_class
= SecDbClasses
[class_ix
];
2725 struct s3dl_export_row_ctx ctx
= {
2727 .dest_keybag
= dest_keybag
, .filter
= filter
,
2731 secnotice("item", "exporting class '%@'", q
.q_class
->name
);
2733 CFErrorRef localError
= NULL
;
2734 if (s3dl_query(dbt
, s3dl_export_row
, &ctx
, &localError
)) {
2735 if (CFArrayGetCount(ctx
.qc
.result
))
2736 CFDictionaryAddValue(keychain
, q
.q_class
->name
, ctx
.qc
.result
);
2739 OSStatus status
= (OSStatus
)CFErrorGetCode(localError
);
2740 if (status
== errSecItemNotFound
) {
2741 CFRelease(localError
);
2743 secerror("Export failed: %@", localError
);
2745 CFReleaseSafe(*error
);
2746 *error
= localError
;
2748 CFRelease(localError
);
2750 CFReleaseNull(keychain
);
2754 CFReleaseNull(ctx
.qc
.result
);
2760 static CF_RETURNS_RETAINED CFDataRef
SecServerExportKeychain(SecDbConnectionRef dbt
,
2761 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
, CFErrorRef
*error
) {
2762 CFDataRef data_out
= NULL
;
2763 /* Export everything except the items for which SecItemIsSystemBound()
2765 CFDictionaryRef keychain
= SecServerExportKeychainPlist(dbt
,
2766 src_keybag
, dest_keybag
, kSecBackupableItemFilter
,
2769 data_out
= CFPropertyListCreateData(kCFAllocatorDefault
, keychain
,
2770 kCFPropertyListBinaryFormat_v1_0
,
2772 CFRelease(keychain
);
2778 struct SecServerImportClassState
{
2779 SecDbConnectionRef dbt
;
2781 keybag_handle_t src_keybag
;
2782 keybag_handle_t dest_keybag
;
2783 enum SecItemFilter filter
;
2786 struct SecServerImportItemState
{
2787 const SecDbClass
*class;
2788 struct SecServerImportClassState
*s
;
2791 /* Infer accessibility and access group for pre-v2 (iOS4.x and earlier) items
2792 being imported from a backup. */
2793 static bool SecDbItemImportMigrate(SecDbItemRef item
, CFErrorRef
*error
) {
2795 CFStringRef agrp
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessGroup
);
2796 CFStringRef accessible
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessible
);
2798 if (!isString(agrp
) || !isString(accessible
))
2800 if (SecDbItemGetClass(item
) == &genp_class
&& CFEqual(accessible
, kSecAttrAccessibleAlways
)) {
2801 CFStringRef svce
= SecDbItemGetCachedValueWithName(item
, kSecAttrService
);
2802 if (!isString(svce
)) return ok
;
2803 if (CFEqual(agrp
, CFSTR("apple"))) {
2804 if (CFEqual(svce
, CFSTR("AirPort"))) {
2805 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleAfterFirstUnlock
, error
);
2806 } else if (CFEqual(svce
, CFSTR("com.apple.airplay.password"))) {
2807 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, error
);
2808 } else if (CFEqual(svce
, CFSTR("YouTube"))) {
2809 ok
= (SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, error
) &&
2810 SecDbItemSetValueWithName(item
, kSecAttrAccessGroup
, CFSTR("com.apple.youtube.credentials"), error
));
2812 CFStringRef desc
= SecDbItemGetCachedValueWithName(item
, kSecAttrDescription
);
2813 if (!isString(desc
)) return ok
;
2814 if (CFEqual(desc
, CFSTR("IPSec Shared Secret")) || CFEqual(desc
, CFSTR("PPP Password"))) {
2815 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleAfterFirstUnlock
, error
);
2819 } else if (SecDbItemGetClass(item
) == &inet_class
&& CFEqual(accessible
, kSecAttrAccessibleAlways
)) {
2820 if (CFEqual(agrp
, CFSTR("PrintKitAccessGroup"))) {
2821 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, error
);
2822 } else if (CFEqual(agrp
, CFSTR("apple"))) {
2823 CFTypeRef ptcl
= SecDbItemGetCachedValueWithName(item
, kSecAttrProtocol
);
2824 bool is_proxy
= false;
2825 if (isNumber(ptcl
)) {
2827 CFNumberGetValue(ptcl
, kCFNumberSInt32Type
, &iptcl
);
2828 is_proxy
= (iptcl
== FOUR_CHAR_CODE('htpx') ||
2829 iptcl
== FOUR_CHAR_CODE('htsx') ||
2830 iptcl
== FOUR_CHAR_CODE('ftpx') ||
2831 iptcl
== FOUR_CHAR_CODE('rtsx') ||
2832 iptcl
== FOUR_CHAR_CODE('xpth') ||
2833 iptcl
== FOUR_CHAR_CODE('xsth') ||
2834 iptcl
== FOUR_CHAR_CODE('xptf') ||
2835 iptcl
== FOUR_CHAR_CODE('xstr'));
2836 } else if (isString(ptcl
)) {
2837 is_proxy
= (CFEqual(ptcl
, kSecAttrProtocolHTTPProxy
) ||
2838 CFEqual(ptcl
, kSecAttrProtocolHTTPSProxy
) ||
2839 CFEqual(ptcl
, kSecAttrProtocolRTSPProxy
) ||
2840 CFEqual(ptcl
, kSecAttrProtocolFTPProxy
));
2843 ok
= SecDbItemSetValueWithName(item
, kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, error
);
2849 bool SecDbItemDecrypt(SecDbItemRef item
, CFDataRef edata
, CFErrorRef
*error
) {
2851 CFDataRef pdata
= NULL
;
2852 keyclass_t keyclass
;
2854 ok
= ks_decrypt_data(SecDbItemGetKeybag(item
), &keyclass
, edata
, &pdata
, &version
, error
);
2859 /* Old V4 style keychain backup being imported. */
2860 ok
= SecDbItemSetValueWithName(item
, CFSTR("v_Data"), pdata
, error
) &&
2861 SecDbItemImportMigrate(item
, error
);
2863 CFDictionaryRef dict
;
2865 dict
= s3dl_item_v2_decode(pdata
, error
);
2867 dict
= s3dl_item_v3_decode(pdata
, error
);
2869 ok
= dict
&& SecDbItemSetValues(item
, dict
, error
);
2870 CFReleaseSafe(dict
);
2873 CFReleaseSafe(pdata
);
2875 keyclass_t my_keyclass
= SecDbItemGetKeyclass(item
, NULL
);
2877 ok
= ok
&& SecDbItemSetKeyclass(item
, keyclass
, error
);
2879 /* Make sure the keyclass in the dictionary matched what we got
2880 back from decoding the data blob. */
2881 if (my_keyclass
!= keyclass
) {
2882 ok
= SecError(errSecDecode
, error
, CFSTR("keyclass attribute %d doesn't match keyclass in blob %d"), my_keyclass
, keyclass
);
2889 /* Automagically make a item syncable, based on various attributes. */
2890 static bool SecDbItemInferSyncable(SecDbItemRef item
, CFErrorRef
*error
)
2892 CFStringRef agrp
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessGroup
);
2894 if (!isString(agrp
))
2897 if (CFEqual(agrp
, CFSTR("com.apple.cfnetwork")) && SecDbItemGetClass(item
) == &inet_class
) {
2898 CFTypeRef srvr
= SecDbItemGetCachedValueWithName(item
, kSecAttrServer
);
2899 CFTypeRef ptcl
= SecDbItemGetCachedValueWithName(item
, kSecAttrProtocol
);
2900 CFTypeRef atyp
= SecDbItemGetCachedValueWithName(item
, kSecAttrAuthenticationType
);
2902 if (isString(srvr
) && isString(ptcl
) && isString(atyp
)) {
2903 /* This looks like a Mobile Safari Password, make syncable */
2904 secnotice("item", "Make this item syncable: %@", item
);
2905 return SecDbItemSetSyncable(item
, true, error
);
2912 /* This create a SecDbItem from the item dictionnary that are exported for backups.
2913 Item are stored in the backup as a dictionary containing two keys:
2914 - v_Data: the encrypted data blob
2915 - v_PersistentRef: a persistent Ref.
2916 src_keybag is normally the backup keybag.
2917 dst_keybag is normally the device keybag.
2919 static SecDbItemRef
SecDbItemCreateWithBackupDictionary(CFAllocatorRef allocator
, const SecDbClass
*dbclass
, CFDictionaryRef dict
, keybag_handle_t src_keybag
, keybag_handle_t dst_keybag
, CFErrorRef
*error
)
2921 CFDataRef edata
= CFDictionaryGetValue(dict
, CFSTR("v_Data"));
2922 SecDbItemRef item
= NULL
;
2925 item
= SecDbItemCreateWithEncryptedData(kCFAllocatorDefault
, dbclass
, edata
, src_keybag
, error
);
2927 if (!SecDbItemSetKeybag(item
, dst_keybag
, error
))
2928 CFReleaseNull(item
);
2930 SecError(errSecDecode
, error
, CFSTR("No v_Data in backup dictionary %@"), dict
);
2936 static bool SecDbItemExtractRowIdFromBackupDictionary(SecDbItemRef item
, CFDictionaryRef dict
, CFErrorRef
*error
) {
2937 CFDataRef ref
= CFDictionaryGetValue(dict
, CFSTR("v_PersistentRef"));
2939 return SecError(errSecDecode
, error
, CFSTR("No v_PersistentRef in backup dictionary %@"), dict
);
2941 CFStringRef className
;
2942 sqlite3_int64 rowid
;
2943 if (!_SecItemParsePersistentRef(ref
, &className
, &rowid
))
2944 return SecError(errSecDecode
, error
, CFSTR("v_PersistentRef %@ failed to decode"), ref
);
2946 if (!CFEqual(SecDbItemGetClass(item
)->name
, className
))
2947 return SecError(errSecDecode
, error
, CFSTR("v_PersistentRef has unexpected class %@"), className
);
2949 return SecDbItemSetRowId(item
, rowid
, error
);
2952 static void SecServerImportItem(const void *value
, void *context
) {
2953 struct SecServerImportItemState
*state
=
2954 (struct SecServerImportItemState
*)context
;
2955 if (state
->s
->error
)
2957 if (!isDictionary(value
)) {
2958 SecError(errSecParam
, &state
->s
->error
, CFSTR("value %@ is not a dictionary"), value
);
2962 CFDictionaryRef dict
= (CFDictionaryRef
)value
;
2964 secdebug("item", "Import Item : %@", dict
);
2966 /* We don't filter non sys_bound items during import since we know we
2967 will never have any in this case, we use the kSecSysBoundItemFilter
2968 to indicate that we don't preserve rowid's during import instead. */
2969 if (state
->s
->filter
== kSecBackupableItemFilter
&&
2970 SecItemIsSystemBound(dict
, state
->class))
2975 /* This is sligthly confusing:
2976 - During upgrade all items are exported with KEYBAG_NONE.
2977 - During restore from backup, existing sys_bound items are exported with KEYBAG_NONE, and are exported as dictionary of attributes.
2978 - Item in the actual backup are export with a real keybag, and are exported as encrypted v_Data and v_PersistentRef
2980 if (state
->s
->src_keybag
== KEYBAG_NONE
) {
2981 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, state
->class, dict
, state
->s
->dest_keybag
, &state
->s
->error
);
2983 item
= SecDbItemCreateWithBackupDictionary(kCFAllocatorDefault
, state
->class, dict
, state
->s
->src_keybag
, state
->s
->dest_keybag
, &state
->s
->error
);
2987 if(state
->s
->filter
!= kSecSysBoundItemFilter
) {
2988 SecDbItemExtractRowIdFromBackupDictionary(item
, dict
, &state
->s
->error
);
2990 SecDbItemInferSyncable(item
, &state
->s
->error
);
2991 SecDbItemInsert(item
, state
->s
->dbt
, &state
->s
->error
);
2994 /* Reset error if we had one, since we just skip the current item
2995 and continue importing what we can. */
2996 if (state
->s
->error
) {
2997 secwarning("Failed to import an item (%@) of class '%@': %@ - ignoring error.",
2998 item
, state
->class->name
, state
->s
->error
);
2999 CFReleaseNull(state
->s
->error
);
3002 CFReleaseSafe(item
);
3005 static void SecServerImportClass(const void *key
, const void *value
,
3007 struct SecServerImportClassState
*state
=
3008 (struct SecServerImportClassState
*)context
;
3011 if (!isString(key
)) {
3012 SecError(errSecParam
, &state
->error
, CFSTR("class name %@ is not a string"), key
);
3015 const SecDbClass
*class = kc_class_with_name(key
);
3016 if (!class || class == &identity_class
) {
3017 SecError(errSecParam
, &state
->error
, CFSTR("attempt to import an identity"));
3020 struct SecServerImportItemState item_state
= {
3021 .class = class, .s
= state
3023 if (isArray(value
)) {
3024 CFArrayRef items
= (CFArrayRef
)value
;
3025 CFArrayApplyFunction(items
, CFRangeMake(0, CFArrayGetCount(items
)),
3026 SecServerImportItem
, &item_state
);
3028 CFDictionaryRef item
= (CFDictionaryRef
)value
;
3029 SecServerImportItem(item
, &item_state
);
3033 static bool SecServerImportKeychainInPlist(SecDbConnectionRef dbt
,
3034 keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
,
3035 CFDictionaryRef keychain
, enum SecItemFilter filter
, CFErrorRef
*error
) {
3038 CFDictionaryRef sys_bound
= NULL
;
3039 if (filter
== kSecBackupableItemFilter
) {
3040 /* Grab a copy of all the items for which SecItemIsSystemBound()
3042 require(sys_bound
= SecServerExportKeychainPlist(dbt
, KEYBAG_DEVICE
,
3043 KEYBAG_NONE
, kSecSysBoundItemFilter
,
3047 /* Delete everything in the keychain. */
3048 require(ok
= SecServerDeleteAll(dbt
, error
), errOut
);
3050 struct SecServerImportClassState state
= {
3052 .src_keybag
= src_keybag
,
3053 .dest_keybag
= dest_keybag
,
3056 /* Import the provided items, preserving rowids. */
3057 CFDictionaryApplyFunction(keychain
, SecServerImportClass
, &state
);
3060 state
.src_keybag
= KEYBAG_NONE
;
3061 /* Import the items we preserved with random rowids. */
3062 state
.filter
= kSecSysBoundItemFilter
;
3063 CFDictionaryApplyFunction(sys_bound
, SecServerImportClass
, &state
);
3064 CFRelease(sys_bound
);
3068 CFReleaseSafe(*error
);
3069 *error
= state
.error
;
3071 CFRelease(state
.error
);
3080 static bool SecServerImportKeychain(SecDbConnectionRef dbt
,
3081 keybag_handle_t src_keybag
,
3082 keybag_handle_t dest_keybag
, CFDataRef data
, CFErrorRef
*error
) {
3083 return kc_transaction(dbt
, error
, ^{
3085 CFDictionaryRef keychain
;
3086 keychain
= CFPropertyListCreateWithData(kCFAllocatorDefault
, data
,
3087 kCFPropertyListImmutable
, NULL
,
3090 if (isDictionary(keychain
)) {
3091 ok
= SecServerImportKeychainInPlist(dbt
, src_keybag
,
3092 dest_keybag
, keychain
,
3093 kSecBackupableItemFilter
,
3096 ok
= SecError(errSecParam
, error
, CFSTR("import: keychain is not a dictionary"));
3098 CFRelease(keychain
);
3104 static bool ks_open_keybag(CFDataRef keybag
, CFDataRef password
, keybag_handle_t
*handle
, CFErrorRef
*error
) {
3106 kern_return_t kernResult
;
3107 kernResult
= aks_load_bag(CFDataGetBytePtr(keybag
), (int)CFDataGetLength(keybag
), handle
);
3109 return SecKernError(kernResult
, error
, CFSTR("aks_load_bag failed: %@"), keybag
);
3112 kernResult
= aks_unlock_bag(*handle
, CFDataGetBytePtr(password
), (int)CFDataGetLength(password
));
3114 aks_unload_bag(*handle
);
3115 return SecKernError(kernResult
, error
, CFSTR("aks_unlock_bag failed"));
3119 #else /* !USE_KEYSTORE */
3120 *handle
= KEYBAG_NONE
;
3122 #endif /* USE_KEYSTORE */
3125 static bool ks_close_keybag(keybag_handle_t keybag
, CFErrorRef
*error
) {
3127 IOReturn kernResult
= aks_unload_bag(keybag
);
3129 return SecKernError(kernResult
, error
, CFSTR("aks_unload_bag failed"));
3131 #endif /* USE_KEYSTORE */
3135 static CF_RETURNS_RETAINED CFDataRef
SecServerKeychainBackup(SecDbConnectionRef dbt
, CFDataRef keybag
,
3136 CFDataRef password
, CFErrorRef
*error
) {
3137 CFDataRef backup
= NULL
;
3138 keybag_handle_t backup_keybag
;
3139 if (ks_open_keybag(keybag
, password
, &backup_keybag
, error
)) {
3140 /* Export from system keybag to backup keybag. */
3141 backup
= SecServerExportKeychain(dbt
, KEYBAG_DEVICE
, backup_keybag
, error
);
3142 if (!ks_close_keybag(backup_keybag
, error
)) {
3143 CFReleaseNull(backup
);
3149 static bool SecServerKeychainRestore(SecDbConnectionRef dbt
, CFDataRef backup
,
3150 CFDataRef keybag
, CFDataRef password
, CFErrorRef
*error
) {
3151 keybag_handle_t backup_keybag
;
3152 if (!ks_open_keybag(keybag
, password
, &backup_keybag
, error
))
3155 /* Import from backup keybag to system keybag. */
3156 bool ok
= SecServerImportKeychain(dbt
, backup_keybag
, KEYBAG_DEVICE
,
3158 ok
&= ks_close_keybag(backup_keybag
, error
);
3164 // MARK - External SPI support code.
3166 CFStringRef
__SecKeychainCopyPath(void) {
3167 CFStringRef kcRelPath
= NULL
;
3169 kcRelPath
= CFSTR("keychain-2.db");
3171 kcRelPath
= CFSTR("keychain-2-debug.db");
3174 CFStringRef kcPath
= NULL
;
3175 CFURLRef kcURL
= SecCopyURLForFileInKeychainDirectory(kcRelPath
);
3177 kcPath
= CFURLCopyFileSystemPath(kcURL
, kCFURLPOSIXPathStyle
);
3185 // MARK: kc_dbhandle init and reset
3187 static SecDbRef
SecKeychainDbCreate(CFStringRef path
) {
3188 return SecDbCreate(path
, ^bool (SecDbConnectionRef dbconn
, bool didCreate
, CFErrorRef
*localError
) {
3191 ok
= s3dl_dbt_upgrade_from_version(dbconn
, 0, localError
);
3193 ok
= s3dl_dbt_upgrade(dbconn
, localError
);
3196 secerror("Upgrade %sfailed: %@", didCreate
? "from v0 " : "", localError
? *localError
: NULL
);
3202 static SecDbRef _kc_dbhandle
= NULL
;
3204 static void kc_dbhandle_init(void) {
3205 SecDbRef oldHandle
= _kc_dbhandle
;
3206 _kc_dbhandle
= NULL
;
3207 CFStringRef dbPath
= __SecKeychainCopyPath();
3209 _kc_dbhandle
= SecKeychainDbCreate(dbPath
);
3213 secerror("replaced %@ with %@", oldHandle
, _kc_dbhandle
);
3214 CFRelease(oldHandle
);
3218 static dispatch_once_t _kc_dbhandle_once
;
3220 static SecDbRef
kc_dbhandle(void) {
3221 dispatch_once(&_kc_dbhandle_once
, ^{
3224 return _kc_dbhandle
;
3227 /* For whitebox testing only */
3228 void kc_dbhandle_reset(void);
3229 void kc_dbhandle_reset(void)
3231 __block
bool done
= false;
3232 dispatch_once(&_kc_dbhandle_once
, ^{
3236 // TODO: Not thread safe at all! - FOR DEBUGGING ONLY
3241 static SecDbConnectionRef
kc_aquire_dbt(bool writeAndRead
, CFErrorRef
*error
) {
3242 return SecDbConnectionAquire(kc_dbhandle(), !writeAndRead
, error
);
3245 /* Return a per thread dbt handle for the keychain. If create is true create
3246 the database if it does not yet exist. If it is false, just return an
3247 error if it fails to auto-create. */
3248 static bool kc_with_dbt(bool writeAndRead
, CFErrorRef
*error
, bool (^perform
)(SecDbConnectionRef dbt
))
3251 SecDbConnectionRef dbt
= kc_aquire_dbt(writeAndRead
, error
);
3254 SecDbConnectionRelease(dbt
);
3260 items_matching_issuer_parent(CFArrayRef accessGroups
,
3261 CFDataRef issuer
, CFArrayRef issuers
, int recurse
)
3264 CFArrayRef results
= NULL
;
3265 SecDbConnectionRef dbt
= NULL
;
3269 if (CFArrayContainsValue(issuers
, CFRangeMake(0, CFArrayGetCount(issuers
)), issuer
))
3272 const void *keys
[] = { kSecClass
, kSecReturnRef
, kSecAttrSubject
};
3273 const void *vals
[] = { kSecClassCertificate
, kCFBooleanTrue
, issuer
};
3274 CFDictionaryRef query
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, vals
, array_size(keys
), NULL
, NULL
);
3279 CFErrorRef localError
= NULL
;
3280 q
= query_create_with_limit(query
, kSecMatchUnlimited
, &localError
);
3283 if ((dbt
= SecDbConnectionAquire(kc_dbhandle(), true, &localError
))) {
3284 s3dl_copy_matching(dbt
, q
, (CFTypeRef
*)&results
, accessGroups
, &localError
);
3285 SecDbConnectionRelease(dbt
);
3287 query_destroy(q
, &localError
);
3290 secerror("items matching issuer parent: %@", localError
);
3291 CFReleaseNull(localError
);
3295 count
= CFArrayGetCount(results
);
3296 for (i
= 0; (i
< count
) && !found
; i
++) {
3297 CFDictionaryRef cert_dict
= (CFDictionaryRef
)CFArrayGetValueAtIndex(results
, i
);
3298 CFDataRef cert_issuer
= CFDictionaryGetValue(cert_dict
, kSecAttrIssuer
);
3299 if (CFEqual(cert_issuer
, issuer
))
3302 found
= items_matching_issuer_parent(accessGroups
, cert_issuer
, issuers
, recurse
);
3309 static bool match_item(Query
*q
, CFArrayRef accessGroups
, CFDictionaryRef item
)
3311 if (q
->q_match_issuer
) {
3312 CFDataRef issuer
= CFDictionaryGetValue(item
, kSecAttrIssuer
);
3313 if (!items_matching_issuer_parent(accessGroups
, issuer
, q
->q_match_issuer
, 10 /*max depth*/))
3317 /* Add future match checks here. */
3322 /****************************************************************************
3323 **************** Beginning of Externally Callable Interface ****************
3324 ****************************************************************************/
3327 // TODO Use as a safety wrapper
3328 static bool SecErrorWith(CFErrorRef
*in_error
, bool (^perform
)(CFErrorRef
*error
)) {
3329 CFErrorRef error
= in_error
? *in_error
: NULL
;
3331 if ((ok
= perform(&error
))) {
3332 assert(error
== NULL
);
3334 secerror("error + success: %@", error
);
3337 OSStatus status
= SecErrorGetOSStatus(error
);
3338 if (status
!= errSecItemNotFound
) // Occurs in normal operation, so exclude
3339 secerror("error:[%" PRIdOSStatus
"] %@", status
, error
);
3343 CFReleaseNull(error
);
3350 /* AUDIT[securityd](done):
3351 query (ok) is a caller provided dictionary, only its cf type has been checked.
3354 SecItemServerCopyMatching(CFDictionaryRef query
, CFTypeRef
*result
,
3355 CFArrayRef accessGroups
, CFErrorRef
*error
)
3358 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
3359 return SecError(errSecMissingEntitlement
, error
,
3360 CFSTR("client has neither application-identifier nor keychain-access-groups entitlements"));
3363 if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*"))) {
3364 /* Having the special accessGroup "*" allows access to all accessGroups. */
3365 accessGroups
= NULL
;
3369 Query
*q
= query_create_with_limit(query
, 1, error
);
3371 CFStringRef agrp
= CFDictionaryGetValue(q
->q_item
, kSecAttrAccessGroup
);
3372 if (agrp
&& accessGroupsAllows(accessGroups
, agrp
)) {
3373 // TODO: Return an error if agrp is not NULL and accessGroupsAllows() fails above.
3374 const void *val
= agrp
;
3375 accessGroups
= CFArrayCreate(0, &val
, 1, &kCFTypeArrayCallBacks
);
3377 CFRetainSafe(accessGroups
);
3380 /* Sanity check the query. */
3381 if (q
->q_use_item_list
) {
3382 ok
= SecError(errSecUseItemListUnsupported
, error
, CFSTR("use item list unsupported"));
3383 #if defined(MULTIPLE_KEYCHAINS)
3384 } else if (q
->q_use_keychain
) {
3385 ok
= SecError(errSecUseKeychainUnsupported
, error
, CFSTR("use keychain list unsupported"));
3387 } else if (q
->q_match_issuer
&& ((q
->q_class
!= &cert_class
) &&
3388 (q
->q_class
!= &identity_class
))) {
3389 ok
= SecError(errSecUnsupportedOperation
, error
, CFSTR("unsupported match attribute"));
3390 } else if (q
->q_return_type
!= 0 && result
== NULL
) {
3391 ok
= SecError(errSecReturnMissingPointer
, error
, CFSTR("missing pointer"));
3392 } else if (!q
->q_error
) {
3393 ok
= kc_with_dbt(false, error
, ^(SecDbConnectionRef dbt
) {
3394 return s3dl_copy_matching(dbt
, q
, result
, accessGroups
, error
);
3398 CFReleaseSafe(accessGroups
);
3399 if (!query_destroy(q
, error
))
3407 _SecItemCopyMatching(CFDictionaryRef query
, CFArrayRef accessGroups
, CFTypeRef
*result
, CFErrorRef
*error
) {
3408 return SecItemServerCopyMatching(query
, result
, accessGroups
, error
);
3411 /* AUDIT[securityd](done):
3412 attributes (ok) is a caller provided dictionary, only its cf type has
3416 _SecItemAdd(CFDictionaryRef attributes
, CFArrayRef accessGroups
,
3417 CFTypeRef
*result
, CFErrorRef
*error
)
3421 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
)))
3422 return SecError(errSecMissingEntitlement
, error
,
3423 CFSTR("client has neither application-identifier nor keychain-access-groups entitlements"));
3425 Query
*q
= query_create_with_limit(attributes
, 0, error
);
3427 /* Access group sanity checking. */
3428 CFStringRef agrp
= (CFStringRef
)CFDictionaryGetValue(attributes
,
3429 kSecAttrAccessGroup
);
3431 CFArrayRef ag
= accessGroups
;
3432 /* Having the special accessGroup "*" allows access to all accessGroups. */
3433 if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*")))
3434 accessGroups
= NULL
;
3437 /* The user specified an explicit access group, validate it. */
3438 if (!accessGroupsAllows(accessGroups
, agrp
))
3439 return SecError(errSecNoAccessForItem
, error
, CFSTR("NoAccessForItem"));
3441 agrp
= (CFStringRef
)CFArrayGetValueAtIndex(ag
, 0);
3443 /* We are using an implicit access group, add it as if the user
3444 specified it as an attribute. */
3445 query_add_attribute(kSecAttrAccessGroup
, agrp
, q
);
3448 query_ensure_keyclass(q
, agrp
);
3451 ok
= SecError(errSecValuePersistentRefUnsupported
, error
, CFSTR("q_row_id")); // TODO: better error string
3452 #if defined(MULTIPLE_KEYCHAINS)
3453 else if (q
->q_use_keychain_list
)
3454 ok
= SecError(errSecUseKeychainListUnsupported
, error
, CFSTR("q_use_keychain_list")); // TODO: better error string;
3456 else if (!q
->q_error
) {
3457 ok
= kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
){
3458 return kc_transaction(dbt
, error
, ^{
3459 query_pre_add(q
, true);
3460 return s3dl_query_add(dbt
, q
, result
, error
);
3464 ok
= query_notify_and_destroy(q
, ok
, error
);
3471 /* AUDIT[securityd](done):
3472 query (ok) and attributesToUpdate (ok) are a caller provided dictionaries,
3473 only their cf types have been checked.
3476 _SecItemUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
,
3477 CFArrayRef accessGroups
, CFErrorRef
*error
)
3480 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
3481 return SecError(errSecMissingEntitlement
, error
,
3482 CFSTR("client has neither application-identifier nor keychain-access-groups entitlements"));
3485 if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*"))) {
3486 /* Having the special accessGroup "*" allows access to all accessGroups. */
3487 accessGroups
= NULL
;
3491 Query
*q
= query_create_with_limit(query
, kSecMatchUnlimited
, error
);
3496 /* Sanity check the query. */
3497 if (q
->q_use_item_list
) {
3498 ok
= SecError(errSecUseItemListUnsupported
, error
, CFSTR("use item list not supported"));
3499 } else if (q
->q_return_type
& kSecReturnDataMask
) {
3500 /* Update doesn't return anything so don't ask for it. */
3501 ok
= SecError(errSecReturnDataUnsupported
, error
, CFSTR("return data not supported by update"));
3502 } else if (q
->q_return_type
& kSecReturnAttributesMask
) {
3503 ok
= SecError(errSecReturnAttributesUnsupported
, error
, CFSTR("return attributes not supported by update"));
3504 } else if (q
->q_return_type
& kSecReturnRefMask
) {
3505 ok
= SecError(errSecReturnRefUnsupported
, error
, CFSTR("return ref not supported by update"));
3506 } else if (q
->q_return_type
& kSecReturnPersistentRefMask
) {
3507 ok
= SecError(errSecReturnPersitentRefUnsupported
, error
, CFSTR("return persistent ref not supported by update"));
3509 /* Access group sanity checking. */
3510 CFStringRef agrp
= (CFStringRef
)CFDictionaryGetValue(attributesToUpdate
,
3511 kSecAttrAccessGroup
);
3513 /* The user is attempting to modify the access group column,
3514 validate it to make sure the new value is allowable. */
3515 if (!accessGroupsAllows(accessGroups
, agrp
)) {
3516 ok
= SecError(errSecNoAccessForItem
, error
, CFSTR("accessGroup %@ not in %@"), agrp
, accessGroups
);
3522 if (!q
->q_use_tomb
&& SOSCCThisDeviceDefinitelyNotActiveInCircle()) {
3523 q
->q_use_tomb
= kCFBooleanFalse
;
3525 ok
= kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
) {
3526 return s3dl_query_update(dbt
, q
, attributesToUpdate
, accessGroups
, error
);
3530 ok
= query_notify_and_destroy(q
, ok
, error
);
3536 /* AUDIT[securityd](done):
3537 query (ok) is a caller provided dictionary, only its cf type has been checked.
3540 _SecItemDelete(CFDictionaryRef query
, CFArrayRef accessGroups
, CFErrorRef
*error
)
3543 if (!accessGroups
|| 0 == (ag_count
= CFArrayGetCount(accessGroups
))) {
3544 return SecError(errSecMissingEntitlement
, error
,
3545 CFSTR("client has neither application-identifier nor keychain-access-groups entitlements"));
3548 if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*"))) {
3549 /* Having the special accessGroup "*" allows access to all accessGroups. */
3550 accessGroups
= NULL
;
3553 Query
*q
= query_create_with_limit(query
, kSecMatchUnlimited
, error
);
3556 /* Sanity check the query. */
3557 if (q
->q_limit
!= kSecMatchUnlimited
)
3558 ok
= SecError(errSecMatchLimitUnsupported
, error
, CFSTR("match limit not supported by delete"));
3559 else if (query_match_count(q
) != 0)
3560 ok
= SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported by delete"));
3562 ok
= SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by delete"));
3563 else if (q
->q_row_id
&& query_attr_count(q
))
3564 ok
= SecError(errSecItemIllegalQuery
, error
, CFSTR("rowid and other attributes are mutually exclusive"));
3566 if (!q
->q_use_tomb
&& SOSCCThisDeviceDefinitelyNotActiveInCircle()) {
3567 q
->q_use_tomb
= kCFBooleanFalse
;
3569 ok
= kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
) {
3570 return s3dl_query_delete(dbt
, q
, accessGroups
, error
);
3573 ok
= query_notify_and_destroy(q
, ok
, error
);
3581 /* AUDIT[securityd](done):
3582 No caller provided inputs.
3585 SecItemServerDeleteAll(CFErrorRef
*error
) {
3586 return kc_with_dbt(true, error
, ^bool (SecDbConnectionRef dbt
) {
3587 return (kc_transaction(dbt
, error
, ^bool {
3588 return (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) &&
3589 SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) &&
3590 SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) &&
3591 SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
));
3592 }) && SecDbExec(dbt
, CFSTR("VACUUM;"), error
));
3597 _SecItemDeleteAll(CFErrorRef
*error
) {
3598 return SecItemServerDeleteAll(error
);
3602 _SecServerKeychainBackup(CFDataRef keybag
, CFDataRef passcode
, CFErrorRef
*error
) {
3604 SecDbConnectionRef dbt
= SecDbConnectionAquire(kc_dbhandle(), false, error
);
3609 if (keybag
== NULL
&& passcode
== NULL
) {
3611 backup
= SecServerExportKeychain(dbt
, KEYBAG_DEVICE
, backup_keybag_handle
, error
);
3612 #else /* !USE_KEYSTORE */
3613 SecError(errSecParam
, error
, CFSTR("Why are you doing this?"));
3615 #endif /* USE_KEYSTORE */
3617 backup
= SecServerKeychainBackup(dbt
, keybag
, passcode
, error
);
3620 SecDbConnectionRelease(dbt
);
3626 _SecServerKeychainRestore(CFDataRef backup
, CFDataRef keybag
, CFDataRef passcode
, CFErrorRef
*error
) {
3627 if (backup
== NULL
|| keybag
== NULL
)
3628 return SecError(errSecParam
, error
, CFSTR("backup or keybag missing"));
3630 __block
bool ok
= true;
3631 ok
&= SecDbPerformWrite(kc_dbhandle(), error
, ^(SecDbConnectionRef dbconn
) {
3632 ok
= SecServerKeychainRestore(dbconn
, backup
, keybag
, passcode
, error
);
3636 SecKeychainChanged(true);
3650 static CFStringRef kSecItemDataSourceErrorDomain
= CFSTR("com.apple.secitem.datasource");
3653 kSecObjectMallocFailed
= 1,
3654 kSecAddDuplicateEntry
,
3655 kSecObjectNotFoundError
,
3656 kSOSAccountCreationFailed
,
3659 typedef struct SecItemDataSource
*SecItemDataSourceRef
;
3661 struct SecItemDataSource
{
3662 struct SOSDataSource ds
;
3665 SecDbConnectionRef _dbconn
;
3670 struct SOSDigestVector dv
;
3671 struct SOSDigestVector toadd
;
3672 struct SOSDigestVector todel
;
3673 SOSManifestRef manifest
;
3674 uint8_t manifest_digest
[SOSDigestSize
];
3676 bool syncWithPeersWhenDone
;
3679 static SecDbConnectionRef
SecItemDataSourceGetConnection(SecItemDataSourceRef ds
, CFErrorRef
*error
) {
3681 ds
->_dbconn
= SecDbConnectionAquire(ds
->db
, ds
->readOnly
, error
);
3683 ds
->changed
= false;
3685 secerror("SecDbConnectionAquire failed: %@", error
? *error
: NULL
);
3691 static bool SecItemDataSourceRecordUpdate(SecItemDataSourceRef ds
, SecDbItemRef deleted
, SecDbItemRef inserted
, CFErrorRef
*error
) {
3694 if (ds
->dv_loaded
) {
3696 ok
= digest
= SecDbItemGetSHA1(inserted
, error
);
3697 if (ok
) SOSDigestVectorAppend(&ds
->toadd
, CFDataGetBytePtr(digest
));
3699 if (ok
&& deleted
) {
3700 ok
= digest
= SecDbItemGetSHA1(deleted
, error
);
3701 if (ok
) SOSDigestVectorAppend(&ds
->todel
, CFDataGetBytePtr(digest
));
3703 if (inserted
|| deleted
) {
3704 CFReleaseNull(ds
->manifest
);
3709 ds
->dv_loaded
= false;
3715 static bool SecItemDataSourceRecordAdd(SecItemDataSourceRef ds
, SecDbItemRef inserted
, CFErrorRef
*error
) {
3716 return SecItemDataSourceRecordUpdate(ds
, NULL
, inserted
, error
);
3719 static bool SecDbItemSelectSHA1(SecDbQueryRef query
, SecDbConnectionRef dbconn
, CFErrorRef
*error
,
3720 bool (^use_attr_in_where
)(const SecDbAttr
*attr
),
3721 bool (^add_where_sql
)(CFMutableStringRef sql
, bool *needWhere
),
3722 bool (^bind_added_where
)(sqlite3_stmt
*stmt
, int col
),
3723 void (^row
)(sqlite3_stmt
*stmt
, bool *stop
)) {
3724 __block
bool ok
= true;
3725 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
3726 return attr
->kind
== kSecDbSHA1Attr
;
3728 CFStringRef sql
= SecDbItemCopySelectSQL(query
, return_attr
, use_attr_in_where
, add_where_sql
);
3730 ok
&= SecDbPrepare(dbconn
, sql
, error
, ^(sqlite3_stmt
*stmt
) {
3731 ok
= (SecDbItemSelectBind(query
, stmt
, error
, use_attr_in_where
, bind_added_where
) &&
3732 SecDbStep(dbconn
, stmt
, error
, ^(bool *stop
){ row(stmt
, stop
); }));
3741 static bool SecItemDataSourceLoadManifest(SecItemDataSourceRef ds
, CFErrorRef
*error
) {
3743 SecDbConnectionRef dbconn
;
3744 if (!(dbconn
= SecItemDataSourceGetConnection(ds
, error
))) return false;
3746 /* Fetch all syncable items. */
3747 const SecDbClass
*synced_classes
[] = {
3753 ds
->dv
.count
= 0; // Empty the digest vectory before we begin
3754 CFErrorRef localError
= NULL
;
3755 for (size_t class_ix
= 0; class_ix
< array_size(synced_classes
);
3757 Query
*q
= query_create(synced_classes
[class_ix
], NULL
, &localError
);
3759 q
->q_return_type
= kSecReturnDataMask
| kSecReturnAttributesMask
;
3760 q
->q_limit
= kSecMatchUnlimited
;
3761 q
->q_keybag
= KEYBAG_DEVICE
;
3762 query_add_attribute(kSecAttrSynchronizable
, kCFBooleanTrue
, q
);
3763 //query_add_attribute(kSecAttrAccessible, ds->name, q);
3764 // Select everything including tombstones that is synchronizable.
3765 if (!SecDbItemSelectSHA1(q
, dbconn
, &localError
, ^bool(const SecDbAttr
*attr
) {
3766 return attr
->kind
== kSecDbSyncAttr
;
3767 }, NULL
, NULL
, ^(sqlite3_stmt
*stmt
, bool *stop
) {
3768 const uint8_t *digest
= sqlite3_column_blob(stmt
, 0);
3769 size_t digestLen
= sqlite3_column_bytes(stmt
, 0);
3770 if (digestLen
!= SOSDigestSize
) {
3771 secerror("digest %zu bytes", digestLen
);
3773 SOSDigestVectorAppend(&ds
->dv
, digest
);
3776 secerror("SecDbItemSelect failed: %@", localError
);
3777 CFReleaseNull(localError
);
3779 query_destroy(q
, &localError
);
3781 secerror("query_destroy failed: %@", localError
);
3782 CFReleaseNull(localError
);
3784 } else if (localError
) {
3785 secerror("query_create failed: %@", localError
);
3786 CFReleaseNull(localError
);
3789 SOSDigestVectorSort(&ds
->dv
);
3793 static bool SecItemDataSourceEnsureFreshManifest(SecItemDataSourceRef ds
, CFErrorRef
*error
) {
3795 if (ds
->dv_loaded
&& (ds
->toadd
.count
|| ds
->todel
.count
)) {
3796 CFErrorRef patchError
= NULL
;
3797 struct SOSDigestVector new_dv
= SOSDigestVectorInit
;
3798 ok
= SOSDigestVectorPatch(&ds
->dv
, &ds
->todel
, &ds
->toadd
, &new_dv
, &patchError
);
3799 if (!ok
) secerror("patch failed %@ manifest: %@ toadd: %@ todel: %@", patchError
, &ds
->dv
, &ds
->todel
, &ds
->toadd
);
3800 CFReleaseSafe(patchError
);
3801 SOSDigestVectorFree(&ds
->dv
);
3802 SOSDigestVectorFree(&ds
->toadd
);
3803 SOSDigestVectorFree(&ds
->todel
);
3806 // If we failed to patch or we haven't loaded yet, force a load from the db.
3807 return (ok
&& ds
->dv_loaded
) || (ds
->dv_loaded
= SecItemDataSourceLoadManifest(ds
, error
));
3810 /* DataSource protocol. */
3811 static bool ds_get_manifest_digest(SOSDataSourceRef data_source
, uint8_t *out_digest
, CFErrorRef
*error
) {
3812 struct SecItemDataSource
*ds
= (struct SecItemDataSource
*)data_source
;
3813 if (!ds
->manifest
) {
3814 SOSManifestRef mf
= data_source
->copy_manifest(data_source
, error
);
3821 memcpy(out_digest
, ds
->manifest_digest
, SOSDigestSize
);
3826 static SOSManifestRef
ds_copy_manifest(SOSDataSourceRef data_source
, CFErrorRef
*error
) {
3827 struct SecItemDataSource
*ds
= (struct SecItemDataSource
*)data_source
;
3830 CFRetain(ds
->manifest
);
3831 return ds
->manifest
;
3834 if (!SecItemDataSourceEnsureFreshManifest(ds
, error
)) return NULL
;
3836 ds
->manifest
= SOSManifestCreateWithBytes((const uint8_t *)ds
->dv
.digest
, ds
->dv
.count
* SOSDigestSize
, error
);
3838 ccdigest(ccsha1_di(), SOSManifestGetSize(ds
->manifest
), SOSManifestGetBytePtr(ds
->manifest
), ds
->manifest_digest
);
3840 return (SOSManifestRef
)CFRetain(ds
->manifest
);
3843 static bool ds_foreach_object(SOSDataSourceRef data_source
, SOSManifestRef manifest
, CFErrorRef
*error
, bool (^handle_object
)(SOSObjectRef object
, CFErrorRef
*error
)) {
3844 struct SecItemDataSource
*ds
= (struct SecItemDataSource
*)data_source
;
3846 __block
bool result
= true;
3847 const SecDbAttr
*sha1Attr
= SecDbClassAttrWithKind(&genp_class
, kSecDbSHA1Attr
, error
);
3848 if (!sha1Attr
) return false;
3849 bool (^return_attr
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
3850 return attr
->kind
== kSecDbRowIdAttr
|| attr
->kind
== kSecDbEncryptedDataAttr
;
3852 bool (^use_attr_in_where
)(const SecDbAttr
*attr
) = ^bool (const SecDbAttr
* attr
) {
3853 return attr
->kind
== kSecDbSHA1Attr
;
3855 const SecDbClass
*synced_classes
[] = {
3860 Query
*select_queries
[array_size(synced_classes
)];
3861 CFStringRef select_sql
[array_size(synced_classes
)];
3862 sqlite3_stmt
*select_stmts
[array_size(synced_classes
)];
3864 __block Query
**queries
= select_queries
;
3865 __block CFStringRef
*sqls
= select_sql
;
3866 __block sqlite3_stmt
**stmts
= select_stmts
;
3868 SecDbConnectionRef dbconn
;
3869 result
= dbconn
= SecItemDataSourceGetConnection(ds
, error
);
3872 for (size_t class_ix
= 0; class_ix
< array_size(synced_classes
); ++class_ix
) {
3874 && (queries
[class_ix
] = query_create(synced_classes
[class_ix
], NULL
, error
))
3875 && (sqls
[class_ix
] = SecDbItemCopySelectSQL(queries
[class_ix
], return_attr
, use_attr_in_where
, NULL
))
3876 && (stmts
[class_ix
] = SecDbCopyStmt(dbconn
, sqls
[class_ix
], NULL
, error
)));
3879 if (result
) SOSManifestForEach(manifest
, ^(CFDataRef key
) {
3880 __block
bool gotItem
= false;
3881 for (size_t class_ix
= 0; result
&& !gotItem
&& class_ix
< array_size(synced_classes
); ++class_ix
) {
3882 CFDictionarySetValue(queries
[class_ix
]->q_item
, sha1Attr
->name
, key
);
3883 result
&= (SecDbItemSelectBind(queries
[class_ix
], stmts
[class_ix
], error
, use_attr_in_where
, NULL
) && SecDbStep(dbconn
, stmts
[class_ix
], error
, ^(bool *stop
) {
3884 SecDbItemRef item
= SecDbItemCreateWithStatement(kCFAllocatorDefault
, queries
[class_ix
]->q_class
, stmts
[class_ix
], KEYBAG_DEVICE
, error
, return_attr
);
3886 CFErrorRef localError
=NULL
;
3888 // Stop on errors from handle_object, except decode errors
3889 if(!(result
=handle_object((SOSObjectRef
)item
, &localError
))){
3890 if (SecErrorGetOSStatus(localError
) == errSecDecode
) {
3891 const uint8_t *p
=CFDataGetBytePtr(key
);
3892 secnotice("item", "Found corrupted item, removing from manifest key=%02X%02X%02X%02X, item=%@", p
[0],p
[1],p
[2],p
[3], item
);
3893 /* Removing from Manifest: */
3894 SOSDigestVectorAppend(&ds
->todel
, p
);
3895 CFReleaseNull(ds
->manifest
);
3899 if(error
&& *error
== NULL
) {
3900 *error
= localError
;
3905 CFReleaseSafe(localError
);
3907 })) && SecDbReset(stmts
[class_ix
], error
);
3911 if (error
&& !*error
) {
3912 SecCFCreateErrorWithFormat(kSecObjectNotFoundError
, kSecItemDataSourceErrorDomain
, NULL
, error
, 0, CFSTR("key %@ not in database"), key
);
3918 for (size_t class_ix
= 0; class_ix
< array_size(synced_classes
); ++class_ix
) {
3919 result
&= SecDbReleaseCachedStmt(dbconn
, sqls
[class_ix
], stmts
[class_ix
], error
);
3920 CFReleaseSafe(sqls
[class_ix
]);
3921 result
&= query_destroy(queries
[class_ix
], error
);
3926 static void ds_dispose(SOSDataSourceRef data_source
) {
3927 struct SecItemDataSource
*ds
= (struct SecItemDataSource
*)data_source
;
3929 SecDbConnectionRelease(ds
->_dbconn
);
3931 SecKeychainChanged(ds
->syncWithPeersWhenDone
);
3932 CFReleaseSafe(ds
->manifest
);
3933 SOSDigestVectorFree(&ds
->dv
);
3937 static SOSObjectRef
ds_create_with_property_list(SOSDataSourceRef ds
, CFDictionaryRef plist
, CFErrorRef
*error
) {
3938 SecDbItemRef item
= NULL
;
3939 const SecDbClass
*class = NULL
;
3940 CFTypeRef cname
= CFDictionaryGetValue(plist
, kSecClass
);
3942 class = kc_class_with_name(cname
);
3944 item
= SecDbItemCreateWithAttributes(kCFAllocatorDefault
, class, plist
, KEYBAG_DEVICE
, error
);
3946 SecError(errSecNoSuchClass
, error
, CFSTR("can find class named: %@"), cname
);
3949 SecError(errSecItemClassMissing
, error
, CFSTR("query missing %@ attribute"), kSecClass
);
3951 return (SOSObjectRef
)item
;
3954 static CFDataRef
ds_copy_digest(SOSObjectRef object
, CFErrorRef
*error
) {
3955 SecDbItemRef item
= (SecDbItemRef
) object
;
3956 CFDataRef digest
= SecDbItemGetSHA1(item
, error
);
3957 CFRetainSafe(digest
);
3961 static CFDataRef
ds_copy_primary_key(SOSObjectRef object
, CFErrorRef
*error
) {
3962 SecDbItemRef item
= (SecDbItemRef
) object
;
3963 CFDataRef pk
= SecDbItemGetPrimaryKey(item
, error
);
3968 static CFDictionaryRef
ds_copy_property_list(SOSObjectRef object
, CFErrorRef
*error
) {
3969 SecDbItemRef item
= (SecDbItemRef
) object
;
3970 CFMutableDictionaryRef plist
= SecDbItemCopyPListWithMask(item
, kSecDbInCryptoDataFlag
, error
);
3972 CFDictionaryAddValue(plist
, kSecClass
, SecDbItemGetClass(item
)->name
);
3976 // Return the newest object
3977 static SOSObjectRef
ds_copy_merged_object(SOSObjectRef object1
, SOSObjectRef object2
, CFErrorRef
*error
) {
3978 SecDbItemRef item1
= (SecDbItemRef
) object1
;
3979 SecDbItemRef item2
= (SecDbItemRef
) object2
;
3980 SOSObjectRef result
= NULL
;
3982 const SecDbAttr
*desc
= SecDbAttrWithKey(SecDbItemGetClass(item1
), kSecAttrModificationDate
, error
);
3983 m1
= SecDbItemGetValue(item1
, desc
, error
);
3986 m2
= SecDbItemGetValue(item2
, desc
, error
);
3989 switch (CFDateCompare(m1
, m2
, NULL
)) {
3990 case kCFCompareGreaterThan
:
3991 result
= (SOSObjectRef
)item1
;
3993 case kCFCompareLessThan
:
3994 result
= (SOSObjectRef
)item2
;
3996 case kCFCompareEqualTo
:
3998 // Return the item with the smallest digest.
3999 CFDataRef digest1
= ds_copy_digest(object1
, error
);
4000 CFDataRef digest2
= ds_copy_digest(object2
, error
);
4001 if (digest1
&& digest2
) switch (CFDataCompare(digest1
, digest2
)) {
4002 case kCFCompareGreaterThan
:
4003 case kCFCompareEqualTo
:
4004 result
= (SOSObjectRef
)item2
;
4006 case kCFCompareLessThan
:
4007 result
= (SOSObjectRef
)item1
;
4010 CFReleaseSafe(digest2
);
4011 CFReleaseSafe(digest1
);
4015 CFRetainSafe(result
);
4019 static SOSMergeResult
dsMergeObject(SOSDataSourceRef data_source
, SOSObjectRef peersObject
, CFErrorRef
*error
) {
4020 struct SecItemDataSource
*ds
= (struct SecItemDataSource
*)data_source
;
4021 SecDbItemRef peersItem
= (SecDbItemRef
)peersObject
;
4022 SecDbConnectionRef dbconn
= SecItemDataSourceGetConnection(ds
, error
);
4023 __block SOSMergeResult mr
= kSOSMergeFailure
;
4024 __block SecDbItemRef mergedItem
= NULL
;
4025 __block SecDbItemRef replacedItem
= NULL
;
4026 if (!peersItem
|| !dbconn
|| !SecDbItemSetKeybag(peersItem
, KEYBAG_DEVICE
, error
)) return mr
;
4027 if (SecDbItemInsertOrReplace(peersItem
, dbconn
, error
, ^(SecDbItemRef myItem
, SecDbItemRef
*replace
) {
4028 // An item with the same primary key as dbItem already exists in the the database. That item is old_item.
4029 // Let the conflict resolver choose which item to keep.
4030 mergedItem
= (SecDbItemRef
)ds_copy_merged_object(peersObject
, (SOSObjectRef
)myItem
, error
);
4031 if (!mergedItem
) return;
4032 if (CFEqual(mergedItem
, myItem
)) {
4033 // Conflict resolver choose my (local) item
4034 mr
= kSOSMergeLocalObject
;
4036 CFRetainSafe(myItem
);
4037 replacedItem
= myItem
;
4038 CFRetainSafe(mergedItem
);
4039 *replace
= mergedItem
;
4040 if (CFEqual(mergedItem
, peersItem
)) {
4041 // Conflict resolver choose peers item
4042 mr
= kSOSMergePeersObject
;
4044 mr
= kSOSMergeCreatedObject
;
4048 if (mr
== kSOSMergeFailure
) {
4049 mr
= kSOSMergePeersObject
;
4050 SecItemDataSourceRecordAdd(ds
, peersItem
, error
);
4051 } else if (mr
!= kSOSMergeLocalObject
) {
4052 SecItemDataSourceRecordUpdate(ds
, replacedItem
, mergedItem
, error
);
4056 if (error
&& *error
&& mr
!= kSOSMergeFailure
)
4057 CFReleaseNull(*error
);
4059 CFReleaseSafe(mergedItem
);
4060 CFReleaseSafe(replacedItem
);
4066 Truthy backup format is a dictionary from sha1 => item.
4067 Each item has class, hash and item data.
4069 TODO: sha1 is included as binary blob to avoid parsing key.
4072 kSecBackupIndexHash
= 0,
4073 kSecBackupIndexClass
,
4074 kSecBackupIndexData
,
4077 static const void *kSecBackupKeys
[] = {
4078 [kSecBackupIndexHash
] = CFSTR("hash"),
4079 [kSecBackupIndexClass
] = CFSTR("class"),
4080 [kSecBackupIndexData
] = CFSTR("data"),
4083 #define kSecBackupHash kSecBackupKeys[kSecBackupIndexHash]
4084 #define kSecBackupClass kSecBackupKeys[kSecBackupIndexClass]
4085 #define kSecBackupData kSecBackupKeys[kSecBackupIndexData]
4087 static CFDictionaryRef
ds_backup_object(SOSObjectRef object
, uint64_t handle
, CFErrorRef
*error
) {
4088 const void *values
[array_size(kSecBackupKeys
)];
4089 SecDbItemRef item
= (SecDbItemRef
)object
;
4090 CFDictionaryRef backup_item
= NULL
;
4092 if ((values
[kSecBackupIndexHash
] = SecDbItemGetSHA1(item
, error
))) {
4093 if ((values
[kSecBackupIndexData
] = SecDbItemCopyEncryptedDataToBackup(item
, handle
, error
))) {
4094 values
[kSecBackupIndexClass
] = SecDbItemGetClass(item
)->name
;
4095 backup_item
= CFDictionaryCreate(kCFAllocatorDefault
, kSecBackupKeys
, values
, array_size(kSecBackupKeys
), &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4096 CFRelease(values
[kSecBackupIndexData
]);
4103 static bool ds_restore_object(SOSDataSourceRef data_source
, uint64_t handle
, CFDictionaryRef item
, CFErrorRef
*error
) {
4104 struct SecItemDataSource
*ds
= (struct SecItemDataSource
*)data_source
;
4105 SecDbConnectionRef dbconn
= SecItemDataSourceGetConnection(ds
, error
);
4106 if (!dbconn
) return false;
4108 CFStringRef item_class
= CFDictionaryGetValue(item
, kSecBackupClass
);
4109 CFDataRef data
= CFDictionaryGetValue(item
, kSecBackupData
);
4110 const SecDbClass
*dbclass
= NULL
;
4112 if (!item_class
|| !data
)
4113 return SecError(errSecDecode
, error
, CFSTR("no class or data in object"));
4115 dbclass
= kc_class_with_name(item_class
);
4117 return SecError(errSecDecode
, error
, CFSTR("no such class %@; update kc_class_with_name "), item_class
);
4119 __block SecDbItemRef dbitem
= SecDbItemCreateWithEncryptedData(kCFAllocatorDefault
, dbclass
, data
, (keybag_handle_t
)handle
, error
);
4123 __block
bool ok
= SecDbItemSetKeybag(dbitem
, KEYBAG_DEVICE
, error
);
4126 __block SecDbItemRef replaced_item
= NULL
;
4127 ok
&= SecDbItemInsertOrReplace(dbitem
, dbconn
, error
, ^(SecDbItemRef old_item
, SecDbItemRef
*replace
) {
4128 // An item with the same primary key as dbItem already exists in the the database. That item is old_item.
4129 // Let the conflict resolver choose which item to keep.
4130 SecDbItemRef chosen_item
= (SecDbItemRef
)ds_copy_merged_object((SOSObjectRef
)dbitem
, (SOSObjectRef
)old_item
, error
);
4132 if (CFEqual(chosen_item
, old_item
)) {
4133 // We're keeping the exisiting item, so we don't need to change anything.
4134 CFRelease(chosen_item
);
4135 CFReleaseNull(dbitem
);
4137 // We choose a different item than what's in the database already. Let's set dbitem to what
4138 // we are replacing the item in the database with, and set replaced_item to the item we are replacing.
4139 CFRelease(dbitem
); // Release the item created via SecDbItemCreateWithEncryptedData
4140 // Record what we put in the database
4141 CFRetain(chosen_item
); // retain what we are about to return in *replace, since SecDbItemInsertOrReplace() CFReleases it.
4142 *replace
= dbitem
= chosen_item
;
4143 // Record that we are replaced old_item in replaced_item.
4145 replaced_item
= old_item
;
4151 && SecItemDataSourceRecordUpdate(ds
, replaced_item
, dbitem
, error
);
4152 CFReleaseSafe(replaced_item
);
4154 CFReleaseSafe(dbitem
);
4160 static SOSDataSourceRef
SecItemDataSourceCreate(SecDbRef db
, bool readOnly
, bool syncWithPeersWhenDone
, CFErrorRef
*error
) {
4161 __block SecItemDataSourceRef ds
= calloc(1, sizeof(struct SecItemDataSource
));
4162 ds
->ds
.get_manifest_digest
= ds_get_manifest_digest
;
4163 ds
->ds
.copy_manifest
= ds_copy_manifest
;
4164 ds
->ds
.foreach_object
= ds_foreach_object
;
4165 ds
->ds
.release
= ds_dispose
;
4166 ds
->ds
.add
= dsMergeObject
;
4168 ds
->ds
.createWithPropertyList
= ds_create_with_property_list
;
4169 ds
->ds
.copyDigest
= ds_copy_digest
;
4170 ds
->ds
.copyPrimaryKey
= ds_copy_primary_key
;
4171 ds
->ds
.copyPropertyList
= ds_copy_property_list
;
4172 ds
->ds
.copyMergedObject
= ds_copy_merged_object
;
4173 ds
->ds
.backupObject
= ds_backup_object
;
4174 ds
->ds
.restoreObject
= ds_restore_object
;
4176 ds
->syncWithPeersWhenDone
= syncWithPeersWhenDone
;
4177 ds
->db
= (SecDbRef
)CFRetain(db
);
4178 ds
->readOnly
= readOnly
;
4180 ds
->changed
= false;
4181 struct SOSDigestVector dv
= SOSDigestVectorInit
;
4184 return (SOSDataSourceRef
)ds
;
4187 static CFArrayRef
SecItemDataSourceFactoryCopyNames(SOSDataSourceFactoryRef factory
)
4189 return CFArrayCreateForCFTypes(kCFAllocatorDefault
,
4190 kSecAttrAccessibleWhenUnlocked
,
4191 //kSecAttrAccessibleAfterFirstUnlock,
4192 //kSecAttrAccessibleAlways,
4196 struct SecItemDataSourceFactory
{
4197 struct SOSDataSourceFactory factory
;
4202 static SOSDataSourceRef
SecItemDataSourceFactoryCopyDataSource(SOSDataSourceFactoryRef factory
, CFStringRef dataSourceName
, bool readOnly
, CFErrorRef
*error
)
4204 struct SecItemDataSourceFactory
*f
= (struct SecItemDataSourceFactory
*)factory
;
4205 return SecItemDataSourceCreate(f
->db
, readOnly
, false, error
);
4208 static void SecItemDataSourceFactoryDispose(SOSDataSourceFactoryRef factory
)
4210 struct SecItemDataSourceFactory
*f
= (struct SecItemDataSourceFactory
*)factory
;
4211 CFReleaseSafe(f
->db
);
4215 SOSDataSourceFactoryRef
SecItemDataSourceFactoryCreate(SecDbRef db
) {
4216 struct SecItemDataSourceFactory
*dsf
= calloc(1, sizeof(struct SecItemDataSourceFactory
));
4217 dsf
->factory
.copy_names
= SecItemDataSourceFactoryCopyNames
;
4218 dsf
->factory
.create_datasource
= SecItemDataSourceFactoryCopyDataSource
;
4219 dsf
->factory
.release
= SecItemDataSourceFactoryDispose
;
4223 return &dsf
->factory
;
4226 SOSDataSourceFactoryRef
SecItemDataSourceFactoryCreateDefault(void) {
4227 return SecItemDataSourceFactoryCreate(kc_dbhandle());
4230 void SecItemServerAppendItemDescription(CFMutableStringRef desc
, CFDictionaryRef object
) {
4231 SOSObjectRef item
= ds_create_with_property_list(NULL
, object
, NULL
);
4233 CFStringRef itemDesc
= CFCopyDescription(item
);
4235 CFStringAppend(desc
, itemDesc
);
4236 CFReleaseSafe(itemDesc
);
4242 /* AUDIT[securityd]:
4243 args_in (ok) is a caller provided, CFDictionaryRef.
4246 _SecServerKeychainSyncUpdate(CFDictionaryRef updates
, CFErrorRef
*error
) {
4247 // This never fails, trust us!
4248 CFRetainSafe(updates
);
4249 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
, 0), ^{
4250 SOSCCHandleUpdate(updates
);
4251 CFReleaseSafe(updates
);
4257 // Truthiness in the cloud backup/restore support.
4260 static CFStringRef
SOSCopyItemKey(SOSDataSourceRef ds
, SOSObjectRef object
, CFErrorRef
*error
)
4262 CFStringRef item_key
= NULL
;
4263 CFDataRef digest_data
= ds
->copyDigest(object
, error
);
4265 item_key
= CFDataCopyHexString(digest_data
);
4266 CFRelease(digest_data
);
4271 static SOSManifestRef
SOSCopyManifestFromBackup(CFDictionaryRef backup
)
4273 CFMutableDataRef manifest
= CFDataCreateMutable(kCFAllocatorDefault
, 0);
4275 CFDictionaryForEach(backup
, ^void (const void * key
, const void * value
) {
4276 if (isDictionary(value
)) {
4277 /* converting key back to binary blob is horrible */
4278 CFDataRef sha1
= CFDictionaryGetValue(value
, kSecBackupHash
);
4280 CFDataAppend(manifest
, sha1
);
4284 return (SOSManifestRef
)manifest
;
4287 static CFDictionaryRef
4288 _SecServerCopyTruthInTheCloud(CFDataRef keybag
, CFDataRef password
,
4289 CFDictionaryRef backup
, CFErrorRef
*error
)
4291 SOSManifestRef mold
= NULL
, mnow
= NULL
, mdelete
= NULL
, madd
= NULL
;
4292 CFErrorRef foreachError
= NULL
;
4293 CFDictionaryRef backup_out
= NULL
;
4294 keybag_handle_t bag_handle
;
4295 if (!ks_open_keybag(keybag
, password
, &bag_handle
, error
))
4298 CFMutableDictionaryRef backup_new
= NULL
;
4299 SOSDataSourceRef ds
= SecItemDataSourceCreate(kc_dbhandle(), true, false, error
);
4301 backup_new
= backup
? CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, backup
) : CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
4302 mold
= SOSCopyManifestFromBackup(backup
);
4303 mnow
= ds
->copy_manifest(ds
, error
);
4304 SOSManifestDiff(mold
, mnow
, &mdelete
, &madd
, error
);
4306 // Delete everything from the new_backup that is no longer in the datasource according to the datasources manifest.
4307 SOSManifestForEach(mdelete
, ^(CFDataRef digest_data
) {
4308 CFStringRef deleted_item_key
= CFDataCopyHexString(digest_data
);
4309 CFDictionaryRemoveValue(backup_new
, deleted_item_key
);
4310 CFRelease(deleted_item_key
);
4313 if(!ds
->foreach_object(ds
, madd
, &foreachError
, ^bool(SOSObjectRef object
, CFErrorRef
*localError
) {
4315 CFStringRef key
= SOSCopyItemKey(ds
, object
, localError
);
4316 CFTypeRef value
= ds
->backupObject(object
, bag_handle
, localError
);
4318 if (!key
|| !value
) {
4321 CFDictionarySetValue(backup_new
, key
, value
);
4324 CFReleaseSafe(value
);
4327 if(!SecErrorGetOSStatus(foreachError
)==errSecDecode
) {
4328 if(error
&& *error
==NULL
) {
4329 *error
= foreachError
;
4330 foreachError
= NULL
;
4336 backup_out
= backup_new
;
4344 CFReleaseSafe(foreachError
);
4345 CFReleaseSafe(mold
);
4346 CFReleaseSafe(mnow
);
4347 CFReleaseSafe(madd
);
4348 CFReleaseSafe(mdelete
);
4349 CFReleaseSafe(backup_new
);
4351 if (!ks_close_keybag(bag_handle
, error
))
4352 CFReleaseNull(backup_out
);
4358 _SecServerRestoreTruthInTheCloud(CFDataRef keybag
, CFDataRef password
, CFDictionaryRef backup_in
, CFErrorRef
*error
) {
4359 __block
bool ok
= true;
4360 keybag_handle_t bag_handle
;
4361 if (!ks_open_keybag(keybag
, password
, &bag_handle
, error
))
4364 SOSManifestRef mbackup
= SOSCopyManifestFromBackup(backup_in
);
4366 SOSDataSourceRef ds
= SecItemDataSourceCreate(kc_dbhandle(), false, true, error
);
4368 SOSManifestRef mnow
= ds
->copy_manifest(ds
, error
);
4369 SOSManifestRef mdelete
= NULL
, madd
= NULL
;
4370 SOSManifestDiff(mnow
, mbackup
, &mdelete
, &madd
, error
);
4372 // Don't delete everything in datasource not in backup.
4374 // Add items from the backup
4375 SOSManifestForEach(madd
, ^void(CFDataRef e
) {
4376 CFDictionaryRef item
= NULL
;
4377 CFStringRef sha1
= CFDataCopyHexString(e
);
4379 item
= CFDictionaryGetValue(backup_in
, sha1
);
4383 CFErrorRef localError
= NULL
;
4384 if (!ds
->restoreObject(ds
, bag_handle
, item
, &localError
)) {
4385 if (SecErrorGetOSStatus(localError
) == errSecDuplicateItem
) {
4386 // Log and ignore duplicate item errors during restore
4387 secnotice("titc", "restore %@ not replacing existing item", item
);
4389 // Propagate the first other error upwards (causing the restore to fail).
4390 secerror("restore %@ failed %@", item
, localError
);
4392 if (error
&& !*error
) {
4393 *error
= localError
;
4397 CFReleaseSafe(localError
);
4403 CFReleaseNull(mdelete
);
4404 CFReleaseNull(madd
);
4405 CFReleaseNull(mnow
);
4412 ok
&= ks_close_keybag(bag_handle
, error
);
4418 CF_RETURNS_RETAINED CFDictionaryRef
4419 _SecServerBackupSyncable(CFDictionaryRef backup
, CFDataRef keybag
, CFDataRef password
, CFErrorRef
*error
) {
4420 require_action_quiet(isData(keybag
), errOut
, SecError(errSecParam
, error
, CFSTR("keybag %@ not a data"), keybag
));
4421 require_action_quiet(!backup
|| isDictionary(backup
), errOut
, SecError(errSecParam
, error
, CFSTR("backup %@ not a dictionary"), backup
));
4422 require_action_quiet(!password
|| isData(password
), errOut
, SecError(errSecParam
, error
, CFSTR("password %@ not a data"), password
));
4424 return _SecServerCopyTruthInTheCloud(keybag
, password
, backup
, error
);
4431 _SecServerRestoreSyncable(CFDictionaryRef backup
, CFDataRef keybag
, CFDataRef password
, CFErrorRef
*error
) {
4433 require_action_quiet(isData(keybag
), errOut
, ok
= SecError(errSecParam
, error
, CFSTR("keybag %@ not a data"), keybag
));
4434 require_action_quiet(isDictionary(backup
), errOut
, ok
= SecError(errSecParam
, error
, CFSTR("backup %@ not a dictionary"), backup
));
4436 require_action_quiet(isData(password
), errOut
, ok
= SecError(errSecParam
, error
, CFSTR("password not a data")));
4439 ok
= _SecServerRestoreTruthInTheCloud(keybag
, password
, backup
, error
);