/ securityd / src / codesigdb.cpp
codesigdb.cpp
  1  /*
  2   * Copyright (c) 2003-2009,2012,2016 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  //
 26  // codesigdb - code-hash equivalence database
 27  //
 28  #include "codesigdb.h"
 29  #include "process.h"
 30  #include "server.h"
 31  #include "agentquery.h"
 32  #include <security_utilities/memutils.h>
 33  #include <security_utilities/logging.h>
 34  #include <Security/SecRequirementPriv.h>
 35  
 36  
 37  //
 38  // Construct a CodeSignatures objects
 39  //
 40  CodeSignatures::CodeSignatures()
 41  {
 42  }
 43  
 44  CodeSignatures::~CodeSignatures()
 45  {
 46  }
 47  
 48  //
 49  // (Re)open the equivalence database.
 50  // This is useful to switch to database in another volume.
 51  //
 52  void CodeSignatures::open(const char *path)
 53  {
 54  }
 55  
 56  
 57  //
 58  // Basic Identity objects
 59  //
 60  CodeSignatures::Identity::Identity() : mState(untried)
 61  { }
 62  
 63  CodeSignatures::Identity::~Identity()
 64  { }
 65  
 66  //
 67  // Verify signature matches.
 68  // This ends up getting called when a CodeSignatureAclSubject is validated.
 69  // The OSXVerifier describes what we require of the client code; the process represents
 70  // the requesting client; and the context gives us access to the ACL and its environment
 71  // in case we want to, well, creatively rewrite it for some reason.
 72  //
 73  bool CodeSignatures::verify(Process &process,
 74  	const OSXVerifier &verifier, const AclValidationContext &context)
 75  {
 76  	secinfo("codesign", "start verify");
 77  
 78  	StLock<Mutex> _(process);
 79  	if (SecRequirementRef requirement = verifier.requirement()) {
 80  		// If the ACL contains a code signature (requirement), we won't match against unsigned code at all.
 81  		// The legacy hash is ignored (it's for use by pre-Leopard systems).
 82  		secinfo("codesign", "CS requirement present; ignoring legacy hashes");
 83  		Server::active().longTermActivity();
 84  		switch (OSStatus rc = process.checkValidity(kSecCSDefaultFlags, requirement)) {
 85  		case noErr:
 86  			secinfo("codesign", "CS verify passed");
 87  			return true;
 88  		case errSecCSUnsigned:
 89  			secinfo("codesign", "CS verify against unsigned binary failed");
 90  			return false;
 91  		default:
 92  			secinfo("codesign", "CS verify failed OSStatus=%d", int32_t(rc));
 93  			return false;
 94  		}
 95  	}
 96  	switch (matchSignedClientToLegacyACL(process, verifier, context)) {
 97  	case noErr:						// handled, allow access
 98  		return true;
 99   	case errSecCSUnsigned: {		// unsigned client, complete legacy case
100   		secinfo("codesign", "no CS requirement - using legacy hash");
101  
102  		/*
103  		 * We should stop supporting this case for binaries
104  		 * built for modern OS/SDK, user should ad-hoc sign
105  		 * their binaries in that case.
106  		 *
107  		 * <rdar://problem/20546287>
108  		 */
109  		Identity &clientIdentity = process;
110  		try {
111  			if (clientIdentity.getHash() == CssmData::wrap(verifier.legacyHash(), SHA1::digestLength)) {
112  				secinfo("codesign", "direct match: pass");
113  				return true;
114  			}
115  		} catch (...) {
116  			secinfo("codesign", "exception getting client code hash: fail");
117  			return false;
118  		}
119  		return false;
120  	}
121  	default:						// client unsuitable, reject this match
122  		return false;
123  	}
124  }
125  
126  //
127  // See if we can rewrite the ACL from legacy to Code Signing form without losing too much security.
128  // Returns true if the present validation should succeed (we probably rewrote the ACL).
129  // Returns false if the present validation shouldn't succeed based on what we did here (we may still
130  // have rewritten the ACL, in principle).
131  //
132  // Note that these checks add nontrivial overhead to ACL processing. We want to eventually phase
133  // this out, or at least make it an option that doesn't run all the time - perhaps an "extra legacy
134  // effort" per-client mode bit.
135  //
136  static string trim(string s, char delimiter)
137  {
138  	string::size_type p = s.rfind(delimiter);
139  	if (p != string::npos)
140  		s = s.substr(p + 1);
141  	return s;
142  }
143  
144  static string trim(string s, char delimiter, string suffix)
145  {
146  	s = trim(s, delimiter);
147  	size_t preLength = s.length() - suffix.length();
148  	if (preLength > 0 && s.substr(preLength) == suffix)
149  		s = s.substr(0, preLength);
150  	return s;
151  }
152  
153  OSStatus CodeSignatures::matchSignedClientToLegacyACL(Process &process,
154  	const OSXVerifier &verifier, const AclValidationContext &context)
155  {
156  	//
157  	// Check whether we seem to be matching a legacy .Mac ACL against a member of the .Mac group
158  	//
159  	if (SecurityServerAcl::looksLikeLegacyDotMac(context)) {
160  		Server::active().longTermActivity();
161  		CFRef<SecRequirementRef> dotmac;
162  		MacOSError::check(SecRequirementCreateGroup(CFSTR("dot-mac"), NULL, kSecCSDefaultFlags, &dotmac.aref()));
163  		if (process.checkValidity(kSecCSDefaultFlags, dotmac) == noErr) {
164  			secinfo("codesign", "client is a dot-mac application; update the ACL accordingly");
165  
166  			// create a suitable AclSubject (this is the above-the-API-line way)
167  			CFRef<CFDataRef> reqdata;
168  			MacOSError::check(SecRequirementCopyData(dotmac, kSecCSDefaultFlags, &reqdata.aref()));
169  			RefPointer<CodeSignatureAclSubject> subject = new CodeSignatureAclSubject(NULL, "group://dot-mac");
170  			subject->add((const BlobCore *)CFDataGetBytePtr(reqdata));
171  
172  			// add it to the ACL and pass the access check (we just quite literally did it above)
173  			SecurityServerAcl::addToStandardACL(context, subject);
174  			return noErr;
175  		}
176  	}
177  
178  	//
179  	// Get best names for the ACL (legacy) subject and the (signed) client
180  	//
181  	CFRef<CFDictionaryRef> info;
182  	MacOSError::check(process.copySigningInfo(kSecCSSigningInformation, &info.aref()));
183  	CFStringRef signingIdentity = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoIdentifier));
184  	if (!signingIdentity)		// unsigned
185  		return errSecCSUnsigned;
186  
187  	string bundleName;	// client
188  	if (CFDictionaryRef infoList = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList)))
189  		if (CFStringRef name = CFStringRef(CFDictionaryGetValue(infoList, kCFBundleNameKey)))
190  			bundleName = trim(cfString(name), '.');
191  	if (bundleName.empty())	// fall back to signing identifier
192  		bundleName = trim(cfString(signingIdentity), '.');
193  
194  	string aclName = trim(verifier.path(), '/', ".app");	// ACL
195  
196  	secinfo("codesign", "matching signed client \"%s\" against legacy ACL \"%s\"",
197  		bundleName.c_str(), aclName.c_str());
198  
199  	//
200  	// Check whether we're matching a signed APPLE application against a legacy ACL by the same name
201  	//
202  	if (bundleName == aclName) {
203  		const unsigned char reqData[] = {		// "anchor apple", version 1 blob, embedded here
204  			0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10,
205  			0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03
206  		};
207  		CFRef<SecRequirementRef> apple;
208  		MacOSError::check(SecRequirementCreateWithData(CFTempData(reqData, sizeof(reqData)),
209  			kSecCSDefaultFlags, &apple.aref()));
210  		Server::active().longTermActivity();
211  		switch (OSStatus rc = process.checkValidity(kSecCSDefaultFlags, apple)) {
212  		case noErr:
213  			{
214  				secinfo("codesign", "withstands strict scrutiny; quietly adding new ACL");
215  				RefPointer<AclSubject> subject = process.copyAclSubject();
216  				SecurityServerAcl::addToStandardACL(context, subject);
217  				return noErr;
218  			}
219  		default:
220  			secinfo("codesign", "validation fails with rc=%d, rejecting", int32_t(rc));
221  			return rc;
222  		}
223  	}
224  
225  	// not close enough to even ask - this can't match
226  	return errSecCSReqFailed;
227  }