#ifdef __MWERKS__
#define _CPP_OSXSIGNING
#endif
#include <Security/osxsigning.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <CoreFoundation/CFBundle.h>
namespace Security
{
namespace CodeSigning
{
void OSXCode::scanFile(const char *pathname, Signer::State &state)
{
int fd = open(pathname, O_RDONLY);
if (fd < 0)
UnixError::throwMe();
struct stat st;
if (fstat(fd, &st)) {
close(fd);
UnixError::throwMe();
}
#if defined(LIMITED_SIGNING)
if (st.st_size >= 0x4000)
st.st_size = 0x4000;
#endif
void *p = mmap(NULL, st.st_size, PROT_READ, MAP_FILE, fd, 0);
close(fd); if (p == MAP_FAILED)
UnixError::throwMe();
debug("codesign", "scanning file %s (%ld bytes)", pathname, long(st.st_size));
state.enumerateContents(p, st.st_size);
munmap(p, st.st_size);
}
OSXCode *OSXCode::decode(const char *extForm)
{
if (!extForm || !extForm[0] || extForm[1] != ':')
return NULL;
switch (extForm[0]) {
case 't':
return new ExecutableTool(extForm+2);
case 'b':
return new GenericBundle(extForm+2);
default:
return NULL;
}
}
OSXCode *OSXCode::main()
{
if (CFBundleRef mainBundle = CFBundleGetMainBundle()) {
CFRef<CFURLRef> base = CFBundleCopyBundleURL(mainBundle);
CFRef<CFURLRef> resources(CFBundleCopyResourcesDirectoryURL(mainBundle));
if (base && resources && !CFEqual(resources, base)) {
return new ApplicationBundle(getPath(CFBundleCopyBundleURL(mainBundle)).c_str());
}
return new ExecutableTool(getPath(CFBundleCopyExecutableURL(mainBundle)).c_str());
}
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
}
extern "C" CFStringRef CFURLCreateStringWithFileSystemPath(CFAllocatorRef allocator,
CFURLRef anURL, CFURLPathStyle fsType, Boolean resolveAgainstBase);
string OSXCode::getPath(CFURLRef url)
{
if (url == NULL)
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
CFRef<CFStringRef> str(CFURLCreateStringWithFileSystemPath(NULL,
url, kCFURLPOSIXPathStyle, true));
CFRelease(url);
if (str) {
char path[PATH_MAX];
if (CFStringGetCString(str, path, PATH_MAX, kCFStringEncodingUTF8))
return path;
}
CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
}
OSXCode *OSXCode::at(const char *path)
{
struct stat st;
if (stat(path, &st))
UnixError::throwMe();
if ((st.st_mode & S_IFMT) == S_IFDIR) { return new GenericBundle(path);
} else { return new ExecutableTool(path);
}
}
void ExecutableTool::scanContents(Signer::State &state) const
{
scanFile(mPath.c_str(), state);
}
string ExecutableTool::encode() const
{
return "t:" + mPath;
}
string ExecutableTool::canonicalPath() const
{
return path();
}
GenericBundle::GenericBundle(const char *path) : mPath(path)
{
CFRef<CFURLRef> url(CFURLCreateFromFileSystemRepresentation(NULL,
(const UInt8 *)path, strlen(path), true));
if (!url || !(mBundle = CFBundleCreate(NULL, url)))
CssmError::throwMe(CSSMERR_CSSM_ADDIN_LOAD_FAILED);
}
GenericBundle::~GenericBundle()
{
CFRelease(mBundle);
}
void GenericBundle::scanContents(Signer::State &state) const
{
scanFile(executablePath().c_str(), state);
}
string GenericBundle::encode() const
{
return "b:" + mPath;
}
void *GenericBundle::lookupSymbol(const char *name)
{
CFRef<CFStringRef> cfName(CFStringCreateWithCString(NULL, name,
kCFStringEncodingMacRoman));
if (!cfName)
CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
void *function = CFBundleGetFunctionPointerForName(mBundle, cfName);
if (function == NULL)
CssmError::throwMe(CSSM_ERRCODE_UNKNOWN_FORMAT);
return function;
}
string GenericBundle::canonicalPath() const
{
return path();
}
void LoadableBundle::load()
{
if (!CFBundleLoadExecutable(mBundle))
CssmError::throwMe(CSSMERR_CSSM_ADDIN_LOAD_FAILED);
IFDEBUG(debug("bundle", "%p (%s) loaded", this, path().c_str()));
}
void LoadableBundle::unload()
{
IFDEBUG(debug("bundle", "%p (%s) unloaded", this, path().c_str()));
CFBundleUnloadExecutable(mBundle);
}
bool LoadableBundle::isLoaded() const
{
return CFBundleIsExecutableLoaded(mBundle);
}
};
}