/ OSX / sec / ipc / server_security_helpers.m
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  }