diskrep.cpp
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 #if TARGET_OS_OSX 36 #include "diskimagerep.h" 37 #endif 38 39 namespace Security { 40 namespace CodeSigning { 41 42 using namespace UnixPlusPlus; 43 44 45 // 46 // Abstract features 47 // 48 DiskRep::DiskRep() 49 { 50 } 51 52 DiskRep::~DiskRep() 53 { 54 CODESIGN_DISKREP_DESTROY(this); 55 } 56 57 58 // 59 // Normal DiskReps are their own base. 60 // 61 DiskRep *DiskRep::base() 62 { 63 return this; 64 } 65 66 67 // 68 // By default, DiskReps are read-only. 69 // 70 DiskRep::Writer *DiskRep::writer() 71 { 72 MacOSError::throwMe(errSecCSUnimplemented); 73 } 74 75 76 void DiskRep::Writer::addDiscretionary(CodeDirectory::Builder &) 77 { 78 // do nothing 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 // 91 DiskRep *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 TARGET_OS_OSX 114 if (DiskImageRep::candidate(fd)) 115 return new DiskImageRep(path); 116 #endif 117 if (DYLDCacheRep::candidate(fd)) 118 return new DYLDCacheRep(path); 119 120 // ultimate fallback - the generic file representation 121 return new FileDiskRep(path); 122 123 } catch (const CommonError &error) { 124 switch (error.unixError()) { 125 case ENOENT: 126 MacOSError::throwMe(errSecCSStaticCodeNotFound); 127 default: 128 throw; 129 } 130 } 131 } 132 133 134 DiskRep *DiskRep::bestFileGuess(const char *path, const Context *ctx) 135 { 136 Context dctx; 137 if (ctx) 138 dctx = *ctx; 139 dctx.fileOnly = true; 140 return bestGuess(path, &dctx); 141 } 142 143 144 // 145 // Given a main executable known to be a Mach-O binary, and an offset into 146 // the file of the actual architecture desired (of a Universal file), 147 // produce a suitable MachORep. 148 // This function does not consider non-MachO binaries. It does however handle 149 // bundles with Mach-O main executables correctly. 150 // 151 DiskRep *DiskRep::bestGuess(const char *path, size_t archOffset) 152 { 153 try { 154 // is it the main executable of a bundle? 155 if (CFRef<CFURLRef> pathURL = makeCFURL(path)) 156 if (CFRef<CFBundleRef> bundle = _CFBundleCreateWithExecutableURLIfMightBeBundle(NULL, pathURL)) { 157 Context ctx; ctx.offset = archOffset; 158 return new BundleDiskRep(bundle, &ctx); // ask bundle to make bundle-with-MachO-at-offset 159 } 160 // else, must be a Mach-O binary 161 Context ctx; ctx.offset = archOffset; 162 return new MachORep(path, &ctx); 163 } catch (const CommonError &error) { 164 switch (error.unixError()) { 165 case ENOENT: 166 MacOSError::throwMe(errSecCSStaticCodeNotFound); 167 default: 168 throw; 169 } 170 } 171 } 172 173 174 // 175 // Default behaviors of DiskRep 176 // 177 string DiskRep::resourcesRootPath() 178 { 179 return ""; // has no resources directory 180 } 181 182 void DiskRep::adjustResources(ResourceBuilder &builder) 183 { 184 // do nothing 185 } 186 187 void DiskRep::prepareForSigning(SigningContext &state) 188 { 189 // do nothing 190 } 191 192 Universal *DiskRep::mainExecutableImage() 193 { 194 return NULL; // no Mach-O executable 195 } 196 197 size_t DiskRep::signingBase() 198 { 199 return 0; // whole file (start at beginning) 200 } 201 202 size_t DiskRep::execSegBase(const Architecture *) 203 { 204 return 0; // whole file (start at beginning) 205 } 206 207 CFArrayRef DiskRep::modifiedFiles() 208 { 209 // by default, claim (just) the main executable modified 210 CFRef<CFURLRef> mainURL = makeCFURL(mainExecutablePath()); 211 return makeCFArray(1, mainURL.get()); 212 } 213 214 void DiskRep::flush() 215 { 216 // nothing cached 217 } 218 219 CFDictionaryRef DiskRep::copyDiskRepInformation() 220 { 221 return NULL; 222 } 223 224 CFDictionaryRef DiskRep::defaultResourceRules(const SigningContext &) 225 { 226 return NULL; // none 227 } 228 229 const Requirements *DiskRep::defaultRequirements(const Architecture *, const SigningContext &) 230 { 231 return NULL; // none 232 } 233 234 size_t DiskRep::pageSize(const SigningContext &) 235 { 236 return monolithicPageSize; // unpaged (monolithic) 237 } 238 239 240 void DiskRep::strictValidate(const CodeDirectory*, const ToleratedErrors& tolerated, SecCSFlags flags) 241 { 242 if (flags & kSecCSRestrictToAppLike) 243 if (tolerated.find(errSecCSNotAppLike) == tolerated.end()) 244 MacOSError::throwMe(errSecCSNotAppLike); 245 } 246 247 CFArrayRef DiskRep::allowedResourceOmissions() 248 { 249 return NULL; 250 } 251 252 253 // 254 // Given some string (usually a pathname), derive a suggested signing identifier 255 // in a canonical way (so there's some consistency). 256 // 257 // This is a heuristic. First we lop off any leading directories and final (non-numeric) 258 // extension. Then we walk backwards, eliminating numeric extensions except the first one. 259 // Thus, libfrotz7.3.5.dylib becomes libfrotz7, mumble.77.plugin becomes mumble.77, 260 // and rumble.rb becomes rumble. This isn't perfect, but it ought to handle 98%+ of 261 // the common varieties out there. Specify an explicit identifier for the oddballs. 262 // 263 // This is called by the various recommendedIdentifier() methods, who are 264 // free to modify or override it. 265 // 266 // Note: We use strchr("...") instead of is*() here because we do not 267 // wish to be influenced by locale settings. 268 // 269 std::string DiskRep::canonicalIdentifier(const std::string &name) 270 { 271 string s = name; 272 string::size_type p; 273 274 // lop off any directory prefixes 275 if ((p = s.rfind('/')) != string::npos) 276 s = s.substr(p+1); 277 278 // remove any final extension (last dot) unless it's numeric 279 if ((p = s.rfind('.')) != string::npos && !strchr("0123456789", s[p+1])) 280 s = s.substr(0, p); 281 282 // eat numeric suffixes except the first one; roughly: 283 // foo.2.3.4 => foo.2, foo2.3 => foo2, foo.9 => foo.9, foo => foo 284 if (strchr("0123456789.", s[0])) // starts with digit or . 285 return s; // ... so don't mess with it 286 p = s.size()-1; 287 // foo3.5^, foo.3.5^, foo3^, foo.3^, foo^ 288 while (strchr("0123456789.", s[p])) 289 p--; 290 // fo^o3.5, fo^o.3.5, fo^o3, fo^o.3, fo^o 291 p++; 292 // foo^3.5, foo^.3.5, foo^3, foo^.3, foo^ 293 if (s[p] == '.') 294 p++; 295 // foo^3.5, foo.^3.5, foo^3, foo.^3, foo^ 296 while (p < s.size() && strchr("0123456789", s[p])) 297 p++; 298 // foo3^.5, foo.3^.5, foo3^, foo.3^, foo^ 299 return s.substr(0, p); 300 } 301 302 void DiskRep::registerStapledTicket() 303 { /* do nothing */ } 304 305 306 // 307 // Writers 308 // 309 DiskRep::Writer::Writer(uint32_t attrs) 310 : mArch(CPU_TYPE_ANY), mAttributes(attrs) 311 { 312 } 313 314 DiskRep::Writer::~Writer() 315 { /* virtual */ } 316 317 uint32_t DiskRep::Writer::attributes() const 318 { return mAttributes; } 319 320 void DiskRep::Writer::flush() 321 { /* do nothing */ } 322 323 void DiskRep::Writer::remove() 324 { 325 MacOSError::throwMe(errSecCSNotSupported); 326 } 327 328 329 } // end namespace CodeSigning 330 } // end namespace Security