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#include "filediskrep.h" 24#include "StaticCode.h" 25#include <security_utilities/macho++.h> 26#include <cstring> 27 28 29namespace Security { 30namespace CodeSigning { 31 32using namespace UnixPlusPlus; 33 34 35// 36// Everything's lazy in here 37// 38FileDiskRep::FileDiskRep(const char *path) 39 : SingleDiskRep(path) 40{ 41 CODESIGN_DISKREP_CREATE_FILE(this, (char*)path); 42} 43 44 45// 46// Produce an extended attribute name from a canonical slot name 47// 48string FileDiskRep::attrName(const char *name) 49{ 50 static const char prefix[] = "com.apple.cs."; 51 return string(prefix) + name; 52} 53 54 55// 56// Retrieve an extended attribute by name 57// 58CFDataRef FileDiskRep::getAttribute(const char *name) 59{ 60 string aname = attrName(name); 61 try { 62 ssize_t length = fd().getAttrLength(aname); 63 if (length < 0) 64 return NULL; // no such attribute 65 CFMallocData buffer(length); 66 fd().getAttr(aname, buffer, length); 67 return buffer; 68 } catch (const UnixError &err) { 69 // recover some errors that happen in (relatively) benign circumstances 70 switch (err.error) { 71 case ENOTSUP: // no extended attributes on this filesystem 72 case EPERM: // filesystem objects to name(?) 73 return NULL; 74 default: 75 throw; 76 } 77 } 78} 79 80 81// 82// Extract and return a component by slot number. 83// If we have a Mach-O binary, use embedded components. 84// Otherwise, look for and return the extended attribute, if any. 85// 86CFDataRef FileDiskRep::component(CodeDirectory::SpecialSlot slot) 87{ 88 if (const char *name = CodeDirectory::canonicalSlotName(slot)) 89 return getAttribute(name); 90 else 91 return NULL; 92} 93 94 95// 96// Generate a suggested set of internal requirements. 97// We don't really have to say much. However, if we encounter a file that 98// starts with the magic "#!" script marker, we do suggest that this should 99// be a valid host if we can reasonably make out what that is. 100// 101const Requirements *FileDiskRep::defaultRequirements(const Architecture *, const SigningContext &ctx) 102{ 103 // read start of file 104 char buffer[256]; 105 size_t length = fd().read(buffer, sizeof(buffer), 0); 106 if (length > 3 && buffer[0] == '#' && buffer[1] == '!' && buffer[2] == '/') { 107 // isolate (full) path element in #!/full/path -some -other -stuff 108 if (length == sizeof(buffer)) 109 length--; 110 buffer[length] = '\0'; 111 char *cmd = buffer + 2; 112 cmd[strcspn(cmd, " \t\n\r\f")] = '\0'; 113 secdebug("filediskrep", "looks like a script for %s", cmd); 114 if (cmd[1]) 115 try { 116 // find path on disk, get designated requirement (if signed) 117 string path = ctx.sdkPath(cmd); 118 if (RefPointer<DiskRep> rep = DiskRep::bestFileGuess(path)) 119 if (SecPointer<SecStaticCode> code = new SecStaticCode(rep)) 120 if (const Requirement *req = code->designatedRequirement()) { 121 CODESIGN_SIGN_DEP_INTERP(this, (char*)cmd, (void*)req); 122 // package up as host requirement and return that 123 Requirements::Maker maker; 124 maker.add(kSecHostRequirementType, req->clone()); 125 return maker.make(); 126 } 127 } catch (...) { 128 secdebug("filediskrep", "exception getting host requirement (ignored)"); 129 } 130 } 131 return NULL; 132} 133 134 135string FileDiskRep::format() 136{ 137 return "generic"; 138} 139 140 141// 142// FileDiskRep::Writers 143// 144DiskRep::Writer *FileDiskRep::writer() 145{ 146 return new Writer(this); 147} 148 149 150// 151// Write a component. 152// Note that this isn't concerned with Mach-O writing; this is handled at 153// a much higher level. If we're called, it's extended attribute time. 154// 155void FileDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 156{ 157 try { 158 fd().setAttr(attrName(CodeDirectory::canonicalSlotName(slot)), 159 CFDataGetBytePtr(data), CFDataGetLength(data)); 160 } catch (const UnixError &error) { 161 if (error.error == ERANGE) 162 MacOSError::throwMe(errSecCSCMSTooLarge); 163 throw; 164 } 165} 166 167 168// 169// Clear all signing data 170// 171void FileDiskRep::Writer::remove() 172{ 173 for (CodeDirectory::SpecialSlot slot = 0; slot < cdSlotCount; slot++) 174 if (const char *name = CodeDirectory::canonicalSlotName(slot)) 175 fd().removeAttr(attrName(name)); 176 fd().removeAttr(attrName(kSecCS_SIGNATUREFILE)); 177} 178 179 180// 181// We are NOT the preferred store for components because our approach 182// (extended attributes) suffers from some serious limitations. 183// 184bool FileDiskRep::Writer::preferredStore() 185{ 186 return false; 187} 188 189 190} // end namespace CodeSigning 191} // end namespace Security 192