#include <security_utilities/osxcode.h>
#include <security_utilities/unix++.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <CoreFoundation/CFBundle.h>
#include <CoreFoundation/CFBundlePriv.h>
namespace Security {
RefPointer<OSXCode> OSXCode::main()
{
CFRef<SecCodeRef> me;
if (!SecCodeCopySelf(kSecCSDefaultFlags, &me.aref()))
return new OSXCodeWrap(me);
Boolean isRealBundle;
string path = cfStringRelease(_CFBundleCopyMainBundleExecutableURL(&isRealBundle));
if (isRealBundle) {
const char *cpath = path.c_str();
if (const char *slash = strrchr(cpath, '/'))
if (const char *contents = strstr(cpath, "/Contents/MacOS/"))
if (contents + 15 == slash)
return new Bundle(path.substr(0, contents-cpath).c_str());
secinfo("bundle", "OSXCode::main(%s) not recognized as bundle (treating as tool)", cpath);
}
return new ExecutableTool(path.c_str());
}
SecStaticCodeRef OSXCode::codeRef() const
{
SecStaticCodeRef code;
MacOSError::check(SecStaticCodeCreateWithPath(CFTempURL(this->canonicalPath()), kSecCSDefaultFlags, &code));
return code;
}
RefPointer<OSXCode> OSXCode::at(const char *path)
{
CFRef<SecStaticCodeRef> code;
if (!SecStaticCodeCreateWithPath(CFTempURL(path), kSecCSDefaultFlags, &code.aref()))
return new OSXCodeWrap(code);
struct stat st;
if (stat(path, &st))
UnixError::throwMe();
if ((st.st_mode & S_IFMT) == S_IFDIR) { return new Bundle(path);
} else {
if (const char *slash = strrchr(path, '/'))
if (const char *contents = strstr(path, "/Contents/MacOS/"))
if (contents + 15 == slash)
return new Bundle(string(path).substr(0, contents-path).c_str(), path);
return new ExecutableTool(path);
}
}
string ExecutableTool::canonicalPath() const
{
return path();
}
string ExecutableTool::executablePath() const
{
return path();
}
Bundle::Bundle(const char *path, const char *execPath )
: mPath(path), mBundle(NULL)
{
if (execPath) mExecutablePath = execPath;
secinfo("bundle", "%p Bundle from path %s(%s)", this, path, executablePath().c_str());
}
Bundle::Bundle(CFBundleRef bundle, const char *root )
: mBundle(bundle)
{
assert(bundle);
CFRetain(bundle);
mPath = root ? root : cfStringRelease(CFBundleCopyBundleURL(mBundle));
secinfo("bundle", "%p Bundle from bundle %p(%s)", this, bundle, mPath.c_str());
}
Bundle::~Bundle()
{
if (mBundle)
CFRelease(mBundle);
}
string Bundle::executablePath() const
{
if (mExecutablePath.empty())
return mExecutablePath = cfStringRelease(CFBundleCopyExecutableURL(cfBundle()));
else
return mExecutablePath;
}
CFBundleRef Bundle::cfBundle() const
{
if (!mBundle) {
secinfo("bundle", "instantiating CFBundle for %s", mPath.c_str());
CFRef<CFURLRef> url = CFURLCreateFromFileSystemRepresentation(NULL,
(const UInt8 *)mPath.c_str(), mPath.length(), true);
if (!url || !(mBundle = CFBundleCreate(NULL, url)))
CFError::throwMe();
}
return mBundle;
}
CFTypeRef Bundle::infoPlistItem(const char *name) const
{
return CFBundleGetValueForInfoDictionaryKey(cfBundle(), CFTempString(name));
}
void *Bundle::lookupSymbol(const char *name)
{
CFRef<CFStringRef> cfName(CFStringCreateWithCString(NULL, name,
kCFStringEncodingMacRoman));
if (!cfName)
UnixError::throwMe(EBADEXEC); void *function = CFBundleGetFunctionPointerForName(cfBundle(), cfName);
if (function == NULL)
UnixError::throwMe(EBADEXEC); return function;
}
string Bundle::canonicalPath() const
{
return path();
}
string Bundle::resource(const char *name, const char *type, const char *subdir)
{
return cfStringRelease(CFBundleCopyResourceURL(cfBundle(),
CFTempString(name), CFTempString(type), CFTempString(subdir)));
}
void Bundle::resources(vector<string> &paths, const char *type, const char *subdir)
{
CFRef<CFArrayRef> cfList = CFBundleCopyResourceURLsOfType(cfBundle(),
CFTempString(type), CFTempString(subdir));
CFIndex size = CFArrayGetCount(cfList);
paths.reserve(size);
for (CFIndex n = 0; n < size; n++)
paths.push_back(cfString(CFURLRef(CFArrayGetValueAtIndex(cfList, n))));
}
void LoadableBundle::load()
{
if (!CFBundleLoadExecutable(cfBundle()))
CFError::throwMe();
secinfo("bundle", "%p (%s) loaded", this, path().c_str());
}
void LoadableBundle::unload()
{
secinfo("bundle", "%p (%s) unloaded", this, path().c_str());
CFBundleUnloadExecutable(cfBundle());
}
bool LoadableBundle::isLoaded() const
{
return CFBundleIsExecutableLoaded(cfBundle());
}
string OSXCodeWrap::canonicalPath() const
{
CFURLRef path;
MacOSError::check(SecCodeCopyPath(mCode, kSecCSDefaultFlags, &path));
return cfStringRelease(path);
}
string OSXCodeWrap::executablePath() const
{
CFRef<CFDictionaryRef> info;
MacOSError::check(SecCodeCopySigningInformation(mCode, kSecCSDefaultFlags, &info.aref()));
return cfString(CFURLRef(CFDictionaryGetValue(info, kSecCodeInfoMainExecutable)));
}
SecStaticCodeRef OSXCodeWrap::codeRef() const
{
return mCode.retain();
}
}