/ OSX / libsecurity_codesigning / lib / filediskrep.cpp
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