2  * Copyright (c) 2006-2015 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> 
  33 #include <securityd/SecItemDataSource.h> 
  34 #include <securityd/SecItemDb.h> 
  35 #include <securityd/SecItemSchema.h> 
  36 #include <securityd/SOSCloudCircleServer.h> 
  37 #include <Security/SecBasePriv.h> 
  38 #include <Security/SecItemPriv.h> 
  39 #include <Security/SecItemInternal.h> 
  40 #include <Security/SecureObjectSync/SOSChangeTracker.h> 
  41 #include <Security/SecureObjectSync/SOSDigestVector.h> 
  42 #include <Security/SecureObjectSync/SOSViews.h> 
  44 // TODO: Make this include work on both platforms. rdar://problem/16526848 
  45 #if TARGET_OS_EMBEDDED 
  46 #include <Security/SecEntitlements.h> 
  47 #include <MobileKeyBag/MobileKeyBag.h> 
  49 /* defines from <Security/SecEntitlements.h> */ 
  50 #define kSecEntitlementAssociatedDomains CFSTR("com.apple.developer.associated-domains") 
  51 #define kSecEntitlementPrivateAssociatedDomains CFSTR("com.apple.private.associated-domains") 
  54 #if TARGET_OS_EMBEDDED 
  55 #include <AggregateDictionary/ADClient.h> 
  60 #include <utilities/array_size.h> 
  61 #include <utilities/SecFileLocations.h> 
  62 #include <utilities/SecTrace.h> 
  63 #include <Security/SecuritydXPC.h> 
  64 #include "swcagent_client.h" 
  66 #if TARGET_OS_IPHONE && !TARGET_OS_WATCH 
  68 #include <SharedWebCredentials/SharedWebCredentials.h> 
  70 typedef OSStatus (*SWCCheckService_f
)(CFStringRef service
, CFStringRef appID
, CFStringRef domain
, SWCCheckServiceCompletion_b completion
); 
  71 typedef OSStatus (*SWCSetServiceFlags_f
)(CFStringRef service
, CFStringRef appID
, CFStringRef domain
, SWCFlags mask
, SWCFlags flags
, SWCSetServiceFlagsCompletion_b completion
); 
  73 typedef uint32_t SWCFlags
; 
  74 #define kSWCFlags_None                          0 
  75 #define kSWCFlag_Pending                        ( 1U << 0 ) 
  76 #define kSWCFlag_SiteApproved           ( 1U << 1 ) 
  77 #define kSWCFlag_SiteDenied                     ( 1U << 2 ) 
  78 #define kSWCFlag_UserApproved           ( 1U << 3 ) 
  79 #define kSWCFlag_UserDenied                     ( 1U << 4 ) 
  80 #define kSWCFlag_ExternalMask           ( kSWCFlag_UserApproved | kSWCFlag_UserDenied ) 
  83 /* Changed the name of the keychain changed notification, for testing */ 
  84 static const char *g_keychain_changed_notification 
= kSecServerKeychainChangedNotification
; 
  86 void SecItemServerSetKeychainChangedNotification(const char *notification_name
) 
  88     g_keychain_changed_notification 
