1/* 2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved. 3 * 4 * The contents of this file constitute Original Code as defined in and are 5 * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 * You may not use this file except in compliance with the License. Please obtain 7 * a copy of the License at http://www.apple.com/publicsource and read it before 8 * using this file. 9 * 10 * This Original Code and all software distributed under the License are 11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS 12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 15 * specific language governing rights and limitations under the License. 16 */ 17 18 19// 20// osxcode - MacOS X's standard code objects 21// 22#include <security_utilities/osxcode.h> 23#include <security_utilities/unix++.h> 24#include <fcntl.h> 25#include <sys/types.h> 26#include <sys/stat.h> 27#include <sys/mman.h> 28#include <errno.h> 29#include <CoreFoundation/CFBundle.h> 30#include <CoreFoundation/CFBundlePriv.h> 31 32 33namespace Security { 34 35 36// 37// Produce an OSXCode for the currently running application. 38// 39// Note that we don't build the CFBundleRef here; we defer this to when we 40// really need it for something more interesting than the base or executable paths. 41// This is important because OSXCode::main() is called from various initialization 42// scenarios (out of the securityd client layer), and CFBundle calls into some 43// bizarrely high-level APIs to complete CFBundleGetMainBundle. Until that is fixed 44// (if it ever is), this particular instance of laziness is mandatory. 45// 46RefPointer<OSXCode> OSXCode::main() 47{ 48 // return a code signing-aware OSXCode subclass if possible 49 CFRef<SecCodeRef> me; 50 if (!SecCodeCopySelf(kSecCSDefaultFlags, &me.aref())) 51 return new OSXCodeWrap(me); 52 53 // otherwise, follow the legacy path precisely - no point in messing with this, is there? 54 Boolean isRealBundle; 55 string path = cfStringRelease(_CFBundleCopyMainBundleExecutableURL(&isRealBundle)); 56 if (isRealBundle) { 57 const char *cpath = path.c_str(); 58 if (const char *slash = strrchr(cpath, '/')) 59 if (const char *contents = strstr(cpath, "/Contents/MacOS/")) 60 if (contents + 15 == slash) 61 return new Bundle(path.substr(0, contents-cpath).c_str()); 62 secdebug("bundle", "OSXCode::main(%s) not recognized as bundle (treating as tool)", cpath); 63 } 64 return new ExecutableTool(path.c_str()); 65} 66 67 68SecStaticCodeRef OSXCode::codeRef() const 69{ 70 SecStaticCodeRef code; 71 MacOSError::check(SecStaticCodeCreateWithPath(CFTempURL(this->canonicalPath()), kSecCSDefaultFlags, &code)); 72 return code; 73} 74 75 76// 77// Produce an OSXCode for whatever is at a given path. 78// This tries to guess at the type of OSXCode to be used. 79// If you *know*, just create the suitable subclass directly. 80// 81RefPointer<OSXCode> OSXCode::at(const char *path) 82{ 83 CFRef<SecStaticCodeRef> code; 84 if (!SecStaticCodeCreateWithPath(CFTempURL(path), kSecCSDefaultFlags, &code.aref())) 85 return new OSXCodeWrap(code); 86 87 struct stat st; 88 // otherwise, follow the legacy path precisely - no point in messing with this, is there? 89 if (stat(path, &st)) 90 UnixError::throwMe(); 91 if ((st.st_mode & S_IFMT) == S_IFDIR) { // directory - assume bundle 92 return new Bundle(path); 93 } else { 94 // look for .../Contents/MacOS/<base> 95 if (const char *slash = strrchr(path, '/')) 96 if (const char *contents = strstr(path, "/Contents/MacOS/")) 97 if (contents + 15 == slash) 98 return new Bundle(string(path).substr(0, contents-path).c_str(), path); 99 // assume tool (single executable) 100 return new ExecutableTool(path); 101 } 102} 103 104 105// 106// Executable Tools 107// 108string ExecutableTool::canonicalPath() const 109{ 110 return path(); 111} 112 113string ExecutableTool::executablePath() const 114{ 115 return path(); 116} 117 118 119// 120// Generic Bundles 121// 122Bundle::Bundle(const char *path, const char *execPath /* = NULL */) 123 : mPath(path), mBundle(NULL) 124{ 125 if (execPath) // caller knows that one; set it 126 mExecutablePath = execPath; 127 secdebug("bundle", "%p Bundle from path %s(%s)", this, path, executablePath().c_str()); 128} 129 130Bundle::Bundle(CFBundleRef bundle, const char *root /* = NULL */) 131 : mBundle(bundle) 132{ 133 assert(bundle); 134 CFRetain(bundle); 135 mPath = root ? root : cfStringRelease(CFBundleCopyBundleURL(mBundle)); 136 secdebug("bundle", "%p Bundle from bundle %p(%s)", this, bundle, mPath.c_str()); 137} 138 139 140Bundle::~Bundle() 141{ 142 if (mBundle) 143 CFRelease(mBundle); 144} 145 146 147string Bundle::executablePath() const 148{ 149 if (mExecutablePath.empty()) 150 return mExecutablePath = cfStringRelease(CFBundleCopyExecutableURL(cfBundle())); 151 else 152 return mExecutablePath; 153} 154 155 156CFBundleRef Bundle::cfBundle() const 157{ 158 if (!mBundle) { 159 secdebug("bundle", "instantiating CFBundle for %s", mPath.c_str()); 160 CFRef<CFURLRef> url = CFURLCreateFromFileSystemRepresentation(NULL, 161 (const UInt8 *)mPath.c_str(), mPath.length(), true); 162 if (!url || !(mBundle = CFBundleCreate(NULL, url))) 163 CFError::throwMe(); 164 } 165 return mBundle; 166} 167 168 169CFTypeRef Bundle::infoPlistItem(const char *name) const 170{ 171 return CFBundleGetValueForInfoDictionaryKey(cfBundle(), CFTempString(name)); 172} 173 174 175void *Bundle::lookupSymbol(const char *name) 176{ 177 CFRef<CFStringRef> cfName(CFStringCreateWithCString(NULL, name, 178 kCFStringEncodingMacRoman)); 179 if (!cfName) 180 UnixError::throwMe(EBADEXEC); // sort of 181 void *function = CFBundleGetFunctionPointerForName(cfBundle(), cfName); 182 if (function == NULL) 183 UnixError::throwMe(EBADEXEC); // sort of 184 return function; 185} 186 187string Bundle::canonicalPath() const 188{ 189 return path(); 190} 191 192 193string Bundle::resource(const char *name, const char *type, const char *subdir) 194{ 195 return cfStringRelease(CFBundleCopyResourceURL(cfBundle(), 196 CFTempString(name), CFTempString(type), CFTempString(subdir))); 197} 198 199void Bundle::resources(vector<string> &paths, const char *type, const char *subdir) 200{ 201 CFRef<CFArrayRef> cfList = CFBundleCopyResourceURLsOfType(cfBundle(), 202 CFTempString(type), CFTempString(subdir)); 203 CFIndex size = CFArrayGetCount(cfList); 204 paths.reserve(size); 205 for (CFIndex n = 0; n < size; n++) 206 paths.push_back(cfString(CFURLRef(CFArrayGetValueAtIndex(cfList, n)))); 207} 208 209 210// 211// Load management for a loadable bundle 212// 213void LoadableBundle::load() 214{ 215 if (!CFBundleLoadExecutable(cfBundle())) 216 CFError::throwMe(); 217 secdebug("bundle", "%p (%s) loaded", this, path().c_str()); 218} 219 220void LoadableBundle::unload() 221{ 222 secdebug("bundle", "%p (%s) unloaded", this, path().c_str()); 223 CFBundleUnloadExecutable(cfBundle()); 224} 225 226bool LoadableBundle::isLoaded() const 227{ 228 return CFBundleIsExecutableLoaded(cfBundle()); 229} 230 231 232// 233// OSXCodeWrap 234// 235string OSXCodeWrap::canonicalPath() const 236{ 237 CFURLRef path; 238 MacOSError::check(SecCodeCopyPath(mCode, kSecCSDefaultFlags, &path)); 239 return cfStringRelease(path); 240} 241 242 243// 244// The executable path is a bit annoying to get, but not quite 245// annoying enough to cache the result. 246// 247string OSXCodeWrap::executablePath() const 248{ 249 CFRef<CFDictionaryRef> info; 250 MacOSError::check(SecCodeCopySigningInformation(mCode, kSecCSDefaultFlags, &info.aref())); 251 return cfString(CFURLRef(CFDictionaryGetValue(info, kSecCodeInfoMainExecutable))); 252} 253 254SecStaticCodeRef OSXCodeWrap::codeRef() const 255{ 256 return mCode.retain(); 257} 258 259 260} // end namespace Security 261