filediskrep.cpp
1 /* 2 * Copyright (c) 2006-2007,2011 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 29 namespace Security { 30 namespace CodeSigning { 31 32 using namespace UnixPlusPlus; 33 34 35 // 36 // Everything's lazy in here 37 // 38 FileDiskRep::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 // 48 string 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 // 58 CFDataRef 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 // 86 CFDataRef 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 // 101 const 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 secinfo("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 secinfo("filediskrep", "exception getting host requirement (ignored)"); 129 } 130 } 131 return NULL; 132 } 133 134 135 string FileDiskRep::format() 136 { 137 return "generic"; 138 } 139 140 // 141 // FileDiskRep::Writers 142 // 143 DiskRep::Writer *FileDiskRep::writer() 144 { 145 return new Writer(this); 146 } 147 148 149 // 150 // Write a component. 151 // Note that this isn't concerned with Mach-O writing; this is handled at 152 // a much higher level. If we're called, it's extended attribute time. 153 // 154 void FileDiskRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 155 { 156 try { 157 std::string name = attrName(CodeDirectory::canonicalSlotName(slot)); 158 fd().setAttr(name, CFDataGetBytePtr(data), CFDataGetLength(data)); 159 mWrittenAttributes.insert(name); 160 } catch (const UnixError &error) { 161 if (error.error == ERANGE) 162 MacOSError::throwMe(errSecCSCMSTooLarge); 163 throw; 164 } 165 } 166 167 168 void FileDiskRep::Writer::flush() 169 { 170 size_t size = fd().listAttr(NULL, 0); 171 std::vector<char> buffer(size); 172 char *s = &buffer[0]; 173 char *end = &buffer[size]; 174 fd().listAttr(s, size); 175 while (s < end) { 176 std::string name = s; 177 s += strlen(s) + 1; // skip to next 178 if (name.compare(0, 13, "com.apple.cs.") == 0) // one of ours 179 if (mWrittenAttributes.find(name) == mWrittenAttributes.end()) { // not written by this signing operation 180 fd().removeAttr(name); 181 } 182 } 183 } 184 185 186 // 187 // Clear all signing data 188 // 189 void FileDiskRep::Writer::remove() 190 { 191 for (CodeDirectory::SpecialSlot slot = 0; slot < cdSlotCount; slot++) 192 if (const char *name = CodeDirectory::canonicalSlotName(slot)) 193 fd().removeAttr(attrName(name)); 194 fd().removeAttr(attrName(kSecCS_SIGNATUREFILE)); 195 } 196 197 198 // 199 // We are NOT the preferred store for components because our approach 200 // (extended attributes) suffers from some serious limitations. 201 // 202 bool FileDiskRep::Writer::preferredStore() 203 { 204 return false; 205 } 206 207 208 } // end namespace CodeSigning 209 } // end namespace Security