1/*
2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19//
20// osxcode - MacOS X's standard code objects
21//
22#include <security_utilities/osxcode.h>
23#include <security_utilities/unix++.h>
24#include <fcntl.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <sys/mman.h>
28#include <errno.h>
29#include <CoreFoundation/CFBundle.h>
30#include <CoreFoundation/CFBundlePriv.h>
31
32
33namespace Security {
34
35
36//
37// Produce an OSXCode for the currently running application.
38//
39// Note that we don't build the CFBundleRef here; we defer this to when we
40// really need it for something more interesting than the base or executable paths.
41// This is important because OSXCode::main() is called from various initialization
42// scenarios (out of the securityd client layer), and CFBundle calls into some
43// bizarrely high-level APIs to complete CFBundleGetMainBundle. Until that is fixed
44// (if it ever is), this particular instance of laziness is mandatory.
45//
46RefPointer<OSXCode> OSXCode::main()
47{
48	// return a code signing-aware OSXCode subclass if possible
49	CFRef<SecCodeRef> me;
50	if (!SecCodeCopySelf(kSecCSDefaultFlags, &me.aref()))
51		return new OSXCodeWrap(me);
52
53	// otherwise, follow the legacy path precisely - no point in messing with this, is there?
54	Boolean isRealBundle;
55	string path = cfStringRelease(_CFBundleCopyMainBundleExecutableURL(&isRealBundle));
56	if (isRealBundle) {
57		const char *cpath = path.c_str();
58		if (const char *slash = strrchr(cpath, '/'))
59			if (const char *contents = strstr(cpath, "/Contents/MacOS/"))
60				if (contents + 15 == slash)
61					return new Bundle(path.substr(0, contents-cpath).c_str());
62		secdebug("bundle", "OSXCode::main(%s) not recognized as bundle (treating as tool)", cpath);
63	}
64	return new ExecutableTool(path.c_str());
65}
66
67
68SecStaticCodeRef OSXCode::codeRef() const
69{
70	SecStaticCodeRef code;
71	MacOSError::check(SecStaticCodeCreateWithPath(CFTempURL(this->canonicalPath()), kSecCSDefaultFlags, &code));
72	return code;
73}
74
75
76//
77// Produce an OSXCode for whatever is at a given path.
78// This tries to guess at the type of OSXCode to be used.
79// If you *know*, just create the suitable subclass directly.
80//
81RefPointer<OSXCode> OSXCode::at(const char *path)
82{
83	CFRef<SecStaticCodeRef> code;
84	if (!SecStaticCodeCreateWithPath(CFTempURL(path), kSecCSDefaultFlags, &code.aref()))
85		return new OSXCodeWrap(code);
86
87	struct stat st;
88	// otherwise, follow the legacy path precisely - no point in messing with this, is there?
89	if (stat(path, &st))
90		UnixError::throwMe();
91	if ((st.st_mode & S_IFMT) == S_IFDIR) {	// directory - assume bundle
92		return new Bundle(path);
93	} else {
94		// look for .../Contents/MacOS/<base>
95		if (const char *slash = strrchr(path, '/'))
96			if (const char *contents = strstr(path, "/Contents/MacOS/"))
97				if (contents + 15 == slash)
98					return new Bundle(string(path).substr(0, contents-path).c_str(), path);
99		// assume tool (single executable)
100		return new ExecutableTool(path);
101	}
102}
103
104
105//
106// Executable Tools
107//
108string ExecutableTool::canonicalPath() const
109{
110	return path();
111}
112
113string ExecutableTool::executablePath() const
114{
115	return path();
116}
117
118
119//
120// Generic Bundles
121//
122Bundle::Bundle(const char *path, const char *execPath /* = NULL */)
123	: mPath(path), mBundle(NULL)
124{
125	if (execPath)						// caller knows that one; set it
126		mExecutablePath = execPath;
127	secdebug("bundle", "%p Bundle from path %s(%s)", this, path, executablePath().c_str());
128}
129
130Bundle::Bundle(CFBundleRef bundle, const char *root /* = NULL */)
131	: mBundle(bundle)
132{
133	assert(bundle);
134	CFRetain(bundle);
135	mPath = root ? root : cfStringRelease(CFBundleCopyBundleURL(mBundle));
136	secdebug("bundle", "%p Bundle from bundle %p(%s)", this, bundle, mPath.c_str());
137}
138
139
140Bundle::~Bundle()
141{
142	if (mBundle)
143		CFRelease(mBundle);
144}
145
146
147string Bundle::executablePath() const
148{
149	if (mExecutablePath.empty())
150		return mExecutablePath = cfStringRelease(CFBundleCopyExecutableURL(cfBundle()));
151	else
152		return mExecutablePath;
153}
154
155
156CFBundleRef Bundle::cfBundle() const
157{
158	if (!mBundle) {
159		secdebug("bundle", "instantiating CFBundle for %s", mPath.c_str());
160		CFRef<CFURLRef> url = CFURLCreateFromFileSystemRepresentation(NULL,
161			(const UInt8 *)mPath.c_str(), mPath.length(), true);
162		if (!url || !(mBundle = CFBundleCreate(NULL, url)))
163			CFError::throwMe();
164	}
165	return mBundle;
166}
167
168
169CFTypeRef Bundle::infoPlistItem(const char *name) const
170{
171	return CFBundleGetValueForInfoDictionaryKey(cfBundle(), CFTempString(name));
172}
173
174
175void *Bundle::lookupSymbol(const char *name)
176{
177    CFRef<CFStringRef> cfName(CFStringCreateWithCString(NULL, name,
178                                                        kCFStringEncodingMacRoman));
179    if (!cfName)
180		UnixError::throwMe(EBADEXEC);	// sort of
181    void *function = CFBundleGetFunctionPointerForName(cfBundle(), cfName);
182    if (function == NULL)
183		UnixError::throwMe(EBADEXEC);	// sort of
184    return function;
185}
186
187string Bundle::canonicalPath() const
188{
189	return path();
190}
191
192
193string Bundle::resource(const char *name, const char *type, const char *subdir)
194{
195	return cfStringRelease(CFBundleCopyResourceURL(cfBundle(),
196		CFTempString(name), CFTempString(type), CFTempString(subdir)));
197}
198
199void Bundle::resources(vector<string> &paths, const char *type, const char *subdir)
200{
201	CFRef<CFArrayRef> cfList = CFBundleCopyResourceURLsOfType(cfBundle(),
202		CFTempString(type), CFTempString(subdir));
203	CFIndex size = CFArrayGetCount(cfList);
204	paths.reserve(size);
205	for (CFIndex n = 0; n < size; n++)
206		paths.push_back(cfString(CFURLRef(CFArrayGetValueAtIndex(cfList, n))));
207}
208
209
210//
211// Load management for a loadable bundle
212//
213void LoadableBundle::load()
214{
215	if (!CFBundleLoadExecutable(cfBundle()))
216		CFError::throwMe();
217    secdebug("bundle", "%p (%s) loaded", this, path().c_str());
218}
219
220void LoadableBundle::unload()
221{
222    secdebug("bundle", "%p (%s) unloaded", this, path().c_str());
223	CFBundleUnloadExecutable(cfBundle());
224}
225
226bool LoadableBundle::isLoaded() const
227{
228	return CFBundleIsExecutableLoaded(cfBundle());
229}
230
231
232//
233// OSXCodeWrap
234//
235string OSXCodeWrap::canonicalPath() const
236{
237	CFURLRef path;
238	MacOSError::check(SecCodeCopyPath(mCode, kSecCSDefaultFlags, &path));
239	return cfStringRelease(path);
240}
241
242
243//
244// The executable path is a bit annoying to get, but not quite
245// annoying enough to cache the result.
246//
247string OSXCodeWrap::executablePath() const
248{
249	CFRef<CFDictionaryRef> info;
250	MacOSError::check(SecCodeCopySigningInformation(mCode, kSecCSDefaultFlags, &info.aref()));
251	return cfString(CFURLRef(CFDictionaryGetValue(info, kSecCodeInfoMainExecutable)));
252}
253
254SecStaticCodeRef OSXCodeWrap::codeRef() const
255{
256	return mCode.retain();
257}
258
259
260} // end namespace Security
261