1/* 2 * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24// 25// diskrep - disk representations of code 26// 27#include "diskrep.h" 28#include <sys/stat.h> 29#include <CoreFoundation/CFBundlePriv.h> 30 31// specific disk representations created by the bestGuess() function 32#include "filediskrep.h" 33#include "bundlediskrep.h" 34#include "cfmdiskrep.h" 35#include "slcrep.h" 36 37 38namespace Security { 39namespace CodeSigning { 40 41using namespace UnixPlusPlus; 42 43 44// 45// Abstract features 46// 47DiskRep::DiskRep() 48{ 49} 50 51DiskRep::~DiskRep() 52{ 53 CODESIGN_DISKREP_DESTROY(this); 54} 55 56 57// 58// Normal DiskReps are their own base. 59// 60DiskRep *DiskRep::base() 61{ 62 return this; 63} 64 65 66// 67// By default, DiskReps are read-only. 68// 69DiskRep::Writer *DiskRep::writer() 70{ 71 MacOSError::throwMe(errSecCSUnimplemented); 72} 73 74 75void DiskRep::Writer::addDiscretionary(CodeDirectory::Builder &) 76{ 77 // do nothing 78} 79 80 81// 82// Given a file system path, come up with the most likely correct 83// disk representation for what's there. 84// This is, strictly speaking, a heuristic that could be fooled - there's 85// no fool-proof rule for figuring this out. But we'd expect this to work 86// fine in ordinary use. If you happen to know what you're looking at 87// (say, a bundle), then just create the suitable subclass of DiskRep directly. 88// That's quite legal. 89// The optional context argument can provide additional information that guides the guess. 90// 91DiskRep *DiskRep::bestGuess(const char *path, const Context *ctx) 92{ 93 try { 94 if (!(ctx && ctx->fileOnly)) { 95 struct stat st; 96 if (::stat(path, &st)) 97 UnixError::throwMe(); 98 99 // if it's a directory, assume it's a bundle 100 if ((st.st_mode & S_IFMT) == S_IFDIR) // directory - assume bundle 101 return new BundleDiskRep(path, ctx); 102 103 // see if it's the main executable of a recognized bundle 104 if (CFRef<CFURLRef> pathURL = makeCFURL(path)) 105 if (CFRef<CFBundleRef> bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, pathURL)) 106 return new BundleDiskRep(bundle, ctx); 107 } 108 109 // try the various single-file representations 110 AutoFileDesc fd(path, O_RDONLY); 111 if (MachORep::candidate(fd)) 112 return new MachORep(path, ctx); 113 if (CFMDiskRep::candidate(fd)) 114 return new CFMDiskRep(path); 115 if (DYLDCacheRep::candidate(fd)) 116 return new DYLDCacheRep(path); 117 118 // ultimate fallback - the generic file representation 119 return new FileDiskRep(path); 120 121 } catch (const CommonError &error) { 122 switch (error.unixError()) { 123 case ENOENT: 124 MacOSError::throwMe(errSecCSStaticCodeNotFound); 125 default: 126 throw; 127 } 128 } 129} 130 131 132DiskRep *DiskRep::bestFileGuess(const char *path, const Context *ctx) 133{ 134 Context dctx; 135 if (ctx) 136 dctx = *ctx; 137 dctx.fileOnly = true; 138 return bestGuess(path, &dctx); 139} 140 141 142// 143// Given a main executable known to be a Mach-O binary, and an offset into 144// the file of the actual architecture desired (of a Universal file), 145// produce a suitable MachORep. 146// This function does not consider non-MachO binaries. It does however handle 147// bundles with Mach-O main executables correctly. 148// 149DiskRep *DiskRep::bestGuess(const char *path, size_t archOffset) 150{ 151 try { 152 // is it the main executable of a bundle? 153 if (CFRef<CFURLRef> pathURL = makeCFURL(path)) 154 if (CFRef<CFBundleRef> bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, pathURL)) { 155 Context ctx; ctx.offset = archOffset; 156 return new BundleDiskRep(bundle, &ctx); // ask bundle to make bundle-with-MachO-at-offset 157 } 158 // else, must be a Mach-O binary 159 Context ctx; ctx.offset = archOffset; 160 return new MachORep(path, &ctx); 161 } catch (const CommonError &error) { 162 switch (error.unixError()) { 163 case ENOENT: 164 MacOSError::throwMe(errSecCSStaticCodeNotFound); 165 default: 166 throw; 167 } 168 } 169} 170 171 172// 173// Default behaviors of DiskRep 174// 175string DiskRep::resourcesRootPath() 176{ 177 return ""; // has no resources directory 178} 179 180void DiskRep::adjustResources(ResourceBuilder &builder) 181{ 182 // do nothing 183} 184 185Universal *DiskRep::mainExecutableImage() 186{ 187 return NULL; // no Mach-O executable 188} 189 190size_t DiskRep::signingBase() 191{ 192 return 0; // whole file (start at beginning) 193} 194 195CFArrayRef DiskRep::modifiedFiles() 196{ 197 // by default, claim (just) the main executable modified 198 CFRef<CFURLRef> mainURL = makeCFURL(mainExecutablePath()); 199 return makeCFArray(1, mainURL.get()); 200} 201 202void DiskRep::flush() 203{ 204 // nothing cached 205} 206 207 208CFDictionaryRef DiskRep::defaultResourceRules(const SigningContext &) 209{ 210 return NULL; // none 211} 212 213const Requirements *DiskRep::defaultRequirements(const Architecture *, const SigningContext &) 214{ 215 return NULL; // none 216} 217 218size_t DiskRep::pageSize(const SigningContext &) 219{ 220 return monolithicPageSize; // unpaged (monolithic) 221} 222 223 224void DiskRep::strictValidate(const ToleratedErrors&) 225{ 226 // do nothing 227} 228 229CFArrayRef DiskRep::allowedResourceOmissions() 230{ 231 return NULL; 232} 233 234 235// 236// Given some string (usually a pathname), derive a suggested signing identifier 237// in a canonical way (so there's some consistency). 238// 239// This is a heuristic. First we lop off any leading directories and final (non-numeric) 240// extension. Then we walk backwards, eliminating numeric extensions except the first one. 241// Thus, libfrotz7.3.5.dylib becomes libfrotz7, mumble.77.plugin becomes mumble.77, 242// and rumble.rb becomes rumble. This isn't perfect, but it ought to handle 98%+ of 243// the common varieties out there. Specify an explicit identifier for the oddballs. 244// 245// This is called by the various recommendedIdentifier() methods, who are 246// free to modify or override it. 247// 248// Note: We use strchr("...") instead of is*() here because we do not 249// wish to be influenced by locale settings. 250// 251std::string DiskRep::canonicalIdentifier(const std::string &name) 252{ 253 string s = name; 254 string::size_type p; 255 256 // lop off any directory prefixes 257 if ((p = s.rfind('/')) != string::npos) 258 s = s.substr(p+1); 259 260 // remove any final extension (last dot) unless it's numeric 261 if ((p = s.rfind('.')) != string::npos && !strchr("0123456789", s[p+1])) 262 s = s.substr(0, p); 263 264 // eat numeric suffixes except the first one; roughly: 265 // foo.2.3.4 => foo.2, foo2.3 => foo2, foo.9 => foo.9, foo => foo 266 if (strchr("0123456789.", s[0])) // starts with digit or . 267 return s; // ... so don't mess with it 268 p = s.size()-1; 269 // foo3.5^, foo.3.5^, foo3^, foo.3^, foo^ 270 while (strchr("0123456789.", s[p])) 271 p--; 272 // fo^o3.5, fo^o.3.5, fo^o3, fo^o.3, fo^o 273 p++; 274 // foo^3.5, foo^.3.5, foo^3, foo^.3, foo^ 275 if (s[p] == '.') 276 p++; 277 // foo^3.5, foo.^3.5, foo^3, foo.^3, foo^ 278 while (p < s.size() && strchr("0123456789", s[p])) 279 p++; 280 // foo3^.5, foo.3^.5, foo3^, foo.3^, foo^ 281 return s.substr(0, p); 282} 283 284 285// 286// Writers 287// 288DiskRep::Writer::Writer(uint32_t attrs) 289 : mArch(CPU_TYPE_ANY), mAttributes(attrs) 290{ 291} 292 293DiskRep::Writer::~Writer() 294{ /* virtual */ } 295 296uint32_t DiskRep::Writer::attributes() const 297{ return mAttributes; } 298 299void DiskRep::Writer::flush() 300{ /* do nothing */ } 301 302void DiskRep::Writer::remove() 303{ 304 MacOSError::throwMe(errSecCSNotSupported); 305} 306 307 308} // end namespace CodeSigning 309} // end namespace Security 310