]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_utilities/lib/osxcode.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_utilities / lib / osxcode.cpp
diff --git a/Security/libsecurity_utilities/lib/osxcode.cpp b/Security/libsecurity_utilities/lib/osxcode.cpp
new file mode 100644 (file)
index 0000000..51c0100
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2000-2001,2011-2012,2014 Apple 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 <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 {
+
+
+//
+// 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> OSXCode::main()
+{
+       // return a code signing-aware OSXCode subclass if possible
+       CFRef<SecCodeRef> 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> OSXCode::at(const char *path)
+{
+       CFRef<SecStaticCodeRef> 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/<base>
+               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<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);   // 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<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))));
+}
+
+
+//
+// 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<CFDictionaryRef> info;
+       MacOSError::check(SecCodeCopySigningInformation(mCode, kSecCSDefaultFlags, &info.aref()));
+       return cfString(CFURLRef(CFDictionaryGetValue(info, kSecCodeInfoMainExecutable)));
+}
+
+SecStaticCodeRef OSXCodeWrap::codeRef() const
+{
+       return mCode.retain();
+}
+
+
+} // end namespace Security