server_security_helpers.m
1 /* 2 * Copyright (c) 2017 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 #include <pthread/pthread.h> 25 26 #include "server_security_helpers.h" 27 #include "server_entitlement_helpers.h" 28 29 #include <Security/SecTask.h> 30 #include <Security/SecTaskPriv.h> 31 #include "ipc/securityd_client.h" 32 #include <Security/SecEntitlements.h> 33 #include "sectask/SystemEntitlements.h" 34 #include <utilities/SecInternalReleasePriv.h> 35 #include <sys/codesign.h> 36 #include <Security/SecItem.h> 37 #include "utilities/SecCFRelease.h" 38 #include "utilities/SecCFWrappers.h" 39 #include "utilities/debugging.h" 40 #include "keychain/securityd/SecDbQuery.h" 41 42 #if __has_include(<MobileKeyBag/MobileKeyBag.h>) && TARGET_HAS_KEYSTORE 43 #include <MobileKeyBag/MobileKeyBag.h> 44 #define HAVE_MOBILE_KEYBAG_SUPPORT 1 45 #endif 46 47 #if __has_include(<UserManagement/UserManagement.h>) 48 #include <UserManagement/UserManagement.h> 49 #endif 50 51 #if TARGET_OS_IOS && HAVE_MOBILE_KEYBAG_SUPPORT 52 static bool 53 device_is_multiuser(void) 54 { 55 static dispatch_once_t once; 56 static bool result; 57 58 dispatch_once(&once, ^{ 59 CFDictionaryRef deviceMode = MKBUserTypeDeviceMode(NULL, NULL); 60 CFTypeRef value = NULL; 61 62 if (deviceMode && CFDictionaryGetValueIfPresent(deviceMode, kMKBDeviceModeKey, &value) && CFEqual(value, kMKBDeviceModeMultiUser)) { 63 result = true; 64 } 65 CFReleaseNull(deviceMode); 66 }); 67 68 return result; 69 } 70 #endif /* HAVE_MOBILE_KEYBAG_SUPPORT && TARGET_OS_IOS */ 71 72 static bool sanityCheckClientAccessGroups(SecurityClient* client) { 73 if (!client->accessGroups) { 74 return true; 75 } 76 77 CFRange range = { 0, CFArrayGetCount(client->accessGroups) }; 78 if (!CFArrayContainsValue(client->accessGroups, range, CFSTR("*"))) { 79 return true; 80 } 81 82 CFMutableArrayRef allowedIdentifiers = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault); 83 #if TARGET_OS_OSX 84 CFArrayAppendValue(allowedIdentifiers, CFSTR("com.apple.keychainaccess")); 85 CFArrayAppendValue(allowedIdentifiers, CFSTR("com.apple.KeychainMigrator")); 86 #endif 87 if (SecIsInternalRelease()) { 88 #if TARGET_OS_OSX 89 CFArrayAppendValue(allowedIdentifiers, CFSTR("com.apple.security2")); 90 #else 91 CFArrayAppendValue(allowedIdentifiers, CFSTR("com.apple.security")); 92 #endif 93 } 94 95 bool answer = SecTaskIsEligiblePlatformBinary(client->task, allowedIdentifiers); 96 CFReleaseNull(allowedIdentifiers); 97 98 return answer; 99 } 100 101 bool 102 fill_security_client(SecurityClient * client, const uid_t uid, audit_token_t auditToken) { 103 if(!client) { 104 return false; 105 } 106 107 @autoreleasepool { 108 109 client->uid = uid; 110 client->musr = NULL; 111 112 #if TARGET_OS_IOS && HAVE_MOBILE_KEYBAG_SUPPORT 113 if (device_is_multiuser()) { 114 CFErrorRef error = NULL; 115 116 client->inMultiUser = true; 117 client->activeUser = MKBForegroundUserSessionID(&error); 118 if (client->activeUser == -1 || client->activeUser == 0) { 119 assert(0); 120 client->activeUser = 0; 121 } 122 123 /* 124 * If we are a edu mode user, and its not the active user, 125 * then the request is coming from inside the syncbubble. 126 * 127 * otherwise we are going to execute the request as the 128 * active user. 129 */ 130 131 if (client->uid > 501 && (uid_t)client->activeUser != client->uid) { 132 secinfo("serverxpc", "securityd client: sync bubble user"); 133 client->musr = SecMUSRCreateSyncBubbleUserUUID(client->uid); 134 client->keybag = KEYBAG_DEVICE; 135 } else { 136 secinfo("serverxpc", "securityd client: active user"); 137 client->musr = SecMUSRCreateActiveUserUUID(client->activeUser); 138 client->uid = (uid_t)client->activeUser; 139 client->keybag = KEYBAG_DEVICE; 140 } 141 } else 142 #endif /* TARGET_OS_IOS && HAVE_MOBILE_KEYBAG_SUPPORT */ 143 #if TARGET_OS_IOS || TARGET_OS_TV 144 /* 145 * iOS supports Enterprise Data Separation. 146 * tvOS supports guest users. 147 * Use the appropriate musr values for either. 148 */ 149 { 150 UMUserPersona * persona = [[UMUserManager sharedManager] currentPersona]; 151 if (persona && 152 #if TARGET_OS_IOS 153 persona.userPersonaType == UMUserPersonaTypeEnterprise 154 #elif TARGET_OS_TV 155 persona.userPersonaType == UMUserPersonaTypeGuest 156 #endif 157 ) { 158 secinfo("serverxpc", "securityd client: persona user %@", persona.userPersonaNickName); 159 uuid_t uuid; 160 161 if (uuid_parse([persona.userPersonaUniqueString UTF8String], uuid) != 0) { 162 return false; 163 } 164 client->musr = CFDataCreate(NULL, uuid, sizeof(uuid_t)); 165 } 166 } 167 #endif /* TARGET_OS_IOS || TARGET_OS_TV */ 168 169 client->task = SecTaskCreateWithAuditToken(kCFAllocatorDefault, auditToken); 170 client->accessGroups = SecTaskCopyAccessGroups(client->task); 171 client->applicationIdentifier = SecTaskCopyApplicationIdentifier(client->task); 172 client->isAppClip = SecTaskGetBooleanValueForEntitlement(client->task, kSystemEntitlementOnDemandInstallCapable); 173 if (client->isAppClip) { 174 secinfo("serverxpc", "securityd client: app clip (API restricted)"); 175 } 176 177 #if TARGET_OS_IPHONE 178 client->allowSystemKeychain = SecTaskGetBooleanValueForEntitlement(client->task, kSecEntitlementPrivateSystemKeychain); 179 client->isNetworkExtension = SecTaskGetBooleanValueForEntitlement(client->task, kSecEntitlementPrivateNetworkExtension); 180 client->canAccessNetworkExtensionAccessGroups = SecTaskGetBooleanValueForEntitlement(client->task, kSecEntitlementNetworkExtensionAccessGroups); 181 #endif 182 #if HAVE_MOBILE_KEYBAG_SUPPORT && TARGET_OS_IOS 183 if (client->inMultiUser) { 184 client->allowSyncBubbleKeychain = SecTaskGetBooleanValueForEntitlement(client->task, kSecEntitlementPrivateKeychainSyncBubble); 185 } 186 #endif 187 if (!sanityCheckClientAccessGroups(client)) { 188 CFReleaseNull(client->task); 189 CFReleaseNull(client->accessGroups); 190 CFReleaseNull(client->musr); 191 CFReleaseNull(client->applicationIdentifier); 192 return false; 193 } 194 } 195 return true; 196 } 197 198 // Stolen and adapted from securityd_service 199 bool SecTaskIsEligiblePlatformBinary(SecTaskRef task, CFArrayRef identifiers) { 200 #if (DEBUG || RC_BUILDIT_YES) 201 secnotice("serverxpc", "Accepting client because debug"); 202 return true; 203 #else 204 205 if (task == NULL) { 206 secerror("serverxpc: Client task is null, cannot verify platformness"); 207 return false; 208 } 209 210 uint32_t flags = SecTaskGetCodeSignStatus(task); 211 /* check if valid and platform binary, but not platform path */ 212 213 if ((flags & (CS_VALID | CS_PLATFORM_BINARY | CS_PLATFORM_PATH)) != (CS_VALID | CS_PLATFORM_BINARY)) { 214 if (SecIsInternalRelease()) { 215 if ((flags & (CS_DEBUGGED | CS_PLATFORM_BINARY | CS_PLATFORM_PATH)) != (CS_DEBUGGED | CS_PLATFORM_BINARY)) { 216 secerror("serverxpc: client is not a platform binary: 0x%08x", flags); 217 return false; 218 } 219 } else { 220 secerror("serverxpc: client is not a platform binary: 0x%08x", flags); 221 return false; 222 } 223 } 224 225 CFStringRef signingIdentifier = SecTaskCopySigningIdentifier(task, NULL); 226 if (identifiers) { 227 if (signingIdentifier == NULL) { 228 secerror("serverxpc: client has no codesign identifier"); 229 return false; 230 } 231 232 __block bool result = false; 233 CFArrayForEach(identifiers, ^(const void *value) { 234 if (CFEqual(value, signingIdentifier)) { 235 result = true; 236 } 237 }); 238 239 if (result == true) { 240 secinfo("serverxpc", "client %@ is eligible platform binary", signingIdentifier); 241 } else { 242 secerror("serverxpc: client %@ is not eligible", signingIdentifier); 243 } 244 245 CFReleaseNull(signingIdentifier); 246 return result; 247 } 248 249 secinfo("serverxpc", "Client %@ is valid platform binary", signingIdentifier); 250 CFReleaseNull(signingIdentifier); 251 return true; 252 253 #endif 254 }