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