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