1/* 2 * Copyright (c) 2012 Apple Computer, 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/* 25 * A simple XPCService that returns the Info.plist for a specific pid, 26 * the use-case is is for service that is not running as the user or 27 * in a sandbox and can't access the file directly. 28 */ 29 30#include <CoreFoundation/CoreFoundation.h> 31#include <CoreFoundation/CFBundlePriv.h> 32#include <sys/param.h> 33#include <xpc/xpc.h> 34#include <syslog.h> 35#include <assert.h> 36#include <libproc.h> 37#include <sandbox.h> 38#include <syslog.h> 39 40static CFDataRef 41CopyDataFromURL(CFURLRef url) 42{ 43 CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, url); 44 if (stream == NULL) 45 return NULL; 46 47 if (!CFReadStreamOpen(stream)) { 48 CFRelease(stream); 49 return NULL; 50 } 51 52 CFMutableDataRef data = CFDataCreateMutable(NULL, 0); 53 if (data == NULL) { 54 CFRelease(stream); 55 return NULL; 56 } 57 58 UInt8 buf[4096]; 59 60 while (1) { 61 /* limit to 100k */ 62 if (CFDataGetLength(data) > 100 * 1024) { 63 syslog(LOG_ERR, "refusing to handle back Info.plist that is more then 100K"); 64 CFRelease(stream); 65 CFRelease(data); 66 return NULL; 67 } 68 CFIndex readBytes = CFReadStreamRead(stream, buf, sizeof(buf)); 69 if (readBytes == 0) { 70 break; 71 } else if (readBytes <= 0) { 72 CFRelease(data); 73 CFRelease(stream); 74 return NULL; 75 } 76 77 assert(readBytes <= sizeof(buf)); 78 CFDataAppendBytes(data, (void *)buf, readBytes); 79 } 80 81 CFReadStreamClose(stream); 82 CFRelease(stream); 83 84 return data; 85} 86 87static void 88fetchData(xpc_connection_t peer, xpc_object_t event) 89{ 90 CFBundleRef bundle = NULL; 91 char path[MAXPATHLEN]; 92 pid_t pid; 93 94 pid = (pid_t)xpc_dictionary_get_int64(event, "pid"); 95 if (pid <= 0) 96 return; 97 98 xpc_object_t reply = xpc_dictionary_create_reply(event); 99 if (reply == NULL) 100 return; 101 102 if (proc_pidpath(pid, path, sizeof(path)) == 0) { 103 xpc_dictionary_set_string(reply, "error", "no process for that pid"); 104 goto send; 105 } 106 path[sizeof(path) - 1] = '\0'; 107 108 CFURLRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const uint8_t *)path, strlen(path), 0); 109 if (url == NULL) { 110 xpc_dictionary_set_string(reply, "error", "failed to create URL"); 111 goto send; 112 } 113 114 bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, url); 115 CFRelease(url); 116 if (bundle == NULL) { 117 xpc_dictionary_set_string(reply, "error", "Failed to create a bundle"); 118 goto send; 119 } 120 121 CFURLRef infoPlistURL = _CFBundleCopyInfoPlistURL(bundle); 122 if (infoPlistURL == NULL) { 123 xpc_dictionary_set_string(reply, "error", "Info.plist missing"); 124 goto send; 125 } 126 127 CFDataRef data = CopyDataFromURL(infoPlistURL); 128 CFRelease(infoPlistURL); 129 if (data == NULL) { 130 xpc_dictionary_set_string(reply, "error", "can't get content of Info.plist"); 131 goto send; 132 } 133 134 xpc_dictionary_set_data(reply, "infoPlist", CFDataGetBytePtr(data), CFDataGetLength(data)); 135 CFRelease(data); 136 137 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle); 138 if (bundleURL == NULL) 139 goto send; 140 141 data = CFURLCreateData(NULL, bundleURL, kCFStringEncodingUTF8, true); 142 CFRelease(bundleURL); 143 if (data == NULL) 144 goto send; 145 146 xpc_dictionary_set_data(reply, "bundleURL", CFDataGetBytePtr(data), CFDataGetLength(data)); 147 CFRelease(data); 148 149send: 150 if (bundle) 151 CFRelease(bundle); 152 xpc_connection_send_message(peer, reply); 153 xpc_release(reply); 154} 155 156 157static void CodeSigningHelper_peer_event_handler(xpc_connection_t peer, xpc_object_t event) 158{ 159 xpc_type_t type = xpc_get_type(event); 160 if (type == XPC_TYPE_ERROR) 161 return; 162 163 assert(type == XPC_TYPE_DICTIONARY); 164 165 const char *cmd = xpc_dictionary_get_string(event, "command"); 166 if (cmd == NULL) { 167 xpc_connection_cancel(peer); 168 } else if (strcmp(cmd, "fetchData") == 0) 169 fetchData(peer, event); 170 else { 171 syslog(LOG_ERR, "peer sent invalid command %s", cmd); 172 xpc_connection_cancel(peer); 173 } 174} 175 176static void CodeSigningHelper_event_handler(xpc_connection_t peer) 177{ 178 xpc_connection_set_event_handler(peer, ^(xpc_object_t event) { 179 CodeSigningHelper_peer_event_handler(peer, event); 180 }); 181 xpc_connection_resume(peer); 182} 183 184int main(int argc, const char *argv[]) 185{ 186 char *error = NULL; 187 if (sandbox_init("com.apple.CodeSigningHelper", SANDBOX_NAMED, &error)) { 188 syslog(LOG_ERR, "failed to enter sandbox: %s", error); 189 exit(EXIT_FAILURE); 190 } 191 xpc_main(CodeSigningHelper_event_handler); 192 return 0; 193} 194 195