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 }