/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ // // osxcode - MacOS X's standard code objects // #include #include #include #include #include #include #include #include #include using namespace CodeSigning; namespace Security { // // Enumerate a single file on disk. // void OSXCode::scanFile(const char *pathname, Signer::State &state) { // open the file (well, try) int fd = open(pathname, O_RDONLY); if (fd < 0) UnixError::throwMe(); // how big is it? 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 // map it void *p = mmap(NULL, st.st_size, PROT_READ, MAP_FILE, fd, 0); close(fd); // done with this either way if (p == MAP_FAILED) UnixError::throwMe(); // scan it secdebug("codesign", "scanning file %s (%ld bytes)", pathname, long(st.st_size)); state.enumerateContents(p, st.st_size); // unmap it (ignore error) munmap(p, st.st_size); } // // Use prefix encoding for externalizing OSXCode objects // 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; } } // // Produce an OSXCode for the currently running application. // // Note that we don't build the CFBundleRef here; we defer this to when we // really need it for something more interesting than the base or executable paths. // This is important because OSXCode::main() is called from various initialization // scenarios (out of the securityd client layer), and CFBundle calls into some // bizarrely high-level APIs to complete CFBundleGetMainBundle. Until that is fixed // (if it ever is), this particular instance of laziness is mandatory. // RefPointer OSXCode::main() { Boolean isRealBundle; string path = cfString(_CFBundleCopyMainBundleExecutableURL(&isRealBundle), true); 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 ApplicationBundle(path.substr(0, contents-cpath).c_str()); secdebug("bundle", "OSXCode::main(%s) not recognized as bundle (treating as tool)", cpath); } return new ExecutableTool(path.c_str()); } // // Produce an OSXCode for whatever is at a given path. // This tries to guess at the type of OSXCode to be used. // If you *know*, just create the suitable subclass directly. // RefPointer OSXCode::at(const char *path) { struct stat st; if (stat(path, &st)) UnixError::throwMe(); if ((st.st_mode & S_IFMT) == S_IFDIR) { // directory - assume bundle return new GenericBundle(path); } else { // look for .../Contents/MacOS/ if (const char *slash = strrchr(path, '/')) if (const char *contents = strstr(path, "/Contents/MacOS/")) if (contents + 15 == slash) return new GenericBundle(string(path).substr(0, contents-path).c_str(), path); // assume tool (single executable) return new ExecutableTool(path); } } // // Executable Tools // 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(); } string ExecutableTool::executablePath() const { return path(); } // // Generic Bundles // GenericBundle::GenericBundle(const char *path, const char *execPath /* = NULL */) : mPath(path), mBundle(NULL) { if (execPath) // caller knows that one; set it mExecutablePath = execPath; secdebug("bundle", "%p GenericBundle from path %s(%s)", this, path, executablePath().c_str()); } GenericBundle::GenericBundle(CFBundleRef bundle, const char *root /* = NULL */) : mBundle(bundle) { assert(bundle); CFRetain(bundle); mPath = root ? root : cfString(CFBundleCopyBundleURL(mBundle), true); secdebug("bundle", "%p GenericBundle from bundle %p(%s)", this, bundle, mPath.c_str()); } GenericBundle::~GenericBundle() { if (mBundle) CFRelease(mBundle); } string GenericBundle::executablePath() const { if (mExecutablePath.empty()) return mExecutablePath = cfString(CFBundleCopyExecutableURL(cfBundle()), true); else return mExecutablePath; } CFBundleRef GenericBundle::cfBundle() const { if (!mBundle) { secdebug("bundle", "instantiating CFBundle for %s", mPath.c_str()); CFRef url = CFURLCreateFromFileSystemRepresentation(NULL, (const UInt8 *)mPath.c_str(), mPath.length(), true); if (!url || !(mBundle = CFBundleCreate(NULL, url))) CFError::throwMe(); } return mBundle; } CFTypeRef GenericBundle::infoPlistItem(const char *name) const { return CFBundleGetValueForInfoDictionaryKey(cfBundle(), CFTempString(name)); } 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 cfName(CFStringCreateWithCString(NULL, name, kCFStringEncodingMacRoman)); if (!cfName) UnixError::throwMe(EBADEXEC); // sort of void *function = CFBundleGetFunctionPointerForName(cfBundle(), cfName); if (function == NULL) UnixError::throwMe(EBADEXEC); // sort of return function; } string GenericBundle::canonicalPath() const { return path(); } string GenericBundle::resource(const char *name, const char *type, const char *subdir) { return cfString(CFBundleCopyResourceURL(cfBundle(), CFTempString(name), CFTempString(type), CFTempString(subdir)), true); } void GenericBundle::resources(vector &paths, const char *type, const char *subdir) { CFRef cfList = CFBundleCopyResourceURLsOfType(cfBundle(), CFTempString(type), CFTempString(subdir)); UInt32 size = CFArrayGetCount(cfList); paths.reserve(size); for (UInt32 n = 0; n < size; n++) paths.push_back(cfString(CFURLRef(CFArrayGetValueAtIndex(cfList, n)), false)); } // // Load management for a loadable bundle // void LoadableBundle::load() { if (!CFBundleLoadExecutable(cfBundle())) CFError::throwMe(); secdebug("bundle", "%p (%s) loaded", this, path().c_str()); } void LoadableBundle::unload() { secdebug("bundle", "%p (%s) unloaded", this, path().c_str()); CFBundleUnloadExecutable(cfBundle()); } bool LoadableBundle::isLoaded() const { return CFBundleIsExecutableLoaded(cfBundle()); } } // end namespace Security