X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_utilities/lib/osxcode.cpp?ds=inline diff --git a/libsecurity_utilities/lib/osxcode.cpp b/libsecurity_utilities/lib/osxcode.cpp new file mode 100644 index 00000000..e5b0a918 --- /dev/null +++ b/libsecurity_utilities/lib/osxcode.cpp @@ -0,0 +1,260 @@ +/* + * 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 + + +namespace Security { + + +// +// 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() +{ + // return a code signing-aware OSXCode subclass if possible + CFRef me; + if (!SecCodeCopySelf(kSecCSDefaultFlags, &me.aref())) + return new OSXCodeWrap(me); + + // otherwise, follow the legacy path precisely - no point in messing with this, is there? + 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()); + secdebug("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; +} + + +// +// 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) +{ + CFRef code; + if (!SecStaticCodeCreateWithPath(CFTempURL(path), kSecCSDefaultFlags, &code.aref())) + return new OSXCodeWrap(code); + + struct stat st; + // otherwise, follow the legacy path precisely - no point in messing with this, is there? + if (stat(path, &st)) + UnixError::throwMe(); + if ((st.st_mode & S_IFMT) == S_IFDIR) { // directory - assume bundle + return new Bundle(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 Bundle(string(path).substr(0, contents-path).c_str(), path); + // assume tool (single executable) + return new ExecutableTool(path); + } +} + + +// +// Executable Tools +// +string ExecutableTool::canonicalPath() const +{ + return path(); +} + +string ExecutableTool::executablePath() const +{ + return path(); +} + + +// +// Generic Bundles +// +Bundle::Bundle(const char *path, const char *execPath /* = NULL */) + : mPath(path), mBundle(NULL) +{ + if (execPath) // caller knows that one; set it + mExecutablePath = execPath; + secdebug("bundle", "%p Bundle from path %s(%s)", this, path, executablePath().c_str()); +} + +Bundle::Bundle(CFBundleRef bundle, const char *root /* = NULL */) + : mBundle(bundle) +{ + assert(bundle); + CFRetain(bundle); + mPath = root ? root : cfStringRelease(CFBundleCopyBundleURL(mBundle)); + secdebug("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) { + 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 Bundle::infoPlistItem(const char *name) const +{ + return CFBundleGetValueForInfoDictionaryKey(cfBundle(), CFTempString(name)); +} + + +void *Bundle::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 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 &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)))); +} + + +// +// 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()); +} + + +// +// OSXCodeWrap +// +string OSXCodeWrap::canonicalPath() const +{ + CFURLRef path; + MacOSError::check(SecCodeCopyPath(mCode, kSecCSDefaultFlags, &path)); + return cfStringRelease(path); +} + + +// +// The executable path is a bit annoying to get, but not quite +// annoying enough to cache the result. +// +string OSXCodeWrap::executablePath() const +{ + CFRef info; + MacOSError::check(SecCodeCopySigningInformation(mCode, kSecCSDefaultFlags, &info.aref())); + return cfString(CFURLRef(CFDictionaryGetValue(info, kSecCodeInfoMainExecutable))); +} + +SecStaticCodeRef OSXCodeWrap::codeRef() const +{ + return mCode.retain(); +} + + +} // end namespace Security