/* * Copyright (c) 2017 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #import <Foundation/Foundation.h> #import <Foundation/NSXPCConnection.h> #import <Foundation/NSXPCConnection_Private.h> #include <xpc/private.h> #include <xpc/xpc.h> #include <ipc/securityd_client.h> #include <ipc/server_security_helpers.h> #include <ipc/server_entitlement_helpers.h> #include <ipc/server_endpoint.h> #include <securityd/SecItemServer.h> #include <Security/SecEntitlements.h> #pragma mark - Securityd Server @implementation SecuritydXPCServer @synthesize connection = _connection; - (instancetype)initWithConnection:(NSXPCConnection *)connection { if ((self = [super init])) { _connection = connection; fill_security_client(&self->_client, connection.effectiveUserIdentifier, connection.auditToken); } return self; } - (instancetype)initWithSecurityClient:(SecurityClient*) existingClient { if(!existingClient) { return nil; } if((self = [super init])) { _connection = nil; self->_client.task = CFRetainSafe(existingClient->task); self->_client.accessGroups = CFRetainSafe(existingClient->accessGroups); self->_client.allowSystemKeychain = existingClient->allowSystemKeychain; self->_client.allowSyncBubbleKeychain = existingClient->allowSyncBubbleKeychain; self->_client.isNetworkExtension = existingClient->isNetworkExtension; self->_client.canAccessNetworkExtensionAccessGroups = existingClient->canAccessNetworkExtensionAccessGroups; self->_client.uid = existingClient->uid; self->_client.musr = CFRetainSafe(existingClient->musr); #if TARGET_OS_EMBEDDED && TARGET_HAS_KEYSTORE self->_client.keybag = existingClient->keybag; #endif #if TARGET_OS_IPHONE self->_client.inMultiUser = existingClient->inMultiUser; self->_client.activeUser = existingClient->activeUser; #endif } return self; } - (bool)clientHasBooleanEntitlement: (NSString*) entitlement { return SecTaskGetBooleanValueForEntitlement(self->_client.task, (__bridge CFStringRef) entitlement); } -(void)dealloc { CFReleaseNull(self->_client.task); CFReleaseNull(self->_client.accessGroups); CFReleaseNull(self->_client.musr); } @end // Class to use for local dispatching of securityd xpcs. Adds capability of fake entitlements, because you don't have a real task on the other end. @interface LocalSecuritydXPCServer : SecuritydXPCServer @property NSMutableDictionary<NSString*, id>* fakeEntitlements; - (instancetype)initWithSecurityClient:(SecurityClient*) existingClient fakeEntitlements:(NSDictionary<NSString*, id>*)fakeEntitlements; @end @implementation LocalSecuritydXPCServer - (instancetype)initWithSecurityClient:(SecurityClient*) existingClient fakeEntitlements:(NSDictionary<NSString*, id>*)fakeEntitlements { if((self = [super initWithSecurityClient: existingClient])) { _fakeEntitlements = [fakeEntitlements mutableCopy]; } return self; } - (bool)clientHasBooleanEntitlement: (NSString*) entitlement { if(self.fakeEntitlements) { return [self.fakeEntitlements[entitlement] isEqual: @YES]; } else { return false; } } @end #pragma mark - SecuritydXPCServerListener // Responsible for bringing up new SecuritydXPCServer objects, and configuring them with their remote connection @interface SecuritydXPCServerListener : NSObject <NSXPCListenerDelegate> @property (retain,nonnull) NSXPCListener *listener; @end @implementation SecuritydXPCServerListener -(instancetype)init { if((self = [super init])){ self.listener = [[NSXPCListener alloc] initWithMachServiceName:@(kSecuritydGeneralServiceName)]; self.listener.delegate = self; [self.listener resume]; } return self; } - (BOOL)listener:(__unused NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { // Anyone is allowed to get a connection to securityd, except if you have kSecEntitlementKeychainDeny entitlement // The SecuritydClient class _must_ check for required entitlements in each XPC handler. if([newConnection valueForEntitlement: (__bridge NSString*) kSecEntitlementKeychainDeny]) { return NO; } newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(SecuritydXPCProtocol)]; // Configure the interface on the server side, too [SecuritydXPCClient configureSecuritydXPCProtocol: newConnection.exportedInterface]; newConnection.exportedObject = [[SecuritydXPCServer alloc] initWithConnection:newConnection]; [newConnection resume]; return YES; } @end void SecCreateSecuritydXPCServer(void) { static SecuritydXPCServerListener* listener = NULL; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @autoreleasepool { listener = [[SecuritydXPCServerListener alloc] init]; } }); } id<SecuritydXPCProtocol> SecCreateLocalSecuritydXPCServer(void) { // Create a fake securitydxpcserver using the access groups of securityd and some number of fake entitlements SecurityClient* client = SecSecurityClientGet(); // We know that SecuritydXPCServerListener will comply with SecuritydXPCProtocol via category, so help the compiler out return (id<SecuritydXPCProtocol>) [[LocalSecuritydXPCServer alloc] initWithSecurityClient: client fakeEntitlements: @{}]; } CFTypeRef SecCreateLocalCFSecuritydXPCServer(void) { return (CFTypeRef) CFBridgingRetain(SecCreateLocalSecuritydXPCServer()); } void SecResetLocalSecuritydXPCFakeEntitlements(void) { if([(__bridge id) gSecurityd->secd_xpc_server isKindOfClass: [LocalSecuritydXPCServer class]]) { LocalSecuritydXPCServer* server = (__bridge LocalSecuritydXPCServer*)gSecurityd->secd_xpc_server; server.fakeEntitlements = [[NSMutableDictionary alloc] init]; } } void SecAddLocalSecuritydXPCFakeEntitlement(CFStringRef entitlement, CFTypeRef value) { if([(__bridge id) gSecurityd->secd_xpc_server isKindOfClass: [LocalSecuritydXPCServer class]]) { LocalSecuritydXPCServer* server = (__bridge LocalSecuritydXPCServer*)gSecurityd->secd_xpc_server; server.fakeEntitlements[(__bridge NSString*)entitlement] = (__bridge id)value; } }