2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // osxcode - MacOS X's standard code objects
22 #include <security_utilities/osxcode.h>
23 #include <security_utilities/unix++.h>
25 #include <sys/types.h>
29 #include <CoreFoundation/CFBundle.h>
30 #include <CoreFoundation/CFBundlePriv.h>
37 // Produce an OSXCode for the currently running application.
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.
46 RefPointer
<OSXCode
> OSXCode::main()
48 // return a code signing-aware OSXCode subclass if possible
50 if (!SecCodeCopySelf(kSecCSDefaultFlags
, &me
.aref()))
51 return new OSXCodeWrap(me
);
53 // otherwise, follow the legacy path precisely - no point in messing with this, is there?
55 string path
= cfStringRelease(_CFBundleCopyMainBundleExecutableURL(&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
);
64 return new ExecutableTool(path
.c_str());
68 SecStaticCodeRef
OSXCode::codeRef() const
70 SecStaticCodeRef code
;
71 MacOSError::check(SecStaticCodeCreateWithPath(CFTempURL(this->canonicalPath()), kSecCSDefaultFlags
, &code
));
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.
81 RefPointer
<OSXCode
> OSXCode::at(const char *path
)
83 CFRef
<SecStaticCodeRef
> code
;
84 if (!SecStaticCodeCreateWithPath(CFTempURL(path
), kSecCSDefaultFlags
, &code
.aref()))
85 return new OSXCodeWrap(code
);
88 // otherwise, follow the legacy path precisely - no point in messing with this, is there?
91 if ((st
.st_mode
& S_IFMT
) == S_IFDIR
) { // directory - assume bundle
92 return new Bundle(path
);
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
);
108 string
ExecutableTool::canonicalPath() const
113 string
ExecutableTool::executablePath() const
122 Bundle::Bundle(const char *path
, const char *execPath
/* = NULL */)
123 : mPath(path
), mBundle(NULL
)
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());
130 Bundle::Bundle(CFBundleRef bundle
, const char *root
/* = NULL */)
135 mPath
= root
? root
: cfStringRelease(CFBundleCopyBundleURL(mBundle
));
136 secdebug("bundle", "%p Bundle from bundle %p(%s)", this, bundle
, mPath
.c_str());
147 string
Bundle::executablePath() const
149 if (mExecutablePath
.empty())
150 return mExecutablePath
= cfStringRelease(CFBundleCopyExecutableURL(cfBundle()));
152 return mExecutablePath
;
156 CFBundleRef
Bundle::cfBundle() const
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
)))
169 CFTypeRef
Bundle::infoPlistItem(const char *name
) const
171 return CFBundleGetValueForInfoDictionaryKey(cfBundle(), CFTempString(name
));
175 void *Bundle::lookupSymbol(const char *name
)
177 CFRef
<CFStringRef
> cfName(CFStringCreateWithCString(NULL
, name
,
178 kCFStringEncodingMacRoman
));
180 UnixError::throwMe(EBADEXEC
); // sort of
181 void *function
= CFBundleGetFunctionPointerForName(cfBundle(), cfName
);
182 if (function
== NULL
)
183 UnixError::throwMe(EBADEXEC
); // sort of
187 string
Bundle::canonicalPath() const
193 string
Bundle::resource(const char *name
, const char *type
, const char *subdir
)
195 return cfStringRelease(CFBundleCopyResourceURL(cfBundle(),
196 CFTempString(name
), CFTempString(type
), CFTempString(subdir
)));
199 void Bundle::resources(vector
<string
> &paths
, const char *type
, const char *subdir
)
201 CFRef
<CFArrayRef
> cfList
= CFBundleCopyResourceURLsOfType(cfBundle(),
202 CFTempString(type
), CFTempString(subdir
));
203 CFIndex size
= CFArrayGetCount(cfList
);
205 for (CFIndex n
= 0; n
< size
; n
++)
206 paths
.push_back(cfString(CFURLRef(CFArrayGetValueAtIndex(cfList
, n
))));
211 // Load management for a loadable bundle
213 void LoadableBundle::load()
215 if (!CFBundleLoadExecutable(cfBundle()))
217 secdebug("bundle", "%p (%s) loaded", this, path().c_str());
220 void LoadableBundle::unload()
222 secdebug("bundle", "%p (%s) unloaded", this, path().c_str());
223 CFBundleUnloadExecutable(cfBundle());
226 bool LoadableBundle::isLoaded() const
228 return CFBundleIsExecutableLoaded(cfBundle());
235 string
OSXCodeWrap::canonicalPath() const
238 MacOSError::check(SecCodeCopyPath(mCode
, kSecCSDefaultFlags
, &path
));
239 return cfStringRelease(path
);
244 // The executable path is a bit annoying to get, but not quite
245 // annoying enough to cache the result.
247 string
OSXCodeWrap::executablePath() const
249 CFRef
<CFDictionaryRef
> info
;
250 MacOSError::check(SecCodeCopySigningInformation(mCode
, kSecCSDefaultFlags
, &info
.aref()));
251 return cfString(CFURLRef(CFDictionaryGetValue(info
, kSecCodeInfoMainExecutable
)));
254 SecStaticCodeRef
OSXCodeWrap::codeRef() const
256 return mCode
.retain();
260 } // end namespace Security