= notification_name
; 
  91 void SecKeychainChanged(bool syncWithPeers
) { 
  92     uint32_t result 
= notify_post(g_keychain_changed_notification
); 
  94         SOSCCSyncWithAllPeers(); 
  95     if (result 
== NOTIFY_STATUS_OK
) 
  96         secnotice("item", "Sent %s%s", syncWithPeers 
? "SyncWithAllPeers and " : "", g_keychain_changed_notification
); 
  98         secerror("%snotify_post %s returned: %" PRIu32
, syncWithPeers 
? "Sent SyncWithAllPeers, " : "", g_keychain_changed_notification
, result
); 
 101 /* Return the current database version in *version. */ 
 102 static bool SecKeychainDbGetVersion(SecDbConnectionRef dbt
, int *version
, CFErrorRef 
*error
) 
 104     __block 
bool ok 
= false; 
 105     SecDbQueryRef query 
= NULL
; 
 106     __block CFNumberRef versionNumber 
= NULL
; 
 107     __block CFErrorRef localError 
= NULL
; 
 109     require_quiet(query 
= query_create(&tversion_class
, NULL
, NULL
, &localError
), out
); 
 110     require_quiet(SecDbItemSelect(query
, dbt
, &localError
, ^bool(const SecDbAttr 
*attr
) { 
 111         // Bind all attributes. 
 113     }, ^bool(const SecDbAttr 
*attr
) { 
 116     }, NULL
, NULL
, ^(SecDbItemRef item
, bool *stop
) { 
 117         versionNumber 
= copyNumber(SecDbItemGetValue(item
, tversion_class
.attrs
[0], &localError
)); 
 121     require_action_quiet(versionNumber 
!= NULL 
&& CFNumberGetValue(versionNumber
, kCFNumberIntType
, version
), out
, 
 122                          // We have a tversion table but we didn't find a single version 
 123                          // value, now what? I suppose we pretend the db is corrupted 
 124                          // since this isn't supposed to ever happen. 
 125                          SecDbError(SQLITE_CORRUPT
, error
, CFSTR("Failed to read version table")); 
 126                          secwarning("tversion read error: %@", error 
? *error 
: NULL
)); 
 130     if (!ok 
&& CFErrorGetCode(localError
) == SQLITE_ERROR
) { 
 131         // Most probably means that the version table does not exist at all. 
 132         // TODO: Use "SELECT name FROM sqlite_master WHERE type='table' AND name='tversion'" to detect tversion presence. 
 133         CFReleaseSafe(localError
); 
 138         query_destroy(query
, NULL
); 
 139     CFReleaseSafe(versionNumber
); 
 140     return ok 
|| CFErrorPropagate(localError
, error
); 
 144 isClassD(SecDbItemRef item
) 
 146     CFTypeRef accessible 
= SecDbItemGetCachedValueWithName(item
, kSecAttrAccessible
); 
 148     if (CFEqualSafe(accessible
, kSecAttrAccessibleAlways
) || CFEqualSafe(accessible
, kSecAttrAccessibleAlwaysThisDeviceOnly
)) 
 153 #if TARGET_OS_EMBEDDED 
 156 measureDuration(struct timeval 
*start
) 
 161     gettimeofday(&stop
, NULL
); 
 163     duration 
= (stop
.tv_sec
-start
->tv_sec
) * 1000; 
 164     duration 
+= (stop
.tv_usec 
/ 1000) - (start
->tv_usec 
/ 1000); 
 166     return SecBucket2Significant(duration
); 
 170 measureUpgradePhase1(struct timeval 
*start
, bool success
, int64_t itemsMigrated
) 
 172     int64_t duration 
= measureDuration(start
); 
 175         ADClientSetValueForScalarKey(CFSTR("com.apple.keychain.phase1.migrated-items-success"), itemsMigrated
); 
 176         ADClientSetValueForScalarKey(CFSTR("com.apple.keychain.phase1.migrated-time-success"), duration
); 
 178         ADClientSetValueForScalarKey(CFSTR("com.apple.keychain.phase1.migrated-items-fail"), itemsMigrated
); 
 179         ADClientSetValueForScalarKey(CFSTR("com.apple.keychain.phase1.migrated-time-fail"), duration
); 
 184 measureUpgradePhase2(struct timeval 
*start
, int64_t itemsMigrated
) 
 186     int64_t duration 
= measureDuration(start
); 
 188     ADClientSetValueForScalarKey(CFSTR("com.apple.keychain.phase2.migrated-items"), itemsMigrated
); 
 189     ADClientSetValueForScalarKey(CFSTR("com.apple.keychain.phase2.migrated-time"), duration
); 
 191 #endif /* TARGET_OS_EMBEDDED */ 
 193 // Goes through all tables represented by old_schema and tries to migrate all items from them into new (current version) tables. 
 194 static bool UpgradeSchemaPhase1(SecDbConnectionRef dbt
, const SecDbSchema 
*oldSchema
, CFErrorRef 
*error
) 
 196     __block 
bool ok 
= true; 
 197     const SecDbSchema 
*newSchema 
= kc_schemas
[0]; 
 198     SecDbClass 
const *const *oldClass
; 
 199     SecDbClass 
const *const *newClass
; 
 200     SecDbQueryRef query 
= NULL
; 
 201     CFMutableStringRef sql 
= NULL
; 
 202 #if TARGET_OS_EMBEDDED 
 203     __block 
int64_t itemsMigrated 
= 0; 
 204     struct timeval start
; 
 206     gettimeofday(&start
, NULL
); 
 209     // Rename existing tables to old names, as present in old schemas. 
 210     sql 
= CFStringCreateMutable(NULL
, 0); 
 211     for (oldClass 
= oldSchema
->classes
, newClass 
= newSchema
->classes
; 
 212          *oldClass 
!= NULL 
&& *newClass 
!= NULL
; oldClass
++, newClass
++) { 
 213         if (!CFEqual((*oldClass
)->name
, (*newClass
)->name
)) { 
 214             CFStringAppendFormat(sql
, NULL
, CFSTR("ALTER TABLE %@ RENAME TO %@;"), 
 215                                  (*newClass
)->name
, (*oldClass
)->name
); 
 217             CFStringAppendFormat(sql
, NULL
, CFSTR("DROP TABLE %@;"), (*oldClass
)->name
); 
 220     require_quiet(ok 
&= SecDbExec(dbt
, sql
, error
), out
); 
 223     // Drop indices that that new schemas will use 
 224     sql 
= CFStringCreateMutable(NULL
, 0); 
 225     for (newClass 
= newSchema
->classes
; *newClass 
!= NULL
; newClass
++) { 
 226         SecDbForEachAttrWithMask((*newClass
), desc
, kSecDbIndexFlag 
| kSecDbInFlag
) { 
 227             CFStringAppendFormat(sql
, 0, CFSTR("DROP INDEX IF EXISTS %@%@;"), (*newClass
)->name
, desc
->name
); 
 230     require_quiet(ok 
&= SecDbExec(dbt
, sql
, error
), out
); 
 233     // Create tables for new schema. 
 234     require_quiet(ok 
&= SecItemDbCreateSchema(dbt
, newSchema
, error
), out
); 
 235     // Go through all classes of current schema to transfer all items to new tables. 
 236     for (oldClass 
= oldSchema
->classes
, newClass 
= newSchema
->classes
; 
 237          *oldClass 
!= NULL 
&& *newClass 
!= NULL
; oldClass
++, newClass
++) { 
 238         if (CFEqual((*oldClass
)->name
, (*newClass
)->name
)) 
 241         secnotice("upgr", "Upgrading table %@", (*oldClass
)->name
); 
 243         // Prepare query to iterate through all items in cur_class. 
 245             query_destroy(query
, NULL
); 
 246         require_quiet(query 
= query_create(*oldClass
, SecMUSRGetAllViews(), NULL
, error
), out
); 
 248         ok 
&= SecDbItemSelect(query
, dbt
, error
, ^bool(const SecDbAttr 
*attr
) { 
 249             // We are interested in all attributes which are physically present in the DB. 
 250             return (attr
->flags 
& kSecDbInFlag
) != 0; 
 251         }, ^bool(const SecDbAttr 
*attr
) { 
 252             // No filtering please. 
 254         }, NULL
, NULL
, ^(SecDbItemRef item
, bool *stop
) { 
 255             CFErrorRef localError 
= NULL
; 
 257 #if TARGET_OS_EMBEDDED 
 260             // Switch item to the new class. 
 261             item
->class = *newClass
; 
 263             if (isClassD(item
)) { 
 265                 ok 
&= SecDbItemEnsureDecrypted(item
, &localError
); 
 266                 require_quiet(ok
, out
); 
 268                 // Delete SHA1 field from the item, so that it is newly recalculated before storing 
 269                 // the item into the new table. 
 270                 require_quiet(ok 
&= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, error
), 
 271                                                           kCFNull
, error
), out
); 
 273                 // Leave item encrypted, do not ever try to decrypt it since it will fail. 
 274                 item
->_edataState 
= kSecDbItemAlwaysEncrypted
; 
 277             // Insert new item into the new table. 
 278             if (!SecDbItemInsert(item
, dbt
, &localError
)) { 
 279                 secerror("item: %@ insert during upgrade: %@", item
, localError
); 
 285                 OSStatus status 
= SecErrorGetOSStatus(localError
); 
 288                     // continue to upgrade and don't propagate errors for insert failures 
 289                     // that are typical of a single item failure 
 291                     case errSecDuplicateItem
: 
 294                     case errSecInteractionNotAllowed
: 
 295                     case errSecAuthNeeded
: 
 299                         ok 
&= CFErrorPropagate(CFRetainSafe(localError
), error
); 
 302                 CFReleaseSafe(localError
); 
 307         require_quiet(ok
, out
); 
 310     // Remove old tables from the DB. 
 311     CFAssignRetained(sql
, CFStringCreateMutable(NULL
, 0)); 
 312     for (oldClass 
= oldSchema
->classes
, newClass 
= newSchema
->classes
; 
 313          *oldClass 
!= NULL 
&& *newClass 
!= NULL
; oldClass
++, newClass
++) { 
 314         if (!CFEqual((*oldClass
)->name
, (*newClass
)->name
)) { 
 315             CFStringAppendFormat(sql
, NULL
, CFSTR("DROP TABLE %@;"), (*oldClass
)->name
); 
 318     require_quiet(ok 
&= SecDbExec(dbt
, sql
, error
), out
); 
 321 #if TARGET_OS_EMBEDDED 
 322     measureUpgradePhase1(&start
, ok
, SecBucket2Significant(itemsMigrated
)); 
 326         query_destroy(query
, NULL
); 
 332 // Goes through all tables represented by old_schema and tries to migrate all items from them into new (current version) tables. 
 333 static bool UpgradeItemPhase2(SecDbConnectionRef dbt
, bool *inProgress
, CFErrorRef 
*error
) { 
 334     __block 
bool ok 
= true; 
 335     SecDbQueryRef query 
= NULL
; 
 336 #if TARGET_OS_EMBEDDED 
 337     __block 
int64_t itemsMigrated 
= 0; 
 338     struct timeval start
; 
 340     gettimeofday(&start
, NULL
); 
 343     // Go through all classes in new schema 
 344     const SecDbSchema 
*newSchema 
= kc_schemas
[0]; 
 345     for (const SecDbClass 
*const *class = newSchema
->classes
; *class != NULL 
&& !*inProgress
; class++) { 
 346         if(CFEqual((*class)->name
, tversion_class
.name
)) { 
 347             //Don't try to decrypt items in tversion table 
 351         const SecDbAttr 
*pdmn 
= SecDbClassAttrWithKind(*class, kSecDbAccessAttr
, error
); 
 356         // Prepare query to go through all non-DK|DKU items 
 358             query_destroy(query
, NULL
); 
 360         require_action_quiet(query 
= query_create(*class, SecMUSRGetAllViews(), NULL
, error
), out
, ok 
= false); 
 361         ok 
= SecDbItemSelect(query
, dbt
, error
, NULL
, ^bool(const SecDbAttr 
*attr
) { 
 362             // No simple per-attribute filtering. 
 364         }, ^bool(CFMutableStringRef sql
, bool *needWhere
) { 
 365             // Select only non-D-class items 
 366             SecDbAppendWhereOrAnd(sql
, needWhere
); 
 367             CFStringAppendFormat(sql
, NULL
, CFSTR("NOT %@ IN (?,?)"), pdmn
->name
); 
 369         }, ^bool(sqlite3_stmt 
*stmt
, int col
) { 
 370             return SecDbBindObject(stmt
, col
++, kSecAttrAccessibleAlways
, error
) && 
 371             SecDbBindObject(stmt
, col
++, kSecAttrAccessibleAlwaysThisDeviceOnly
, error
); 
 372         }, ^(SecDbItemRef item
, bool *stop
) { 
 373             CFErrorRef localError 
= NULL
; 
 375 #if TARGET_OS_EMBEDDED 
 380             if (SecDbItemEnsureDecrypted(item
, &localError
)) { 
 382                 // Delete SHA1 field from the item, so that it is newly recalculated before storing 
 383                 // the item into the new table. 
 384                 require_quiet(ok 
= SecDbItemSetValue(item
, SecDbClassAttrWithKind(item
->class, kSecDbSHA1Attr
, error
), 
 385                                                      kCFNull
, &localError
), out
); 
 387                 // Replace item with the new value in the table; this will cause the item to be decoded and recoded back, 
 388                 // incl. recalculation of item's hash. 
 389                 ok 
= SecDbItemUpdate(item
, item
, dbt
, false, &localError
); 
 393                 CFIndex status 
= CFErrorGetCode(localError
); 
 397                         // Items producing errSecDecode are silently dropped - they are not decodable and lost forever. 
 398                         (void)SecDbItemDelete(item
, dbt
, false, error
); 
 401                     case errSecInteractionNotAllowed
: 
 402                         // If we are still not able to decrypt the item because the class key is not released yet, 
 403                         // remember that DB still needs phase2 migration to be run next time a connection is made.  Also 
 404                         // stop iterating next items, it would be just waste of time because the whole iteration will be run 
 405                         // next time when this phase2 will be rerun. 
 410                     case errSecAuthNeeded
: 
 411                         // errSecAuthNeeded means that it is an ACL-based item which requires authentication (or at least 
 412                         // ACM context, which we do not have). 
 416                         //  Other errors should abort the migration completely. 
 417                         ok 
= CFErrorPropagate(CFRetainSafe(localError
), error
); 
 423             CFReleaseSafe(localError
); 
 424             *stop 
= *stop 
|| !ok
; 
 430 #if TARGET_OS_EMBEDDED 
 431     measureUpgradePhase2(&start
, SecBucket2Significant(itemsMigrated
)); 
 436         query_destroy(query
, NULL
); 
 440 static bool SecKeychainDbUpgradeFromVersion(SecDbConnectionRef dbt
, int version
, bool *inProgress
, CFErrorRef 
*error
) { 
 441     __block 
bool didPhase2 
= false; 
 442     __block 
bool ok 
= true; 
 444     // The schema we want to have is the first in the list of schemas. 
 445     const SecDbSchema 
*newSchema 
= kc_schemas
[0]; 
 447     // If DB schema is the one we want, we are done. 
 448     require_quiet(newSchema
->version 
!= version
, out
); 
 451         // Pre v6 keychains need to have WAL enabled, since SecDb only does this at db creation time. 
 452         // NOTE: This has to be run outside of a transaction. 
 453         require_action_quiet(ok 
= (SecDbExec(dbt
, CFSTR("PRAGMA auto_vacuum = FULL"), error
) && 
 454                                    SecDbExec(dbt
, CFSTR("PRAGMA journal_mode = WAL"), error
)), 
 455                              out
, secerror("unable to enable WAL or auto vacuum, marking DB as corrupt: %@", 
 456                                            error 
? *error 
: NULL
)); 
 459     ok 
&= SecDbTransaction(dbt
, kSecDbExclusiveTransactionType
, error
, ^(bool *commit
) { 
 460         CFStringRef sql 
= NULL
; 
 461         bool didPhase1 
= false; 
 463         // Get version again once we start a transaction, someone else might change the migration state. 
 465         require_quiet(ok 
= SecKeychainDbGetVersion(dbt
, &version
, error
), out
); 
 466         require_quiet(newSchema
->version 
!= version
, out
); 
 468         // If this is empty database, just create table according to schema and be done with it. 
 469         require_action_quiet(version 
!= 0, out
, ok 
= SecItemDbCreateSchema(dbt
, newSchema
, error
)); 
 471         int oldVersion 
= (version 
>> 16) & 0xffff; 
 473         require_action_quiet(version 
== newSchema
->version 
|| oldVersion 
== 0, out
, 
 474                              ok 
= SecDbError(SQLITE_CORRUPT
, error
, 
 475                                              CFSTR("Half migrated but obsolete DB found: found %d(%d) but %d is needed"), 
 476                                              version
, oldVersion
, newSchema
->version
)); 
 478         // Check whether we have both old and new tables in the DB. 
 479         if (oldVersion 
== 0) { 
 480             // Pure old-schema migration attempt, with full blown table renames etc (a.k.a. phase1) 
 481             oldVersion 
= version
; 
 482             version 
= newSchema
->version
; 
 484             // Find schema for old database. 
 485             const SecDbSchema 
*oldSchema 
= NULL
; 
 486             for (const SecDbSchema 
* const *pschema 
= kc_schemas
; *pschema
; ++pschema
) { 
 487                 if ((*pschema
)->version 
== oldVersion
) { 
 488                     oldSchema 
= *pschema
; 
 493             // If we are attempting to upgrade from a version for which we have no schema, fail. 
 494             require_action_quiet(oldSchema 
!= NULL
, out
, 
 495                                  ok 
= SecDbError(SQLITE_CORRUPT
, error
, CFSTR("no schema for version: %d"), oldVersion
); 
 496                                  secerror("no schema for version %d", oldVersion
)); 
 498             secnotice("upgr", "Upgrading from version %d to %d", oldVersion
, newSchema
->version
); 
 499             require(ok 
= UpgradeSchemaPhase1(dbt
, oldSchema
, error
), out
); 
 505             CFErrorRef phase2Error 
= NULL
; 
 507             // Lests try to go through non-D-class items in new tables and apply decode/encode on them 
 508             // If this fails the error will be ignored after doing a phase1 since but not in the second 
 509             // time when we are doing phase2. 
 510             ok 
= UpgradeItemPhase2(dbt
, inProgress
, &phase2Error
); 
 516                     SecErrorPropagate(phase2Error
, error
); 
 519             CFReleaseNull(phase2Error
); 
 523                 // If either migration path we did reported that the migration was complete, signalize that 
 524                 // in the version database by cleaning oldVersion (which is stored in upper halfword of the version) 
 525                 secnotice("upgr", "Done upgrading from version %d to %d", oldVersion
, newSchema
->version
); 
 533         // Update database version table. 
 534         version 
|= oldVersion 
<< 16; 
 535         sql 
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("UPDATE %@ SET %@ = %d"), 
 536                                        tversion_class
.name
, tversion_class
.attrs
[0]->name
, version
); 
 537         require_quiet(ok 
= SecDbExec(dbt
, sql
, error
), out
); 
 544     if (ok 
&& didPhase2
) { 
 545 #if TARGET_OS_EMBEDDED 
 546         ADClientAddValueForScalarKey(CFSTR("com.apple.keychain.migration-success"), 1); 
 551     if (!ok 
|| (error 
&& *error
)) { 
 553             secwarning("upgrade: error has been set but status is true"); 
 556         secerror("unable to complete upgrade, marking DB as corrupt: %@", error 
? *error 
: NULL
); 
 557         SecDbCorrupt(dbt
, error 
? *error 
: NULL
); 
 558 #if TARGET_OS_EMBEDDED 
 559         ADClientAddValueForScalarKey(CFSTR("com.apple.keychain.migration-failure"), 1); 
 566 /* AUDIT[securityd](done): 
 567    accessGroup (ok) is a caller provided, non NULL CFTypeRef. 
 569    Return true iff accessGroup is allowable according to accessGroups. 
 571 static bool accessGroupsAllows(CFArrayRef accessGroups
, 
 572     CFStringRef accessGroup
) { 
 573     /* NULL accessGroups is wildcard. */ 
 576     /* Make sure we have a string. */ 
 577     if (!isString(accessGroup
)) 
 580     /* Having the special accessGroup "*" allows access to all accessGroups. */ 
 581     CFRange range 
= { 0, CFArrayGetCount(accessGroups
) }; 
 583         (CFArrayContainsValue(accessGroups
, range
, accessGroup
) || 
 584          CFArrayContainsValue(accessGroups
, range
, CFSTR("*")))) 
 590 bool itemInAccessGroup(CFDictionaryRef item
, CFArrayRef accessGroups
) { 
 591     return accessGroupsAllows(accessGroups
, 
 592                               CFDictionaryGetValue(item
, kSecAttrAccessGroup
)); 
 596 static CF_RETURNS_RETAINED CFDataRef 
SecServerExportBackupableKeychain(SecDbConnectionRef dbt
, 
 597     SecurityClient 
*client
, 
 598     keybag_handle_t src_keybag
, keybag_handle_t dest_keybag
, CFErrorRef 
*error
) { 
 599     CFDataRef data_out 
= NULL
; 
 600     /* Export everything except the items for which SecItemIsSystemBound() 
 602     CFDictionaryRef keychain 
= SecServerCopyKeychainPlist(dbt
, client
, 
 603         src_keybag
, dest_keybag
, kSecBackupableItemFilter
, 
 606         data_out 
= CFPropertyListCreateData(kCFAllocatorDefault
, keychain
, 
 607                                              kCFPropertyListBinaryFormat_v1_0
, 
 615 static bool SecServerImportBackupableKeychain(SecDbConnectionRef dbt
, 
 616                                               SecurityClient 
*client
, 
 617                                               keybag_handle_t src_keybag
, 
 618                                               keybag_handle_t dest_keybag
, 
 622     return kc_transaction(dbt
, error
, ^{ 
 624         CFDictionaryRef keychain
; 
 625         keychain 
= CFPropertyListCreateWithData(kCFAllocatorDefault
, data
, 
 626                                                 kCFPropertyListImmutable
, NULL
, 
 629             if (isDictionary(keychain
)) { 
 630                 ok 
= SecServerImportKeychainInPlist(dbt
, 
 635                                                     kSecBackupableItemFilter
, 
 638                 ok 
= SecError(errSecParam
, error
, CFSTR("import: keychain is not a dictionary")); 
 646 static CFDataRef 
SecServerKeychainCreateBackup(SecDbConnectionRef dbt
, SecurityClient 
*client
, CFDataRef keybag
, 
 647     CFDataRef password
, CFErrorRef 
*error
) { 
 648     CFDataRef backup 
= NULL
; 
 649     keybag_handle_t backup_keybag
; 
 650     if (ks_open_keybag(keybag
, password
, &backup_keybag
, error
)) { 
 651         /* Export from system keybag to backup keybag. */ 
 652         backup 
= SecServerExportBackupableKeychain(dbt
, client
, KEYBAG_DEVICE
, backup_keybag
, error
); 
 653         if (!ks_close_keybag(backup_keybag
, error
)) { 
 654             CFReleaseNull(backup
); 
 660 static bool SecServerKeychainRestore(SecDbConnectionRef dbt
, 
 661                                      SecurityClient 
*client
, 
 667     keybag_handle_t backup_keybag
; 
 668     if (!ks_open_keybag(keybag
, password
, &backup_keybag
, error
)) 
 671     /* Import from backup keybag to system keybag. */ 
 672     bool ok 
= SecServerImportBackupableKeychain(dbt
, client
, backup_keybag
, KEYBAG_DEVICE
, 
 674     ok 
&= ks_close_keybag(backup_keybag
, error
); 
 680 // MARK - External SPI support code. 
 682 CFStringRef 
__SecKeychainCopyPath(void) { 
 683     CFStringRef kcRelPath 
= NULL
; 
 685         kcRelPath 
= CFSTR("keychain-2.db"); 
 687         kcRelPath 
= CFSTR("keychain-2-debug.db"); 
 690     CFStringRef kcPath 
= NULL
; 
 691     CFURLRef kcURL 
= SecCopyURLForFileInKeychainDirectory(kcRelPath
); 
 693         kcPath 
= CFURLCopyFileSystemPath(kcURL
, kCFURLPOSIXPathStyle
); 
 700 // MARK: kc_dbhandle init and reset 
 702 SecDbRef 
SecKeychainDbCreate(CFStringRef path
) { 
 703     return SecDbCreate(path
, ^bool (SecDbConnectionRef dbconn
, bool didCreate
, bool *callMeAgainForNextConnection
, CFErrorRef 
*error
) { 
 704         // Upgrade from version 0 means create the schema in empty db. 
 708             ok 
= SecKeychainDbGetVersion(dbconn
, &version
, error
); 
 710         ok 
= ok 
&& SecKeychainDbUpgradeFromVersion(dbconn
, version
, callMeAgainForNextConnection
, error
); 
 712             secerror("Upgrade %sfailed: %@", didCreate 
? "from v0 " : "", error 
? *error 
: NULL
); 
 718 static SecDbRef _kc_dbhandle 
= NULL
; 
 720 static void kc_dbhandle_init(void) { 
 721     SecDbRef oldHandle 
= _kc_dbhandle
; 
 723     CFStringRef dbPath 
= __SecKeychainCopyPath(); 
 725         _kc_dbhandle 
= SecKeychainDbCreate(dbPath
); 
 728         secerror("no keychain path available"); 
 731         secerror("replaced %@ with %@", oldHandle
, _kc_dbhandle
); 
 732         CFRelease(oldHandle
); 
 736 // A callback for the sqlite3_log() interface. 
 737 static void sqlite3Log(void *pArg
, int iErrCode
, const char *zMsg
){ 
 738     secinfo("sqlite3", "(%d) %s", iErrCode
, zMsg
); 
 741 static void setup_sqlite3_defaults_settings() { 
 742     int rx 
= sqlite3_config(SQLITE_CONFIG_LOG
, sqlite3Log
, NULL
); 
 743     if (SQLITE_OK 
!= rx
) { 
 744         secwarning("Could not set up sqlite global error logging to syslog: %d", rx
); 
 748 static dispatch_once_t _kc_dbhandle_once
; 
 750 static SecDbRef 
kc_dbhandle(void) { 
 751     dispatch_once(&_kc_dbhandle_once
, ^{ 
 752         setup_sqlite3_defaults_settings(); 
 758 /* For whitebox testing only */ 
 759 void SecKeychainDbReset(dispatch_block_t inbetween
) 
 761     CFStringRef dbPath 
= __SecKeychainCopyPath(); 
 765     CFReleaseNull(_kc_dbhandle
); 
 770     _kc_dbhandle 
= SecKeychainDbCreate(dbPath
); 
 774 static SecDbConnectionRef 
kc_aquire_dbt(bool writeAndRead
, CFErrorRef 
*error
) { 
 775     SecDbRef db 
= kc_dbhandle(); 
 777         SecError(errSecDataNotAvailable
, error
, CFSTR("failed to get a db handle")); 
 780     return SecDbConnectionAquire(db
, !writeAndRead
, error
); 
 783 /* Return a per thread dbt handle for the keychain.  If create is true create 
 784  the database if it does not yet exist.  If it is false, just return an 
 785  error if it fails to auto-create. */ 
 786 static bool kc_with_dbt(bool writeAndRead
, CFErrorRef 
*error
, bool (^perform
)(SecDbConnectionRef dbt
)) 
 788     // Make sure we initialize our engines before writing to the keychain 
 790         SecItemDataSourceFactoryGetDefault(); 
 793     SecDbConnectionRef dbt 
= kc_aquire_dbt(writeAndRead
, error
); 
 796         SecDbConnectionRelease(dbt
); 
 802 items_matching_issuer_parent(SecDbConnectionRef dbt
, CFArrayRef accessGroups
, CFDataRef musrView
, 
 803                              CFDataRef issuer
, CFArrayRef issuers
, int recurse
) 
 806     CFArrayRef results 
= NULL
; 
 810     if (CFArrayContainsValue(issuers
, CFRangeMake(0, CFArrayGetCount(issuers
)), issuer
)) 
 813     /* XXX make musr supported */ 
 814     const void *keys
[] = { kSecClass
, kSecReturnRef
, kSecAttrSubject 
}; 
 815     const void *vals
[] = { kSecClassCertificate
, kCFBooleanTrue
, issuer 
}; 
 816     CFDictionaryRef query 
= CFDictionaryCreate(kCFAllocatorDefault
, keys
, vals
, array_size(keys
), NULL
, NULL
); 
 821     CFErrorRef localError 
= NULL
; 
 822     q 
= query_create_with_limit(query
, musrView
, kSecMatchUnlimited
, &localError
); 
 825         s3dl_copy_matching(dbt
, q
, (CFTypeRef
*)&results
, accessGroups
, &localError
); 
 826         query_destroy(q
, &localError
); 
 829         secerror("items matching issuer parent: %@", localError
); 
 830         CFReleaseNull(localError
); 
 834     count 
= CFArrayGetCount(results
); 
 835     for (i 
= 0; (i 
< count
) && !found
; i
++) { 
 836         CFDictionaryRef cert_dict 
= (CFDictionaryRef
)CFArrayGetValueAtIndex(results
, i
); 
 837         CFDataRef cert_issuer 
= CFDictionaryGetValue(cert_dict
, kSecAttrIssuer
); 
 838         if (CFEqual(cert_issuer
, issuer
)) 
 841             found 
= items_matching_issuer_parent(dbt
, accessGroups
, musrView
, cert_issuer
, issuers
, recurse
); 
 843     CFReleaseSafe(results
); 
 848 bool match_item(SecDbConnectionRef dbt
, Query 
*q
, CFArrayRef accessGroups
, CFDictionaryRef item
) 
 850     if (q
->q_match_issuer
) { 
 851         CFDataRef issuer 
= CFDictionaryGetValue(item
, kSecAttrIssuer
); 
 852         if (!items_matching_issuer_parent(dbt
, accessGroups
, q
->q_musrView
, issuer
, q
->q_match_issuer
, 10 /*max depth*/)) 
 856     /* Add future match checks here. */ 
 861 /**************************************************************************** 
 862  **************** Beginning of Externally Callable Interface **************** 
 863  ****************************************************************************/ 
 865 void (*SecTaskDiagnoseEntitlements
)(CFArrayRef accessGroups
) = NULL
; 
 867 /* AUDIT[securityd](done): 
 868    query (ok) is a caller provided dictionary, only its cf type has been checked. 
 871 SecItemServerCopyMatching(CFDictionaryRef query
, CFTypeRef 
*result
, 
 872     SecurityClient 
*client
, CFErrorRef 
*error
) 
 874     CFArrayRef accessGroups 
= client
->accessGroups
; 
 877     if (!accessGroups 
|| 0 == (ag_count 
= CFArrayGetCount(accessGroups
))) { 
 878         if (SecTaskDiagnoseEntitlements
) 
 879             SecTaskDiagnoseEntitlements(accessGroups
); 
 880         return SecError(errSecMissingEntitlement
, error
, 
 881                          CFSTR("client has neither application-identifier nor keychain-access-groups entitlements")); 
 884     if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*"))) { 
 885         /* Having the special accessGroup "*" allows access to all accessGroups. */ 
 890     Query 
*q 
= query_create_with_limit(query
, client
->musr
, 1, error
); 
 892         CFStringRef agrp 
= CFDictionaryGetValue(q
->q_item
, kSecAttrAccessGroup
); 
 893         if (agrp 
&& accessGroupsAllows(accessGroups
, agrp
)) { 
 894             // TODO: Return an error if agrp is not NULL and accessGroupsAllows() fails above. 
 895             const void *val 
= agrp
; 
 896             accessGroups 
= CFArrayCreate(0, &val
, 1, &kCFTypeArrayCallBacks
); 
 898             CFRetainSafe(accessGroups
); 
 902         if (q
->q_sync_bubble 
&& client
->inMultiUser
) { 
 903             CFReleaseNull(q
->q_musrView
); 
 904             q
->q_musrView 
= SecMUSRCreateSyncBubbleUserUUID(q
->q_sync_bubble
); 
 905         } else if (client
->inMultiUser 
&& client
->isNetworkExtension
) { 
 906             CFReleaseNull(q
->q_musrView
); 
 907             q
->q_musrView 
= SecMUSRCreateBothUserAndSystemUUID(client
->uid
); 
 908         } else if (q
->q_system_keychain 
&& client
->inMultiUser
) { 
 909             CFReleaseNull(q
->q_musrView
); 
 910             q
->q_musrView 
= SecMUSRCopySystemKeychainUUID(); 
 912             q
->q_system_keychain 
= false; 
 916         query_set_caller_access_groups(q
, accessGroups
); 
 918         /* Sanity check the query. */ 
 919         if (q
->q_system_keychain 
&& !client
->allowSystemKeychain
) { 
 920             ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("client doesn't have entitlement for system keychain")); 
 921         } else if (q
->q_sync_bubble 
&& !client
->allowSyncBubbleKeychain
) { 
 922             ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("client doesn't have entitlement for syncbubble keychain")); 
 923         } else if (q
->q_system_keychain 
&& q
->q_sync_bubble
) { 
 924             ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("can't do both system and syncbubble keychain")); 
 925         } else if (q
->q_use_item_list
) { 
 926             ok 
= SecError(errSecUseItemListUnsupported
, error
, CFSTR("use item list unsupported")); 
 927         } else if (q
->q_match_issuer 
&& ((q
->q_class 
!= &cert_class
) && 
 928                     (q
->q_class 
!= &identity_class
))) { 
 929             ok 
= SecError(errSecUnsupportedOperation
, error
, CFSTR("unsupported match attribute")); 
 930         } else if (q
->q_return_type 
!= 0 && result 
== NULL
) { 
 931             ok 
= SecError(errSecReturnMissingPointer
, error
, CFSTR("missing pointer")); 
 932         } else if (!q
->q_error
) { 
 933             ok 
= kc_with_dbt(false, error
, ^(SecDbConnectionRef dbt
) { 
 934                 return s3dl_copy_matching(dbt
, q
, result
, accessGroups
, error
); 
 938         CFReleaseSafe(accessGroups
); 
 939         if (!query_destroy(q
, error
)) 
 947 _SecItemCopyMatching(CFDictionaryRef query
, SecurityClient 
*client
, CFTypeRef 
*result
, CFErrorRef 
*error
) { 
 948     return SecItemServerCopyMatching(query
, result
, client
, error
); 
 953 SecItemSynchronizable(CFDictionaryRef query
) 
 956     CFTypeRef value 
= CFDictionaryGetValue(query
, kSecAttrSynchronizable
); 
 957     if (isBoolean(value
)) 
 958         return CFBooleanGetValue(value
); 
 959     else if (isNumber(value
)) { 
 961         (void)CFNumberGetValue(value
, kCFNumberSInt32Type
, &number
); 
 970 /* AUDIT[securityd](done): 
 971    attributes (ok) is a caller provided dictionary, only its cf type has 
 975 _SecItemAdd(CFDictionaryRef attributes
, SecurityClient 
*client
, CFTypeRef 
*result
, CFErrorRef 
*error
) 
 977     CFArrayRef accessGroups 
= client
->accessGroups
; 
 981     if (!accessGroups 
|| 0 == (ag_count 
= CFArrayGetCount(accessGroups
))) { 
 982         if (SecTaskDiagnoseEntitlements
) 
 983             SecTaskDiagnoseEntitlements(accessGroups
); 
 984         return SecError(errSecMissingEntitlement
, error
, 
 985                            CFSTR("client has neither application-identifier nor keychain-access-groups entitlements")); 
 988     Query 
*q 
= query_create_with_limit(attributes
, client
->musr
, 0, error
); 
 990         /* Access group sanity checking. */ 
 991         CFStringRef agrp 
= (CFStringRef
)CFDictionaryGetValue(attributes
, 
 992             kSecAttrAccessGroup
); 
 994         /* Having the special accessGroup "*" allows access to all accessGroups. */ 
 995         if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*"))) 
 999             /* The user specified an explicit access group, validate it. */ 
1000             if (!accessGroupsAllows(accessGroups
, agrp
)) 
1001                 ok 
= SecError(errSecNoAccessForItem
, error
, CFSTR("NoAccessForItem")); 
1003             agrp 
= (CFStringRef
)CFArrayGetValueAtIndex(client
->accessGroups
, 0); 
1005             /* We are using an implicit access group, add it as if the user 
1006                specified it as an attribute. */ 
1007             query_add_attribute(kSecAttrAccessGroup
, agrp
, q
); 
1009 #if TARGET_OS_IPHONE 
1010         if (q
->q_system_keychain 
&& client
->inMultiUser
) { 
1011             CFReleaseNull(q
->q_musrView
); 
1012             q
->q_musrView 
= SecMUSRCopySystemKeychainUUID(); 
1014             q
->q_system_keychain 
= false; 
1016         query_add_attribute_with_desc(&v8musr
, q
->q_musrView
, q
); 
1020             query_ensure_access_control(q
, agrp
); 
1022             if (q
->q_system_keychain 
&& !client
->allowSystemKeychain
) { 
1023                 ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("client doesn't have entitlement for system keychain")); 
1024             } else if (q
->q_sync_bubble 
&& !client
->allowSyncBubbleKeychain
) { 
1025                 ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("client doesn't have entitlement for syncbubble keychain")); 
1026 #if TARGET_OS_IPHONE 
1027             } else if (q
->q_system_keychain 
&& SecItemSynchronizable(attributes
) && !client
->inMultiUser
) { 
1028                 ok 
= SecError(errSecInvalidKey
, error
, CFSTR("Can't store system keychain and synchronizable")); 
1030             } else if (q
->q_row_id
) { 
1031                 ok 
= SecError(errSecValuePersistentRefUnsupported
, error
, CFSTR("q_row_id"));  // TODO: better error string 
1032             } else if (!q
->q_error
) { 
1033                 ok 
= kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
){ 
1034                     return kc_transaction(dbt
, error
, ^{ 
1035                         query_pre_add(q
, true); 
1036                         return s3dl_query_add(dbt
, q
, result
, error
); 
1041         ok 
= query_notify_and_destroy(q
, ok
, error
); 
1048 /* AUDIT[securityd](done): 
1049    query (ok) and attributesToUpdate (ok) are a caller provided dictionaries, 
1050        only their cf types have been checked. 
1053 _SecItemUpdate(CFDictionaryRef query
, CFDictionaryRef attributesToUpdate
, 
1054                SecurityClient 
*client
, CFErrorRef 
*error
) 
1056     CFArrayRef accessGroups 
= client
->accessGroups
; 
1059     if (!accessGroups 
|| 0 == (ag_count 
= CFArrayGetCount(accessGroups
))) { 
1060         if (SecTaskDiagnoseEntitlements
) 
1061             SecTaskDiagnoseEntitlements(accessGroups
); 
1062         return SecError(errSecMissingEntitlement
, error
, 
1063                          CFSTR("client has neither application-identifier nor keychain-access-groups entitlements")); 
1066     if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*"))) { 
1067         /* Having the special accessGroup "*" allows access to all accessGroups. */ 
1068         accessGroups 
= NULL
; 
1072     Query 
*q 
= query_create_with_limit(query
, client
->musr
, kSecMatchUnlimited
, error
); 
1077 #if TARGET_OS_IPHONE 
1078         if (q
->q_system_keychain 
&& client
->inMultiUser
) { 
1079             CFReleaseNull(q
->q_musrView
); 
1080             q
->q_musrView 
= SecMUSRCopySystemKeychainUUID(); 
1082             q
->q_system_keychain 
= false; 
1086         /* Sanity check the query. */ 
1087         query_set_caller_access_groups(q
, accessGroups
); 
1088         if (q
->q_system_keychain 
&& !client
->allowSystemKeychain
) { 
1089             ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("client doesn't have entitlement for system keychain")); 
1090         } else if (q
->q_sync_bubble 
&& !client
->allowSyncBubbleKeychain
) { 
1091             ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("client doesn't have entitlement for syncbubble keychain")); 
1092 #if TARGET_OS_IPHONE 
1093         } else if (q
->q_system_keychain 
&& SecItemSynchronizable(attributesToUpdate
) && !client
->inMultiUser
) { 
1094             ok 
= SecError(errSecInvalidKey
, error
, CFSTR("Can't update an system keychain item with synchronizable")); 
1096         } else if (q
->q_use_item_list
) { 
1097             ok 
= SecError(errSecUseItemListUnsupported
, error
, CFSTR("use item list not supported")); 
1098         } else if (q
->q_return_type 
& kSecReturnDataMask
) { 
1099             /* Update doesn't return anything so don't ask for it. */ 
1100             ok 
= SecError(errSecReturnDataUnsupported
, error
, CFSTR("return data not supported by update")); 
1101         } else if (q
->q_return_type 
& kSecReturnAttributesMask
) { 
1102             ok 
= SecError(errSecReturnAttributesUnsupported
, error
, CFSTR("return attributes not supported by update")); 
1103         } else if (q
->q_return_type 
& kSecReturnRefMask
) { 
1104             ok 
= SecError(errSecReturnRefUnsupported
, error
, CFSTR("return ref not supported by update")); 
1105         } else if (q
->q_return_type 
& kSecReturnPersistentRefMask
) { 
1106             ok 
= SecError(errSecReturnPersistentRefUnsupported
, error
, CFSTR("return persistent ref not supported by update")); 
1108             /* Access group sanity checking. */ 
1109             CFStringRef agrp 
= (CFStringRef
)CFDictionaryGetValue(attributesToUpdate
, 
1110                 kSecAttrAccessGroup
); 
1112                 /* The user is attempting to modify the access group column, 
1113                    validate it to make sure the new value is allowable. */ 
1114                 if (!accessGroupsAllows(accessGroups
, agrp
)) { 
1115                     ok 
= SecError(errSecNoAccessForItem
, error
, CFSTR("accessGroup %@ not in %@"), agrp
, accessGroups
); 
1121         ok 
= kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
) { 
1122             return kc_transaction(dbt
, error
, ^{ 
1123                 return s3dl_query_update(dbt
, q
, attributesToUpdate
, accessGroups
, error
); 
1128         ok 
= query_notify_and_destroy(q
, ok
, error
); 
1134 /* AUDIT[securityd](done): 
1135    query (ok) is a caller provided dictionary, only its cf type has been checked. 
1138 _SecItemDelete(CFDictionaryRef query
, SecurityClient 
*client
, CFErrorRef 
*error
) 
1140     CFArrayRef accessGroups 
= client
->accessGroups
; 
1143     if (!accessGroups 
|| 0 == (ag_count 
= CFArrayGetCount(accessGroups
))) { 
1144         if (SecTaskDiagnoseEntitlements
) 
1145             SecTaskDiagnoseEntitlements(accessGroups
); 
1146         return SecError(errSecMissingEntitlement
, error
, 
1147                            CFSTR("client has neither application-identifier nor keychain-access-groups entitlements")); 
1150     if (CFArrayContainsValue(accessGroups
, CFRangeMake(0, ag_count
), CFSTR("*"))) { 
1151         /* Having the special accessGroup "*" allows access to all accessGroups. */ 
1152         accessGroups 
= NULL
; 
1155     Query 
*q 
= query_create_with_limit(query
, client
->musr
, kSecMatchUnlimited
, error
); 
1158 #if TARGET_OS_IPHONE 
1159         if (q
->q_system_keychain 
&& client
->inMultiUser
) { 
1160             CFReleaseNull(q
->q_musrView
); 
1161             q
->q_musrView 
= SecMUSRCopySystemKeychainUUID(); 
1163             q
->q_system_keychain 
= false; 
1167         query_set_caller_access_groups(q
, accessGroups
); 
1168         /* Sanity check the query. */ 
1169         if (q
->q_system_keychain 
&& !client
->allowSystemKeychain
) { 
1170             ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("client doesn't have entitlement for system keychain")); 
1171         } else if (q
->q_sync_bubble 
&& !client
->allowSyncBubbleKeychain
) { 
1172             ok 
= SecError(errSecMissingEntitlement
, error
, CFSTR("client doesn't have entitlement for syncbubble keychain")); 
1173         } else if (q
->q_limit 
!= kSecMatchUnlimited
) { 
1174             ok 
= SecError(errSecMatchLimitUnsupported
, error
, CFSTR("match limit not supported by delete")); 
1175         } else if (query_match_count(q
) != 0) { 
1176             ok 
= SecError(errSecItemMatchUnsupported
, error
, CFSTR("match not supported by delete")); 
1177         } else if (q
->q_ref
) { 
1178             ok 
= SecError(errSecValueRefUnsupported
, error
, CFSTR("value ref not supported by delete")); 
1179         } else if (q
->q_row_id 
&& query_attr_count(q
)) { 
1180             ok 
= SecError(errSecItemIllegalQuery
, error
, CFSTR("rowid and other attributes are mutually exclusive")); 
1182             ok 
= kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
) { 
1183                 return kc_transaction(dbt
, error
, ^{ 
1184                     return s3dl_query_delete(dbt
, q
, accessGroups
, error
); 
1188         ok 
= query_notify_and_destroy(q
, ok
, error
); 
1196 /* AUDIT[securityd](done): 
1197    No caller provided inputs. 
1200 SecItemServerDeleteAll(CFErrorRef 
*error
) { 
1201     return kc_with_dbt(true, error
, ^bool (SecDbConnectionRef dbt
) { 
1202         return (kc_transaction(dbt
, error
, ^bool { 
1203             return (SecDbExec(dbt
, CFSTR("DELETE from genp;"), error
) && 
1204                     SecDbExec(dbt
, CFSTR("DELETE from inet;"), error
) && 
1205                     SecDbExec(dbt
, CFSTR("DELETE from cert;"), error
) && 
1206                     SecDbExec(dbt
, CFSTR("DELETE from keys;"), error
)); 
1207         }) && SecDbExec(dbt
, CFSTR("VACUUM;"), error
)); 
1212 _SecItemDeleteAll(CFErrorRef 
*error
) { 
1213     return SecItemServerDeleteAll(error
); 
1218 // MARK: Shared web credentials 
1221 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v); 
1223 SEC_CONST_DECL (kSecSafariAccessGroup
, "com.apple.cfnetwork"); 
1224 SEC_CONST_DECL (kSecSafariDefaultComment
, "default"); 
1225 SEC_CONST_DECL (kSecSafariPasswordsNotSaved
, "Passwords not saved"); 
1226 SEC_CONST_DECL (kSecSharedCredentialUrlScheme
, "https://"); 
1227 SEC_CONST_DECL (kSecSharedWebCredentialsService
, "webcredentials"); 
1229 #if TARGET_OS_IPHONE && !TARGET_OS_WATCH 
1230 static dispatch_once_t                  sSecSWCInitializeOnce   
= 0; 
1231 static void *                                   sSecSWCLibrary                  
= NULL
; 
1232 static SWCCheckService_f                sSWCCheckService_f              
= NULL
; 
1233 static SWCSetServiceFlags_f             sSWCSetServiceFlags_f   
= NULL
; 
1235 static OSStatus 
_SecSWCEnsuredInitialized(void); 
1237 static OSStatus 
_SecSWCEnsuredInitialized(void) 
1239     __block OSStatus status 
= errSecNotAvailable
; 
1241     dispatch_once(&sSecSWCInitializeOnce
, ^{ 
1242         sSecSWCLibrary 
= dlopen("/System/Library/PrivateFrameworks/SharedWebCredentials.framework/SharedWebCredentials", RTLD_LAZY 
| RTLD_LOCAL
); 
1243         assert(sSecSWCLibrary
); 
1244         if (sSecSWCLibrary
) { 
1245             sSWCCheckService_f 
= (SWCCheckService_f
)(uintptr_t) dlsym(sSecSWCLibrary
, "SWCCheckService"); 
1246             sSWCSetServiceFlags_f 
= (SWCSetServiceFlags_f
)(uintptr_t) dlsym(sSecSWCLibrary
, "SWCSetServiceFlags"); 
1250     if (sSWCCheckService_f 
&& sSWCSetServiceFlags_f
) { 
1257 #if !TARGET_IPHONE_SIMULATOR 
1259 _SecAppDomainApprovalStatus(CFStringRef appID
, CFStringRef fqdn
, CFErrorRef 
*error
) 
1261     __block SWCFlags flags 
= kSWCFlags_None
; 
1263 #if TARGET_OS_IPHONE && !TARGET_OS_WATCH 
1264     OSStatus status 
= _SecSWCEnsuredInitialized(); 
1266         SecError(status
, error
, CFSTR("SWC initialize failed")); 
1269     CFRetainSafe(appID
); 
1271     dispatch_semaphore_t semaphore 
= dispatch_semaphore_create(0); 
1272     dispatch_retain(semaphore
); 
1273     if (0 == sSWCCheckService_f(kSecSharedWebCredentialsService
, appID
, fqdn
, 
1274         ^void (OSStatus inStatus
, SWCFlags inFlags
, CFDictionaryRef inDetails
) { 
1275             if (!inStatus
) { flags 
= inFlags
; } 
1276             CFReleaseSafe(appID
); 
1277             CFReleaseSafe(fqdn
); 
1278             dispatch_semaphore_signal(semaphore
); 
1279             dispatch_release(semaphore
); 
1280             //secerror("SWCCheckService: inStatus=%d, flags=%0X", inStatus, flags); 
1283         // wait for the block to complete, as we need its answer 
1284         dispatch_semaphore_wait(semaphore
, DISPATCH_TIME_FOREVER
); 
1286     else // didn't queue the block 
1288         CFReleaseSafe(appID
); 
1289         CFReleaseSafe(fqdn
); 
1290         dispatch_release(semaphore
); 
1292     dispatch_release(semaphore
); 
1294     flags 
|= (kSWCFlag_SiteApproved
); 
1297     if (!error
) { return flags
; } 
1300     // check website approval status 
1301     if (!(flags 
& kSWCFlag_SiteApproved
)) { 
1302         if (flags 
& kSWCFlag_Pending
) { 
1303             SecError(errSecAuthFailed
, error
, CFSTR("Approval is pending for \"%@\", try later"), fqdn
); 
1305             SecError(errSecAuthFailed
, error
, CFSTR("\"%@\" failed to approve \"%@\""), fqdn
, appID
); 
1310     // check user approval status 
1311     if (flags 
& kSWCFlag_UserDenied
) { 
1312         SecError(errSecAuthFailed
, error
, CFSTR("User denied access to \"%@\" by \"%@\""), fqdn
, appID
); 
1318 #if !TARGET_IPHONE_SIMULATOR 
1320 _SecEntitlementContainsDomainForService(CFArrayRef domains
, CFStringRef domain
, CFStringRef service
) 
1322     bool result 
= false; 
1323     CFIndex idx
, count 
= (domains
) ? CFArrayGetCount(domains
) : (CFIndex
) 0; 
1324     if (!count 
|| !domain 
|| !service
) { 
1327     for (idx
=0; idx 
< count
; idx
++) { 
1328         CFStringRef str 
= (CFStringRef
) CFArrayGetValueAtIndex(domains
, idx
); 
1329         if (str 
&& CFStringHasPrefix(str
, kSecSharedWebCredentialsService
)) { 
1330             CFIndex prefix_len 
= CFStringGetLength(kSecSharedWebCredentialsService
)+1; 
1331             CFIndex substr_len 
= CFStringGetLength(str
) - prefix_len
; 
1332             CFRange range 
= { prefix_len
, substr_len 
}; 
1333             CFStringRef substr 
= CFStringCreateWithSubstring(kCFAllocatorDefault
, str
, range
); 
1334             if (substr 
&& CFEqual(substr
, domain
)) { 
1337             CFReleaseSafe(substr
); 
1348 _SecAddNegativeWebCredential(SecurityClient 
*client
, CFStringRef fqdn
, CFStringRef appID
, bool forSafari
) 
1350     bool result 
= false; 
1351     if (!fqdn
) { return result
; } 
1353 #if TARGET_OS_IPHONE && !TARGET_OS_WATCH 
1354     OSStatus status 
= _SecSWCEnsuredInitialized(); 
1355     if (status
) { return false; } 
1357     // update our database 
1358     CFRetainSafe(appID
); 
1360     if (0 == sSWCSetServiceFlags_f(kSecSharedWebCredentialsService
, 
1361         appID
, fqdn
, kSWCFlag_ExternalMask
, kSWCFlag_UserDenied
, 
1362         ^void(OSStatus inStatus
, SWCFlags inNewFlags
){ 
1363             CFReleaseSafe(appID
); 
1364             CFReleaseSafe(fqdn
); 
1369     else // didn't queue the block 
1371         CFReleaseSafe(appID
); 
1372         CFReleaseSafe(fqdn
); 
1375     if (!forSafari
) { return result
; } 
1377     // below this point: create a negative Safari web credential item 
1379     CFMutableDictionaryRef attrs 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
1380     if (!attrs
) { return result
; } 
1382     CFErrorRef error 
= NULL
; 
1383     CFStringRef accessGroup 
= CFSTR("*"); 
1384     SecurityClient swcclient 
= { 
1386         .accessGroups 
=  CFArrayCreate(kCFAllocatorDefault
, (const void **)&accessGroup
, 1, &kCFTypeArrayCallBacks
), 
1387         .allowSystemKeychain 
= false, 
1388         .allowSyncBubbleKeychain 
= false, 
1389         .isNetworkExtension 
= false, 
1390         .musr 
= client
->musr
, 
1393     CFDictionaryAddValue(attrs
, kSecClass
, kSecClassInternetPassword
); 
1394     CFDictionaryAddValue(attrs
, kSecAttrAccessGroup
, kSecSafariAccessGroup
); 
1395     CFDictionaryAddValue(attrs
, kSecAttrAuthenticationType
, kSecAttrAuthenticationTypeHTMLForm
); 
1396     CFDictionaryAddValue(attrs
, kSecAttrProtocol
, kSecAttrProtocolHTTPS
); 
1397     CFDictionaryAddValue(attrs
, kSecAttrServer
, fqdn
); 
1398     CFDictionaryAddValue(attrs
, kSecAttrSynchronizable
, kCFBooleanTrue
); 
1400     (void)_SecItemDelete(attrs
, &swcclient
, &error
); 
1401     CFReleaseNull(error
); 
1403     CFDictionaryAddValue(attrs
, kSecAttrAccount
, kSecSafariPasswordsNotSaved
); 
1404     CFDictionaryAddValue(attrs
, kSecAttrComment
, kSecSafariDefaultComment
); 
1406     CFStringRef label 
= CFStringCreateWithFormat(kCFAllocatorDefault
, 
1407                                                  NULL
, CFSTR("%@ (%@)"), fqdn
, kSecSafariPasswordsNotSaved
); 
1409         CFDictionaryAddValue(attrs
, kSecAttrLabel
, label
); 
1410         CFReleaseSafe(label
); 
1414     CFDataRef data 
= CFDataCreate(kCFAllocatorDefault
, &space
, 1); 
1416         CFDictionarySetValue(attrs
, kSecValueData
, data
); 
1417         CFReleaseSafe(data
); 
1420     CFTypeRef addResult 
= NULL
; 
1421     result 
= _SecItemAdd(attrs
, &swcclient
, &addResult
, &error
); 
1423     CFReleaseSafe(addResult
); 
1424     CFReleaseSafe(error
); 
1425     CFReleaseSafe(attrs
); 
1426     CFReleaseSafe(swcclient
.accessGroups
); 
1431 /* Specialized version of SecItemAdd for shared web credentials */ 
1433 _SecAddSharedWebCredential(CFDictionaryRef attributes
, 
1434                            SecurityClient 
*client
, 
1435                            const audit_token_t 
*clientAuditToken
, 
1442     SecurityClient swcclient 
= {}; 
1444     CFStringRef fqdn 
= CFRetainSafe(CFDictionaryGetValue(attributes
, kSecAttrServer
)); 
1445     CFStringRef account 
= CFDictionaryGetValue(attributes
, kSecAttrAccount
); 
1446     CFStringRef password 
= CFDictionaryGetValue(attributes
, CFSTR("spwd") /* kSecSharedPassword */); 
1447     CFStringRef accessGroup 
= CFSTR("*"); 
1448     CFMutableDictionaryRef query 
= NULL
, attrs 
= NULL
; 
1452     // check autofill enabled status 
1453     if (!swca_autofill_enabled(clientAuditToken
)) { 
1454         SecError(errSecBadReq
, error
, CFSTR("Autofill is not enabled in Safari settings")); 
1458     // parse fqdn with CFURL here, since it could be specified as domain:port 
1460         CFStringRef urlStr 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@%@"), kSecSharedCredentialUrlScheme
, fqdn
); 
1462             CFURLRef url 
= CFURLCreateWithString(kCFAllocatorDefault
, urlStr
, nil
); 
1464                 CFStringRef hostname 
= CFURLCopyHostName(url
); 
1466                     CFReleaseSafe(fqdn
); 
1468                     port 
= CFURLGetPortNumber(url
); 
1472             CFReleaseSafe(urlStr
); 
1477         SecError(errSecParam
, error
, CFSTR("No account provided")); 
1481         SecError(errSecParam
, error
, CFSTR("No domain provided")); 
1485 #if TARGET_IPHONE_SIMULATOR 
1486     secerror("app/site association entitlements not checked in Simulator"); 
1488     OSStatus status 
= errSecMissingEntitlement
; 
1489     // validate that fqdn is part of caller's shared credential domains entitlement 
1491         SecError(status
, error
, CFSTR("Missing application-identifier entitlement")); 
1494     if (_SecEntitlementContainsDomainForService(domains
, fqdn
, kSecSharedWebCredentialsService
)) { 
1495         status 
= errSecSuccess
; 
1497     if (errSecSuccess 
!= status
) { 
1498         CFStringRef msg 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, 
1499                                                    CFSTR("%@ not found in %@ entitlement"), fqdn
, kSecEntitlementAssociatedDomains
); 
1501             msg 
= CFRetain(CFSTR("Requested domain not found in entitlement")); 
1503         SecError(status
, error
, CFSTR("%@"), msg
); 
1509 #if TARGET_IPHONE_SIMULATOR 
1510     secerror("Ignoring app/site approval state in the Simulator."); 
1512     // get approval status for this app/domain pair 
1513     SWCFlags flags 
= _SecAppDomainApprovalStatus(appID
, fqdn
, error
); 
1514     if (!(flags 
& kSWCFlag_SiteApproved
)) { 
1519     // give ourselves access to see matching items for kSecSafariAccessGroup 
1520     swcclient
.task 
= NULL
; 
1521     swcclient
.accessGroups 
=  CFArrayCreate(kCFAllocatorDefault
, (const void **)&accessGroup
, 1, &kCFTypeArrayCallBacks
); 
1522     swcclient
.allowSystemKeychain 
= false; 
1523     swcclient
.musr 
= client
->musr
; 
1524     swcclient
.allowSystemKeychain 
= false; 
1525     swcclient
.allowSyncBubbleKeychain 
= false; 
1526     swcclient
.isNetworkExtension 
= false; 
1529     // create lookup query 
1530     query 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
1532         SecError(errSecAllocate
, error
, CFSTR("Unable to create query dictionary")); 
1535     CFDictionaryAddValue(query
, kSecClass
, kSecClassInternetPassword
); 
1536     CFDictionaryAddValue(query
, kSecAttrAccessGroup
, kSecSafariAccessGroup
); 
1537     CFDictionaryAddValue(query
, kSecAttrAuthenticationType
, kSecAttrAuthenticationTypeHTMLForm
); 
1538     CFDictionaryAddValue(query
, kSecAttrServer
, fqdn
); 
1539     CFDictionaryAddValue(query
, kSecAttrSynchronizable
, kCFBooleanTrue
); 
1541     // check for presence of Safari's negative entry ('passwords not saved') 
1542     CFDictionarySetValue(query
, kSecAttrAccount
, kSecSafariPasswordsNotSaved
); 
1543     ok 
= _SecItemCopyMatching(query
, &swcclient
, result
, error
); 
1544     if(result
) CFReleaseNull(*result
); 
1545     CFReleaseNull(*error
); 
1547         SecError(errSecDuplicateItem
, error
, CFSTR("Item already exists for this server")); 
1551     // now use the provided account (and optional port number, if one was present) 
1552     CFDictionarySetValue(query
, kSecAttrAccount
, account
); 
1553     if (port 
< -1 || port 
> 0) { 
1554         SInt16 portValueShort 
= (port 
& 0xFFFF); 
1555         CFNumberRef portNumber 
= CFNumberCreate(NULL
, kCFNumberSInt16Type
, &portValueShort
); 
1556         CFDictionaryAddValue(query
, kSecAttrPort
, portNumber
); 
1557         CFReleaseSafe(portNumber
); 
1560     // look up existing password 
1561     if (_SecItemCopyMatching(query
, &swcclient
, result
, error
)) { 
1562         // found it, so this becomes either an "update password" or "delete password" operation 
1563         if(result
) CFReleaseNull(*result
); 
1564         CFReleaseNull(*error
); 
1565         bool update 
= (password 
!= NULL
); 
1567             attrs 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
1568             CFDataRef credential 
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, password
, kCFStringEncodingUTF8
, 0); 
1569             CFDictionaryAddValue(attrs
, kSecValueData
, credential
); 
1570             CFReleaseSafe(credential
); 
1571             CFDictionaryAddValue(attrs
, kSecAttrComment
, kSecSafariDefaultComment
); 
1573             // confirm the update 
1574             // (per rdar://16676310 we always prompt, even if there was prior user approval) 
1575             ok 
= /*approved ||*/ swca_confirm_operation(swca_update_request_id
, clientAuditToken
, query
, error
, 
1576                 ^void (CFStringRef fqdn
) { _SecAddNegativeWebCredential(client
, fqdn
, appID
, false); }); 
1578                 ok 
= _SecItemUpdate(query
, attrs
, &swcclient
, error
); 
1582             // confirm the delete 
1583             // (per rdar://16676288 we always prompt, even if there was prior user approval) 
1584             ok 
= /*approved ||*/ swca_confirm_operation(swca_delete_request_id
, clientAuditToken
, query
, error
, 
1585                 ^void (CFStringRef fqdn
) { _SecAddNegativeWebCredential(client
, fqdn
, appID
, false); }); 
1587                 ok 
= _SecItemDelete(query
, &swcclient
, error
); 
1591             CFReleaseNull(*error
); 
1595     if(result
) CFReleaseNull(*result
); 
1596     CFReleaseNull(*error
); 
1598     // password does not exist, so prepare to add it 
1600         // a NULL password value removes the existing credential. Since we didn't find it, this is a no-op. 
1605         CFStringRef label 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@ (%@)"), fqdn
, account
); 
1607             CFDictionaryAddValue(query
, kSecAttrLabel
, label
); 
1608             CFReleaseSafe(label
); 
1610         // NOTE: we always expect to use HTTPS for web forms. 
1611         CFDictionaryAddValue(query
, kSecAttrProtocol
, kSecAttrProtocolHTTPS
); 
1613         CFDataRef credential 
= CFStringCreateExternalRepresentation(kCFAllocatorDefault
, password
, kCFStringEncodingUTF8
, 0); 
1614         CFDictionarySetValue(query
, kSecValueData
, credential
); 
1615         CFReleaseSafe(credential
); 
1616         CFDictionarySetValue(query
, kSecAttrComment
, kSecSafariDefaultComment
); 
1618         CFReleaseSafe(swcclient
.accessGroups
); 
1619         swcclient
.accessGroups 
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&kSecSafariAccessGroup
, 1, &kCFTypeArrayCallBacks
); 
1621         // mark the item as created by this function 
1622         const int32_t creator_value 
= 'swca'; 
1623         CFNumberRef creator 
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &creator_value
); 
1625             CFDictionarySetValue(query
, kSecAttrCreator
, creator
); 
1626             CFReleaseSafe(creator
); 
1631             // (per rdar://16680019, we won't prompt here in the normal case) 
1632             ok 
= /*approved ||*/ swca_confirm_operation(swca_add_request_id
, clientAuditToken
, query
, error
, 
1633                 ^void (CFStringRef fqdn
) { _SecAddNegativeWebCredential(client
, fqdn
, appID
, false); }); 
1637         ok 
= _SecItemAdd(query
, &swcclient
, result
, error
); 
1641     CFReleaseSafe(attrs
); 
1642     CFReleaseSafe(query
); 
1643     CFReleaseSafe(swcclient
.accessGroups
); 
1644     CFReleaseSafe(fqdn
); 
1648 /* Specialized version of SecItemCopyMatching for shared web credentials */ 
1650 _SecCopySharedWebCredential(CFDictionaryRef query
, 
1651                             SecurityClient 
*client
, 
1652                             const audit_token_t 
*clientAuditToken
, 
1658     CFMutableArrayRef credentials 
= NULL
; 
1659     CFMutableArrayRef foundItems 
= NULL
; 
1660     CFMutableArrayRef fqdns 
= NULL
; 
1661     CFStringRef fqdn 
= NULL
; 
1662     CFStringRef account 
= NULL
; 
1667     require_quiet(result
, cleanup
); 
1668     credentials 
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
); 
1669     foundItems 
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
); 
1670     fqdns 
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
); 
1672     // give ourselves access to see matching items for kSecSafariAccessGroup 
1673     CFStringRef accessGroup 
= CFSTR("*"); 
1674     SecurityClient swcclient 
= { 
1676         .accessGroups 
=  CFArrayCreate(kCFAllocatorDefault
, (const void **)&accessGroup
, 1, &kCFTypeArrayCallBacks
), 
1677         .allowSystemKeychain 
= false, 
1678         .allowSyncBubbleKeychain 
= false, 
1679         .isNetworkExtension 
= false, 
1680         .musr 
= client
->musr
, 
1683     // On input, the query dictionary contains optional fqdn and account entries. 
1684     fqdn 
= CFDictionaryGetValue(query
, kSecAttrServer
); 
1685     account 
= CFDictionaryGetValue(query
, kSecAttrAccount
); 
1687     // Check autofill enabled status 
1688     if (!swca_autofill_enabled(clientAuditToken
)) { 
1689         SecError(errSecBadReq
, error
, CFSTR("Autofill is not enabled in Safari settings")); 
1693     // Check fqdn; if NULL, add domains from caller's entitlement. 
1695         CFArrayAppendValue(fqdns
, fqdn
); 
1698         CFIndex idx
, count 
= CFArrayGetCount(domains
); 
1699         for (idx
=0; idx 
< count
; idx
++) { 
1700             CFStringRef str 
= (CFStringRef
) CFArrayGetValueAtIndex(domains
, idx
); 
1701             // Parse the entry for our service label prefix 
1702             if (str 
&& CFStringHasPrefix(str
, kSecSharedWebCredentialsService
)) { 
1703                 CFIndex prefix_len 
= CFStringGetLength(kSecSharedWebCredentialsService
)+1; 
1704                 CFIndex substr_len 
= CFStringGetLength(str
) - prefix_len
; 
1705                 CFRange range 
= { prefix_len
, substr_len 
}; 
1706                 fqdn 
= CFStringCreateWithSubstring(kCFAllocatorDefault
, str
, range
); 
1708                     CFArrayAppendValue(fqdns
, fqdn
); 
1714     count 
= CFArrayGetCount(fqdns
); 
1716         SecError(errSecParam
, error
, CFSTR("No domain provided")); 
1720     // Aggregate search results for each domain 
1721     for (idx 
= 0; idx 
< count
; idx
++) { 
1722         CFMutableArrayRef items 
= NULL
; 
1723         CFMutableDictionaryRef attrs 
= NULL
; 
1724         fqdn 
= (CFStringRef
) CFArrayGetValueAtIndex(fqdns
, idx
); 
1728         // Parse the fqdn for a possible port specifier. 
1730             CFStringRef urlStr 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, CFSTR("%@%@"), kSecSharedCredentialUrlScheme
, fqdn
); 
1732                 CFURLRef url 
= CFURLCreateWithString(kCFAllocatorDefault
, urlStr
, nil
); 
1734                     CFStringRef hostname 
= CFURLCopyHostName(url
); 
1736                         CFReleaseSafe(fqdn
); 
1738                         port 
= CFURLGetPortNumber(url
); 
1742                 CFReleaseSafe(urlStr
); 
1746 #if TARGET_IPHONE_SIMULATOR 
1747         secerror("app/site association entitlements not checked in Simulator"); 
1749             OSStatus status 
= errSecMissingEntitlement
; 
1751             SecError(status
, error
, CFSTR("Missing application-identifier entitlement")); 
1752             CFReleaseSafe(fqdn
); 
1755         // validate that fqdn is part of caller's entitlement 
1756         if (_SecEntitlementContainsDomainForService(domains
, fqdn
, kSecSharedWebCredentialsService
)) { 
1757             status 
= errSecSuccess
; 
1759         if (errSecSuccess 
!= status
) { 
1760             CFStringRef msg 
= CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
, 
1761                 CFSTR("%@ not found in %@ entitlement"), fqdn
, kSecEntitlementAssociatedDomains
); 
1763                 msg 
= CFRetain(CFSTR("Requested domain not found in entitlement")); 
1765             SecError(status
, error
, CFSTR("%@"), msg
); 
1767             CFReleaseSafe(fqdn
); 
1772         attrs 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
1774             SecError(errSecAllocate
, error
, CFSTR("Unable to create query dictionary")); 
1775             CFReleaseSafe(fqdn
); 
1778         CFDictionaryAddValue(attrs
, kSecClass
, kSecClassInternetPassword
); 
1779         CFDictionaryAddValue(attrs
, kSecAttrAccessGroup
, kSecSafariAccessGroup
); 
1780         CFDictionaryAddValue(attrs
, kSecAttrAuthenticationType
, kSecAttrAuthenticationTypeHTMLForm
); 
1781         CFDictionaryAddValue(attrs
, kSecAttrServer
, fqdn
); 
1783             CFDictionaryAddValue(attrs
, kSecAttrAccount
, account
); 
1785         if (port 
< -1 || port 
> 0) { 
1786             SInt16 portValueShort 
= (port 
& 0xFFFF); 
1787             CFNumberRef portNumber 
= CFNumberCreate(NULL
, kCFNumberSInt16Type
, &portValueShort
); 
1788             CFDictionaryAddValue(attrs
, kSecAttrPort
, portNumber
); 
1789             CFReleaseSafe(portNumber
); 
1791         CFDictionaryAddValue(attrs
, kSecAttrSynchronizable
, kCFBooleanTrue
); 
1792         CFDictionaryAddValue(attrs
, kSecMatchLimit
, kSecMatchLimitAll
); 
1793         CFDictionaryAddValue(attrs
, kSecReturnAttributes
, kCFBooleanTrue
); 
1794         CFDictionaryAddValue(attrs
, kSecReturnData
, kCFBooleanTrue
); 
1796         ok 
= _SecItemCopyMatching(attrs
, &swcclient
, (CFTypeRef
*)&items
, error
); 
1798             // ignore interim error since we have multiple domains to search 
1799             CFReleaseNull(*error
); 
1801         if (ok 
&& items 
&& CFGetTypeID(items
) == CFArrayGetTypeID()) { 
1802     #if TARGET_IPHONE_SIMULATOR 
1803             secerror("Ignoring app/site approval state in the Simulator."); 
1804             bool approved 
= true; 
1806             // get approval status for this app/domain pair 
1807             SWCFlags flags 
= _SecAppDomainApprovalStatus(appID
, fqdn
, error
); 
1809                 // ignore interim error since we have multiple domains to check 
1810                 CFReleaseNull(*error
); 
1812             bool approved 
= (flags 
& kSWCFlag_SiteApproved
); 
1815                 CFArrayAppendArray(foundItems
, items
, CFRangeMake(0, CFArrayGetCount(items
))); 
1818         CFReleaseSafe(items
); 
1819         CFReleaseSafe(attrs
); 
1820         CFReleaseSafe(fqdn
); 
1823 //  If matching credentials are found, the credentials provided to the completionHandler 
1824 //  will be a CFArrayRef containing CFDictionaryRef entries. Each dictionary entry will 
1825 //  contain the following pairs (see Security/SecItem.h): 
1826 //  key: kSecAttrServer     value: CFStringRef (the website) 
1827 //  key: kSecAttrAccount    value: CFStringRef (the account) 
1828 //  key: kSecSharedPassword value: CFStringRef (the password) 
1830 //  key: kSecAttrPort       value: CFNumberRef (the port number, if non-standard for https) 
1832     count 
= CFArrayGetCount(foundItems
); 
1833     for (idx 
= 0; idx 
< count
; idx
++) { 
1834         CFDictionaryRef dict 
= (CFDictionaryRef
) CFArrayGetValueAtIndex(foundItems
, idx
); 
1835         CFMutableDictionaryRef newdict 
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
1836         if (newdict 
&& dict 
&& CFGetTypeID(dict
) == CFDictionaryGetTypeID()) { 
1837             CFStringRef srvr 
= CFDictionaryGetValue(dict
, kSecAttrServer
); 
1838             CFStringRef acct 
= CFDictionaryGetValue(dict
, kSecAttrAccount
); 
1839             CFNumberRef pnum 
= CFDictionaryGetValue(dict
, kSecAttrPort
); 
1840             CFStringRef icmt 
= CFDictionaryGetValue(dict
, kSecAttrComment
); 
1841             CFDataRef data 
= CFDictionaryGetValue(dict
, kSecValueData
); 
1843                 CFDictionaryAddValue(newdict
, kSecAttrServer
, srvr
); 
1846                 CFDictionaryAddValue(newdict
, kSecAttrAccount
, acct
); 
1850                 if (CFNumberGetValue(pnum
, kCFNumberSInt16Type
, &pval
) && 
1851                     (pval 
< -1 || pval 
> 0)) { 
1852                     CFDictionaryAddValue(newdict
, kSecAttrPort
, pnum
); 
1856                 CFStringRef password 
= CFStringCreateFromExternalRepresentation(kCFAllocatorDefault
, data
, kCFStringEncodingUTF8
); 
1858                 #if TARGET_OS_IPHONE && !TARGET_OS_WATCH 
1859                     CFDictionaryAddValue(newdict
, kSecSharedPassword
, password
); 
1861                     CFDictionaryAddValue(newdict
, CFSTR("spwd"), password
); 
1863                     CFReleaseSafe(password
); 
1866             if (icmt 
&& CFEqual(icmt
, kSecSafariDefaultComment
)) { 
1867                 CFArrayInsertValueAtIndex(credentials
, 0, newdict
); 
1869                 CFArrayAppendValue(credentials
, newdict
); 
1872         CFReleaseSafe(newdict
); 
1879         // create a new array of dictionaries (without the actual password) for picker UI 
1880         count 
= CFArrayGetCount(credentials
); 
1881         CFMutableArrayRef items 
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
); 
1882         for (idx 
= 0; idx 
< count
; idx
++) { 
1883             CFDictionaryRef dict 
= (CFDictionaryRef
) CFArrayGetValueAtIndex(credentials
, idx
); 
1884             CFMutableDictionaryRef newdict 
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, dict
); 
1885         #if TARGET_OS_IPHONE && !TARGET_OS_WATCH 
1886             CFDictionaryRemoveValue(newdict
, kSecSharedPassword
); 
1888             CFDictionaryRemoveValue(newdict
, CFSTR("spwd")); 
1890             CFArrayAppendValue(items
, newdict
); 
1891             CFReleaseSafe(newdict
); 
1894         // prompt user to select one of the dictionary items 
1895         CFDictionaryRef selected 
= swca_copy_selected_dictionary(swca_select_request_id
, 
1896                                                                  clientAuditToken
, items
, error
); 
1898             // find the matching item in our credentials array 
1899             CFStringRef srvr 
= CFDictionaryGetValue(selected
, kSecAttrServer
); 
1900             CFStringRef acct 
= CFDictionaryGetValue(selected
, kSecAttrAccount
); 
1901             CFNumberRef pnum 
= CFDictionaryGetValue(selected
, kSecAttrPort
); 
1902             for (idx 
= 0; idx 
< count
; idx
++) { 
1903                 CFDictionaryRef dict 
= (CFDictionaryRef
) CFArrayGetValueAtIndex(credentials
, idx
); 
1904                 CFStringRef srvr1 
= CFDictionaryGetValue(dict
, kSecAttrServer
); 
1905                 CFStringRef acct1 
= CFDictionaryGetValue(dict
, kSecAttrAccount
); 
1906                 CFNumberRef pnum1 
= CFDictionaryGetValue(dict
, kSecAttrPort
); 
1908                 if (!srvr 
|| !srvr1 
|| !CFEqual(srvr
, srvr1
)) continue; 
1909                 if (!acct 
|| !acct1 
|| !CFEqual(acct
, acct1
)) continue; 
1910                 if ((pnum 
&& pnum1
) && !CFEqual(pnum
, pnum1
)) continue; 
1913                 CFReleaseSafe(selected
); 
1920         CFReleaseSafe(items
); 
1921         CFArrayRemoveAllValues(credentials
); 
1922         if (selected 
&& ok
) { 
1923 #if TARGET_OS_IPHONE && !TARGET_OS_WATCH 
1924             fqdn 
= CFDictionaryGetValue(selected
, kSecAttrServer
); 
1926             CFArrayAppendValue(credentials
, selected
); 
1930             #if TARGET_OS_IPHONE && !TARGET_OS_WATCH 
1931             // register confirmation with database 
1932             OSStatus status 
= _SecSWCEnsuredInitialized(); 
1934                 SecError(status
, error
, CFSTR("SWC initialize failed")); 
1936                 CFReleaseSafe(selected
); 
1939             CFRetainSafe(appID
); 
1941             if (0 != sSWCSetServiceFlags_f(kSecSharedWebCredentialsService
, 
1942                 appID
, fqdn
, kSWCFlag_ExternalMask
, kSWCFlag_UserApproved
, 
1943                 ^void(OSStatus inStatus
, SWCFlags inNewFlags
){ 
1944                     CFReleaseSafe(appID
); 
1945                     CFReleaseSafe(fqdn
); 
1948                  // we didn't queue the block 
1949                 CFReleaseSafe(appID
); 
1950                 CFReleaseSafe(fqdn
); 
1954         CFReleaseSafe(selected
); 
1956     else if (NULL 
== *error
) { 
1957         // found no items, and we haven't already filled in the error 
1958         SecError(errSecItemNotFound
, error
, CFSTR("no matching items found")); 
1963         CFArrayRemoveAllValues(credentials
); 
1965     CFReleaseSafe(foundItems
); 
1966     *result 
= credentials
; 
1967     CFReleaseSafe(swcclient
.accessGroups
); 
1968     CFReleaseSafe(fqdns
); 
1974 // MARK: Keychain backup 
1976 CF_RETURNS_RETAINED CFDataRef
 
1977 _SecServerKeychainCreateBackup(SecurityClient 
*client
, CFDataRef keybag
, CFDataRef passcode
, CFErrorRef 
*error
) { 
1979         SecDbConnectionRef dbt 
= SecDbConnectionAquire(kc_dbhandle(), false, error
); 
1984     if (keybag 
== NULL 
&& passcode 
== NULL
) { 
1986         backup 
= SecServerExportBackupableKeychain(dbt
, client
, KEYBAG_DEVICE
, backup_keybag_handle
, error
); 
1987 #else /* !USE_KEYSTORE */ 
1989         SecError(errSecParam
, error
, CFSTR("Why are you doing this?")); 
1991 #endif /* USE_KEYSTORE */ 
1993         backup 
= SecServerKeychainCreateBackup(dbt
, client
, keybag
, passcode
, error
); 
1996     SecDbConnectionRelease(dbt
); 
2002 _SecServerKeychainRestore(CFDataRef backup
, SecurityClient 
*client
, CFDataRef keybag
, CFDataRef passcode
, CFErrorRef 
*error
) { 
2003     if (backup 
== NULL 
|| keybag 
== NULL
) 
2004         return SecError(errSecParam
, error
, CFSTR("backup or keybag missing")); 
2006     __block 
bool ok 
= true; 
2007     ok 
&= SecDbPerformWrite(kc_dbhandle(), error
, ^(SecDbConnectionRef dbconn
) { 
2008         ok 
= SecServerKeychainRestore(dbconn
, client
, backup
, keybag
, passcode
, error
); 
2012         SecKeychainChanged(true); 
2020 // MARK: SecItemDataSource 
2022 // Make sure to call this before any writes to the keychain, so that we fire 
2023 // up the engines to monitor manifest changes. 
2024 SOSDataSourceFactoryRef 
SecItemDataSourceFactoryGetDefault(void) { 
2025     return SecItemDataSourceFactoryGetShared(kc_dbhandle()); 
2028 /* AUDIT[securityd]: 
2029    args_in (ok) is a caller provided, CFDictionaryRef. 
2032 CF_RETURNS_RETAINED CFArrayRef
 
2033 _SecServerKeychainSyncUpdateMessage(CFDictionaryRef updates
, CFErrorRef 
*error
) { 
2034     // This never fails, trust us! 
2035     return SOSCCHandleUpdateMessage(updates
); 
2039 // Truthiness in the cloud backup/restore support. 
2042 static CFDictionaryRef
 
2043 _SecServerCopyTruthInTheCloud(CFDataRef keybag
, CFDataRef password
, 
2044     CFDictionaryRef backup
, CFErrorRef 
*error
) 
2046     SOSManifestRef mold 
= NULL
, mnow 
= NULL
, mdelete 
= NULL
, madd 
= NULL
; 
2047     __block CFMutableDictionaryRef backup_new 
= NULL
; 
2048     keybag_handle_t bag_handle
; 
2049     if (!ks_open_keybag(keybag
, password
, &bag_handle
, error
)) 
2052     // We need to have a datasource singleton for protection domain 
2053     // kSecAttrAccessibleWhenUnlocked and keep a single shared engine 
2054     // instance around which we create in the datasource constructor as well. 
2055     SOSDataSourceFactoryRef dsf 
= SecItemDataSourceFactoryGetDefault(); 
2056     SOSDataSourceRef ds 
= SOSDataSourceFactoryCreateDataSource(dsf
, kSecAttrAccessibleWhenUnlocked
, error
); 
2058         backup_new 
= backup 
? CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, 0, backup
) : CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
); 
2059         mold 
= SOSCreateManifestWithBackup(backup
, error
); 
2060         SOSEngineRef engine 
= SOSDataSourceGetSharedEngine(ds
, error
); 
2061         mnow 
= SOSEngineCopyManifest(engine
, NULL
); 
2063             mnow 
= SOSDataSourceCopyManifestWithViewNameSet(ds
, SOSViewsGetV0ViewSet(), error
); 
2066             CFReleaseNull(backup_new
); 
2067             secerror("failed to obtain manifest for keychain: %@", error 
? *error 
: NULL
); 
2069             SOSManifestDiff(mold
, mnow
, &mdelete
, &madd
, error
); 
2072         // Delete everything from the new_backup that is no longer in the datasource according to the datasources manifest. 
2073         SOSManifestForEach(mdelete
, ^(CFDataRef digest_data
, bool *stop
) { 
2074             CFStringRef deleted_item_key 
= CFDataCopyHexString(digest_data
); 
2075             CFDictionaryRemoveValue(backup_new
, deleted_item_key
); 
2076             CFRelease(deleted_item_key
); 
2079         CFMutableArrayRef changes 
= CFArrayCreateMutableForCFTypes(kCFAllocatorDefault
); 
2080         SOSDataSourceForEachObject(ds
, madd
, error
, ^void(CFDataRef digest
, SOSObjectRef object
, bool *stop
) { 
2081             CFErrorRef localError 
= NULL
; 
2082             CFDataRef digest_data 
= NULL
; 
2083             CFTypeRef value 
= NULL
; 
2085                 // Key in our manifest can't be found in db, remove it from our manifest 
2086                 SOSChangesAppendDelete(changes
, digest
); 
2087             } else if (!(digest_data 
= SOSObjectCopyDigest(ds
, object
, &localError
)) 
2088                 || !(value 
= SOSObjectCopyBackup(ds
, object
, bag_handle
, &localError
))) { 
2089                 if (SecErrorGetOSStatus(localError
) == errSecDecode
) { 
2090                     // Ignore decode errors, pretend the objects aren't there 
2091                     CFRelease(localError
); 
2092                     // Object undecodable, remove it from our manifest 
2093                     SOSChangesAppendDelete(changes
, digest
); 
2095                     // Stop iterating and propagate out all other errors. 
2097                     *error 
= localError
; 
2098                     CFReleaseNull(backup_new
); 
2101                 // TODO: Should we skip tombstones here? 
2102                 CFStringRef key 
= CFDataCopyHexString(digest_data
); 
2103                 CFDictionarySetValue(backup_new
, key
, value
); 
2106             CFReleaseSafe(digest_data
); 
2107             CFReleaseSafe(value
); 
2108         }) || CFReleaseNull(backup_new
); 
2110         if (CFArrayGetCount(changes
)) { 
2111             if (!SOSEngineUpdateChanges(engine
, kSOSDataSourceSOSTransaction
, changes
, error
)) { 
2112                 CFReleaseNull(backup_new
); 
2115         CFReleaseSafe(changes
); 
2117         SOSDataSourceRelease(ds
, error
) || CFReleaseNull(backup_new
); 
2120     CFReleaseSafe(mold
); 
2121     CFReleaseSafe(mnow
); 
2122     CFReleaseSafe(madd
); 
2123     CFReleaseSafe(mdelete
); 
2124     ks_close_keybag(bag_handle
, error
) || CFReleaseNull(backup_new
); 
2130 _SecServerRestoreTruthInTheCloud(CFDataRef keybag
, CFDataRef password
, CFDictionaryRef backup_in
, CFErrorRef 
*error
) { 
2131     __block 
bool ok 
= true; 
2132     keybag_handle_t bag_handle
; 
2133     if (!ks_open_keybag(keybag
, password
, &bag_handle
, error
)) 
2136     SOSManifestRef mbackup 
= SOSCreateManifestWithBackup(backup_in
, error
); 
2138         SOSDataSourceFactoryRef dsf 
= SecItemDataSourceFactoryGetDefault(); 
2139         SOSDataSourceRef ds 
= SOSDataSourceFactoryCreateDataSource(dsf
, kSecAttrAccessibleWhenUnlocked
, error
); 
2140         ok 
&= ds 
&& SOSDataSourceWith(ds
, error
, ^(SOSTransactionRef txn
, bool *commit
) { 
2141             SOSManifestRef mnow 
= SOSDataSourceCopyManifestWithViewNameSet(ds
, SOSViewsGetV0BackupViewSet(), error
); 
2142             SOSManifestRef mdelete 
= NULL
, madd 
= NULL
; 
2143             SOSManifestDiff(mnow
, mbackup
, &mdelete
, &madd
, error
); 
2145             // Don't delete everything in datasource not in backup. 
2147             // Add items from the backup 
2148             SOSManifestForEach(madd
, ^void(CFDataRef e
, bool *stop
) { 
2149                 CFDictionaryRef item 
= NULL
; 
2150                 CFStringRef sha1 
= CFDataCopyHexString(e
); 
2152                     item 
= CFDictionaryGetValue(backup_in
, sha1
); 
2156                     CFErrorRef localError 
= NULL
; 
2158                     if (!SOSObjectRestoreObject(ds
, txn
, bag_handle
, item
, &localError
)) { 
2159                         OSStatus status 
= SecErrorGetOSStatus(localError
); 
2160                         if (status 
== errSecDuplicateItem
) { 
2161                             // Log and ignore duplicate item errors during restore 
2162                             secnotice("titc", "restore %@ not replacing existing item", item
); 
2163                         } else if (status 
== errSecDecode
) { 
2164                             // Log and ignore corrupted item errors during restore 
2165                             secnotice("titc", "restore %@ skipping corrupted item %@", item
, localError
); 
2167                             if (status 
== errSecInteractionNotAllowed
) 
2169                             // Propagate the first other error upwards (causing the restore to fail). 
2170                             secerror("restore %@ failed %@", item
, localError
); 
2172                             if (error 
&& !*error
) { 
2173                                 *error 
= localError
; 
2177                         CFReleaseSafe(localError
); 
2181             ok 
&= SOSDataSourceRelease(ds
, error
); 
2182             CFReleaseNull(mdelete
); 
2183             CFReleaseNull(madd
); 
2184             CFReleaseNull(mnow
); 
2189     ok 
&= ks_close_keybag(bag_handle
, error
); 
2195 CF_RETURNS_RETAINED CFDictionaryRef
 
2196 _SecServerBackupSyncable(CFDictionaryRef backup
, CFDataRef keybag
, CFDataRef password
, CFErrorRef 
*error
) { 
2197     require_action_quiet(isData(keybag
), errOut
, SecError(errSecParam
, error
, CFSTR("keybag %@ not a data"), keybag
)); 
2198     require_action_quiet(!backup 
|| isDictionary(backup
), errOut
, SecError(errSecParam
, error
, CFSTR("backup %@ not a dictionary"), backup
)); 
2199     require_action_quiet(!password 
|| isData(password
), errOut
, SecError(errSecParam
, error
, CFSTR("password %@ not a data"), password
)); 
2201     return _SecServerCopyTruthInTheCloud(keybag
, password
, backup
, error
); 
2208 _SecServerRestoreSyncable(CFDictionaryRef backup
, CFDataRef keybag
, CFDataRef password
, CFErrorRef 
*error
) { 
2210     require_action_quiet(isData(keybag
), errOut
, ok 
= SecError(errSecParam
, error
, CFSTR("keybag %@ not a data"), keybag
)); 
2211     require_action_quiet(isDictionary(backup
), errOut
, ok 
= SecError(errSecParam
, error
, CFSTR("backup %@ not a dictionary"), backup
)); 
2214         require_action_quiet(isData(password
), errOut
, ok 
= SecError(errSecParam
, error
, CFSTR("password not a data"))); 
2217     ok 
= _SecServerRestoreTruthInTheCloud(keybag
, password
, backup
, error
); 
2223 bool _SecServerRollKeysGlue(bool force
, CFErrorRef 
*error
) { 
2224     return _SecServerRollKeys(force
, NULL
, error
); 
2228 bool _SecServerRollKeys(bool force
, SecurityClient 
*client
, CFErrorRef 
*error
) { 
2230     uint32_t keystore_generation_status 
= 0; 
2231     if (aks_generation(KEYBAG_DEVICE
, generation_noop
, &keystore_generation_status
)) 
2233     uint32_t current_generation 
= keystore_generation_status 
& generation_current
; 
2235     return kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
) { 
2236         bool up_to_date 
= s3dl_dbt_keys_current(dbt
, current_generation
, NULL
); 
2238         if (force 
&& !up_to_date
) { 
2239             up_to_date 
= s3dl_dbt_update_keys(dbt
, client
, error
); 
2241                 secerror("Completed roll keys."); 
2242                 up_to_date 
= s3dl_dbt_keys_current(dbt
, current_generation
, NULL
); 
2245                 secerror("Failed to roll keys."); 
2257  * Sync bubble migration code 
2260 struct SyncBubbleRule 
{ 
2261     CFStringRef attribute
; 
2266 TransmogrifyItemsToSyncBubble(SecurityClient 
*client
, uid_t uid
, 
2269                               const SecDbClass 
*qclass
, 
2270                               struct SyncBubbleRule 
*items
, CFIndex nItems
, 
2273     CFMutableDictionaryRef updateAttributes 
= NULL
; 
2274     CFDataRef syncBubbleView 
= NULL
; 
2275     CFDataRef activeUserView 
= NULL
; 
2280     syncBubbleView 
= SecMUSRCreateSyncBubbleUserUUID(uid
); 
2281     require(syncBubbleView
, fail
); 
2283     activeUserView 
= SecMUSRCreateActiveUserUUID(uid
); 
2284     require(activeUserView
, fail
); 
2287     if ((onlyDelete 
&& !copyToo
) || !onlyDelete
) { 
2290          * Clean out items first 
2293         secnotice("syncbubble", "cleaning out old items"); 
2295         q 
= query_create(qclass
, NULL
, NULL
, error
); 
2298         q
->q_limit 
= kSecMatchUnlimited
; 
2299         q
->q_keybag 
= device_keybag_handle
; 
2301         for (n 
= 0; n 
< nItems
; n
++) { 
2302             query_add_attribute(items
[n
].attribute
, items
[n
].value
, q
); 
2304         q
->q_musrView 
= CFRetain(syncBubbleView
); 
2305         require(q
->q_musrView
, fail
); 
2307         kc_with_dbt(false, error
, ^(SecDbConnectionRef dbt
) { 
2308             return kc_transaction(dbt
, error
, ^{ 
2309                 return s3dl_query_delete(dbt
, q
, NULL
, error
); 
2313         query_destroy(q
, NULL
); 
2318     if (onlyDelete 
|| !copyToo
) { 
2319         secnotice("syncbubble", "skip migration of items"); 
2322          * Copy over items from EMCS to sync bubble 
2325         secnotice("syncbubble", "migrating sync bubble items"); 
2327         q 
= query_create(qclass
, NULL
, NULL
, error
); 
2330         q
->q_return_type 
= kSecReturnDataMask 
| kSecReturnAttributesMask
; 
2331         q
->q_limit 
= kSecMatchUnlimited
; 
2332         q
->q_keybag 
= device_keybag_handle
; /* XXX change to session key bag when it exists */ 
2334         for (n 
= 0; n 
< nItems
; n
++) { 
2335             query_add_or_attribute(items
[n
].attribute
, items
[n
].value
, q
); 
2337         query_add_or_attribute(CFSTR("musr"), activeUserView
, q
); 
2338         q
->q_musrView 
= CFRetain(activeUserView
); 
2340         updateAttributes 
= CFDictionaryCreateMutableForCFTypes(NULL
); 
2341         require(updateAttributes
, fail
); 
2343         CFDictionarySetValue(updateAttributes
, CFSTR("musr"), syncBubbleView
); /* XXX should use kSecAttrMultiUser */ 
2346         kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
) { 
2347             return kc_transaction(dbt
, error
, ^{ 
2348                 CFErrorRef error2 
= NULL
; 
2350                 SecDbItemSelect(q
, dbt
, &error2
, NULL
, ^bool(const SecDbAttr 
*attr
) { 
2351                     return CFDictionaryGetValue(q
->q_item
, attr
->name
); 
2352                 }, NULL
, NULL
, ^(SecDbItemRef item
, bool *stop
) { 
2353                     CFErrorRef error3 
= NULL
; 
2354                     secinfo("syncbubble", "migrating item"); 
2356                     SecDbItemRef new_item 
= SecDbItemCopyWithUpdates(item
, updateAttributes
, NULL
); 
2357                     if (new_item 
== NULL
) 
2360                     SecDbItemClearRowId(new_item
, NULL
); 
2362                     if (!SecDbItemSetKeybag(new_item
, device_keybag_handle
, NULL
)) { 
2363                         CFRelease(new_item
); 
2367                     if (!SecDbItemInsert(new_item
, dbt
, &error3
)) { 
2368                         secnotice("syncbubble", "migration failed with %@ for item %@", error3
, new_item
); 
2370                     CFRelease(new_item
); 
2371                     CFReleaseNull(error3
); 
2373                 CFReleaseNull(error2
); 
2382     CFReleaseNull(syncBubbleView
); 
2383     CFReleaseNull(activeUserView
); 
2384     CFReleaseNull(updateAttributes
); 
2386         query_destroy(q
, NULL
); 
2391 static struct SyncBubbleRule PCSItems
[] = { 
2393         .attribute 
= CFSTR("agrp"), 
2394         .value 
= CFSTR("com.apple.ProtectedCloudStorage"), 
2397 static struct SyncBubbleRule NSURLSesssiond
[] = { 
2399         .attribute 
= CFSTR("agrp"), 
2400         .value 
= CFSTR("com.apple.nsurlsessiond"), 
2403 static struct SyncBubbleRule AccountsdItems
[] = { 
2405         .attribute 
= CFSTR("svce"), 
2406         .value 
= CFSTR("com.apple.account.AppleAccount.token"), 
2409         .attribute 
= CFSTR("svce"), 
2410         .value 
= CFSTR("com.apple.account.AppleAccount.password"), 
2413         .attribute 
= CFSTR("svce"), 
2414         .value 
= CFSTR("com.apple.account.AppleAccount.rpassword"), 
2417         .attribute 
= CFSTR("svce"), 
2418         .value 
= CFSTR("com.apple.account.idms.token"), 
2421         .attribute 
= CFSTR("svce"), 
2422         .value 
= CFSTR("com.apple.account.idms.continuation-key"), 
2425         .attribute 
= CFSTR("svce"), 
2426         .value 
= CFSTR("com.apple.account.CloudKit.token"), 
2430 static struct SyncBubbleRule MobileMailItems
[] = { 
2432         .attribute 
= CFSTR("svce"), 
2433         .value 
= CFSTR("com.apple.account.IMAP.password"), 
2436         .attribute 
= CFSTR("svce"), 
2437         .value 
= CFSTR("com.apple.account.SMTP.password"), 
2440         .attribute 
= CFSTR("svce"), 
2441         .value 
= CFSTR("com.apple.account.Exchange.password"), 
2444         .attribute 
= CFSTR("svce"), 
2445         .value 
= CFSTR("com.apple.account.Hotmail.password"), 
2448         .attribute 
= CFSTR("svce"), 
2449         .value 
= CFSTR("com.apple.account.Google.password"), 
2452         .attribute 
= CFSTR("svce"), 
2453         .value 
= CFSTR("com.apple.account.Google.oauth-token"), 
2456         .attribute 
= CFSTR("svce"), 
2457         .value 
= CFSTR("com.apple.account.Google.oath-refresh-token"), 
2460         .attribute 
= CFSTR("svce"), 
2461         .value 
= CFSTR("com.apple.account.Yahoo.password"), 
2464         .attribute 
= CFSTR("svce"), 
2465         .value 
= CFSTR("com.apple.account.Yahoo.oauth-token"), 
2468         .attribute 
= CFSTR("svce"), 
2469         .value 
= CFSTR("com.apple.account.Yahoo.oauth-token-nosync"), 
2472         .attribute 
= CFSTR("svce"), 
2473         .value 
= CFSTR("com.apple.account.Yahoo.oath-refresh-token"), 
2476         .attribute 
= CFSTR("svce"), 
2477         .value 
= CFSTR("com.apple.account.IMAPNotes.password"), 
2480         .attribute 
= CFSTR("svce"), 
2481         .value 
= CFSTR("com.apple.account.IMAPMail.password"), 
2484         .attribute 
= CFSTR("svce"), 
2485         .value 
= CFSTR("com.apple.account.126.password"), 
2488         .attribute 
= CFSTR("svce"), 
2489         .value 
= CFSTR("com.apple.account.163.password"), 
2492         .attribute 
= CFSTR("svce"), 
2493         .value 
= CFSTR("com.apple.account.aol.password"), 
2498 ArrayContains(CFArrayRef array
, CFStringRef service
) 
2500     return CFArrayContainsValue(array
, CFRangeMake(0, CFArrayGetCount(array
)), service
); 
2504 _SecServerTransmogrifyToSyncBubble(CFArrayRef services
, uid_t uid
, SecurityClient 
*client
, CFErrorRef 
*error
) 
2506     bool copyCloudAuthToken 
= false; 
2507     bool copyMobileMail 
= false; 
2509     bool copyPCS 
= false; 
2510     bool onlyDelete 
= false; 
2511     bool copyNSURLSesssion 
= false; 
2513     if (!client
->inMultiUser
) 
2516     secnotice("syncbubble", "migration for uid %d uid for services %@", (int)uid
, services
); 
2518 #if TARGET_OS_SIMULATOR 
2521     if (uid 
!= (uid_t
)client
->activeUser
) 
2524 #error "no sync bubble on other platforms" 
2528      * First select that services to copy/delete 
2531     if (ArrayContains(services
, CFSTR("com.apple.bird.usermanager.sync")) 
2532         || ArrayContains(services
, CFSTR("com.apple.cloudphotod.sync")) 
2533         || ArrayContains(services
, CFSTR("com.apple.cloudphotod.syncstakeholder")) 
2534         || ArrayContains(services
, CFSTR("com.apple.cloudd.usermanager.sync"))) 
2536         copyCloudAuthToken 
= true; 
2540     if (ArrayContains(services
, CFSTR("com.apple.nsurlsessiond.usermanager.sync"))) 
2542         copyCloudAuthToken 
= true; 
2543         copyNSURLSesssion 
= true; 
2546     if (ArrayContains(services
, CFSTR("com.apple.syncdefaultsd.usermanager.sync"))) { 
2547         copyCloudAuthToken 
= true; 
2549     if (ArrayContains(services
, CFSTR("com.apple.mailq.sync")) || ArrayContains(services
, CFSTR("com.apple.mailq.sync.xpc"))) { 
2550         copyCloudAuthToken 
= true; 
2551         copyMobileMail 
= true; 
2556      * The actually copy/delete the items selected 
2559     res 
= TransmogrifyItemsToSyncBubble(client
, uid
, onlyDelete
, copyPCS
, &inet_class
, PCSItems
, sizeof(PCSItems
)/sizeof(PCSItems
[0]), error
); 
2561     res 
= TransmogrifyItemsToSyncBubble(client
, uid
, onlyDelete
, copyPCS
, &genp_class
, PCSItems
, sizeof(PCSItems
)/sizeof(PCSItems
[0]), error
); 
2565     res 
= TransmogrifyItemsToSyncBubble(client
, uid
, onlyDelete
, copyMobileMail
, &genp_class
, MobileMailItems
, sizeof(MobileMailItems
)/sizeof(MobileMailItems
[0]), error
); 
2569     res 
= TransmogrifyItemsToSyncBubble(client
, uid
, onlyDelete
, copyCloudAuthToken
, &genp_class
, AccountsdItems
, sizeof(AccountsdItems
)/sizeof(AccountsdItems
[0]), error
); 
2573     res 
= TransmogrifyItemsToSyncBubble(client
, uid
, onlyDelete
, copyNSURLSesssion
, &inet_class
, NSURLSesssiond
, sizeof(NSURLSesssiond
)/sizeof(NSURLSesssiond
[0]), error
); 
2581  * Migrate from user keychain to system keychain when switching to edu mode 
2585 _SecServerTransmogrifyToSystemKeychain(SecurityClient 
*client
, CFErrorRef 
*error
) 
2587     __block 
bool ok 
= true; 
2590      * we are not in multi user yet, about to switch, otherwise we would 
2591      * check that for client->inMultiuser here 
2594     kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
) { 
2595         return kc_transaction(dbt
, error
, ^{ 
2596             CFDataRef systemUUID 
= SecMUSRGetSystemKeychainUUID(); 
2598             const SecDbSchema 
*newSchema 
= kc_schemas
[0]; 
2599             SecDbClass 
const *const *kcClass
; 
2601             for (kcClass 
= newSchema
->classes
; *kcClass 
!= NULL
; kcClass
++) { 
2602                 CFErrorRef localError 
= NULL
; 
2605                 if (*kcClass 
== &tversion_class 
|| *kcClass 
== &identity_class
) 
2608                 q 
= query_create(*kcClass
, SecMUSRGetSingleUserKeychainUUID(), NULL
, error
); 
2612                 ok 
&= SecDbItemSelect(q
, dbt
, error
, ^bool(const SecDbAttr 
*attr
) { 
2613                     return (attr
->flags 
& kSecDbInFlag
) != 0; 
2614                 }, ^bool(const SecDbAttr 
*attr
) { 
2615                     // No filtering please. 
2617                 }, ^bool(CFMutableStringRef sql
, bool *needWhere
) { 
2618                     SecDbAppendWhereOrAnd(sql
, needWhere
); 
2619                     CFStringAppendFormat(sql
, NULL
, CFSTR("musr = ?")); 
2621                 }, ^bool(sqlite3_stmt 
*stmt
, int col
) { 
2622                     return SecDbBindObject(stmt
, col
++, SecMUSRGetSingleUserKeychainUUID(), error
); 
2623                 }, ^(SecDbItemRef item
, bool *stop
) { 
2624                     CFErrorRef localError 
= NULL
; 
2626                     if (!SecDbItemSetValueWithName(item
, kSecAttrMultiUser
, systemUUID
, &localError
)) { 
2627                         secerror("item: %@ update musr to system failed: %@", item
, localError
); 
2632                     if (!SecDbItemDoUpdate(item
, item
, dbt
, &localError
, ^bool (const SecDbAttr 
*attr
) { 
2633                         return attr
->kind 
== kSecDbRowIdAttr
; 
2635                         secerror("item: %@ insert during UPDATE: %@", item
, localError
); 
2641                     SecErrorPropagate(localError
, error
); 
2642                     CFReleaseSafe(localError
); 
2646                     query_destroy(q
, &localError
); 
2657  * Delete account from local usage 
2661 _SecServerDeleteMUSERViews(SecurityClient 
*client
, uid_t uid
, CFErrorRef 
*error
) 
2663     return kc_with_dbt(true, error
, ^(SecDbConnectionRef dbt
) { 
2664         CFDataRef musrView 
= NULL
, syncBubbleView 
= NULL
; 
2667         syncBubbleView 
= SecMUSRCreateSyncBubbleUserUUID(uid
); 
2668         require(syncBubbleView
, fail
); 
2670         musrView 
= SecMUSRCreateActiveUserUUID(uid
); 
2671         require(musrView
, fail
); 
2673         require(ok 
= SecServerDeleteAllForUser(dbt
, syncBubbleView
, false, error
), fail
); 
2674         require(ok 
= SecServerDeleteAllForUser(dbt
, musrView
, false, error
), fail
); 
2677         CFReleaseNull(syncBubbleView
); 
2678         CFReleaseNull(musrView
); 
2684 #endif /* TARGET_OS_IOS */ 
2687 _SecServerGetKeyStats(const SecDbClass 
*qclass
, 
2688                       struct _SecServerKeyStats 
*stats
) 
2690     __block CFErrorRef error 
= NULL
; 
2693     Query 
*q 
= query_create(qclass
, NULL
, NULL
, &error
); 
2696     q
->q_return_type 
= kSecReturnDataMask 
| kSecReturnAttributesMask
; 
2697     q
->q_limit 
= kSecMatchUnlimited
; 
2698     q
->q_keybag 
= KEYBAG_DEVICE
; 
2699     query_add_or_attribute(kSecAttrAccessible
, kSecAttrAccessibleWhenUnlocked
, q
); 
2700     query_add_or_attribute(kSecAttrAccessible
, kSecAttrAccessibleAfterFirstUnlock
, q
); 
2701     query_add_or_attribute(kSecAttrAccessible
, kSecAttrAccessibleAlways
, q
); 
2702     query_add_or_attribute(kSecAttrAccessible
, kSecAttrAccessibleWhenUnlockedThisDeviceOnly
, q
); 
2703     query_add_or_attribute(kSecAttrAccessible
, kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
, q
); 
2704     query_add_or_attribute(kSecAttrAccessible
, kSecAttrAccessibleAlwaysThisDeviceOnly
, q
); 
2705     query_add_attribute(kSecAttrTombstone
, kCFBooleanFalse
, q
); 
2707     kc_with_dbt(false, &error
, ^(SecDbConnectionRef dbconn
) { 
2708         CFErrorRef error2 
= NULL
; 
2709         __block CFIndex totalSize 
= 0; 
2710         stats
->maxDataSize 
= 0; 
2712         SecDbItemSelect(q
, dbconn
, &error2
, NULL
, ^bool(const SecDbAttr 
*attr
) { 
2713             return CFDictionaryContainsKey(q
->q_item
, attr
->name
); 
2714         }, NULL
, NULL
, ^(SecDbItemRef item
, bool *stop
) { 
2715             CFErrorRef error3 
= NULL
; 
2716             CFDataRef data 
= SecDbItemGetValue(item
, &v6v_Data
, &error3
); 
2718                 CFIndex size 
= CFDataGetLength(data
); 
2719                 if (size 
> stats
->maxDataSize
) 
2720                     stats
->maxDataSize 
= size
; 
2724             CFReleaseNull(error3
); 
2726         CFReleaseNull(error2
); 
2728             stats
->averageSize 
= totalSize 
/ stats
->items
; 
2737     CFReleaseNull(error
); 
2739         query_destroy(q
, NULL
);