]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_codesigning/lib/opaquewhitelist.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / opaquewhitelist.cpp
1 /*
2 * Copyright (c) 2014 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 "opaquewhitelist.h"
24 #include "csutilities.h"
25 #include "StaticCode.h"
26 #include <CoreFoundation/CoreFoundation.h>
27 #include <Security/SecCodePriv.h>
28 #include <Security/SecCodeSigner.h>
29 #include <Security/SecStaticCode.h>
30 #include <security_utilities/cfutilities.h>
31 #include <security_utilities/cfmunge.h>
32 #include <CoreFoundation/CFBundlePriv.h>
33 #include <spawn.h>
34
35 namespace Security {
36 namespace CodeSigning {
37
38 using namespace SQLite;
39
40
41 static std::string hashString(CFDataRef hash);
42 static void attachOpaque(SecStaticCodeRef code, SecAssessmentFeedback feedback);
43
44
45 //
46 // Open the database
47 //
48 OpaqueWhitelist::OpaqueWhitelist(const char *path, int flags)
49 : SQLite::Database(path ? path : opaqueDatabase, flags)
50 {
51 SQLite::Statement createConditions(*this,
52 "CREATE TABLE IF NOT EXISTS conditions ("
53 " label text,"
54 " weight real not null unique,"
55 " source text,"
56 " identifier text,"
57 " version text,"
58 " conditions text not null);"
59 );
60 createConditions.execute();
61 mOverrideQueue = dispatch_queue_create("com.apple.security.assessment.whitelist-override", DISPATCH_QUEUE_SERIAL);
62 }
63
64 OpaqueWhitelist::~OpaqueWhitelist()
65 {
66 dispatch_release(mOverrideQueue);
67 }
68
69
70 //
71 // Check if a code object is whitelisted
72 //
73 bool OpaqueWhitelist::contains(SecStaticCodeRef codeRef, SecAssessmentFeedback feedback, OSStatus reason)
74 {
75 // make our own copy of the code object, so we can poke at it without disturbing the original
76 SecPointer<SecStaticCode> code = new SecStaticCode(SecStaticCode::requiredStatic(codeRef)->diskRep());
77
78 CFCopyRef<CFDataRef> current = code->cdHash(); // current cdhash
79 CFDataRef opaque = NULL; // holds computed opaque cdhash
80 bool match = false; // holds final result
81
82 if (!current)
83 return false; // unsigned
84
85 // collect auxiliary information for trace
86 CFRef<CFDictionaryRef> info;
87 std::string team = "";
88 CFStringRef cfVersion = NULL, cfShortVersion = NULL, cfExecutable = NULL;
89 if (errSecSuccess == SecCodeCopySigningInformation(code->handle(false), kSecCSSigningInformation, &info.aref())) {
90 if (CFStringRef cfTeam = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoTeamIdentifier)))
91 team = cfString(cfTeam);
92 if (CFDictionaryRef infoPlist = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList))) {
93 if (CFTypeRef version = CFDictionaryGetValue(infoPlist, kCFBundleVersionKey))
94 if (CFGetTypeID(version) == CFStringGetTypeID())
95 cfVersion = CFStringRef(version);
96 if (CFTypeRef shortVersion = CFDictionaryGetValue(infoPlist, _kCFBundleShortVersionStringKey))
97 if (CFGetTypeID(shortVersion) == CFStringGetTypeID())
98 cfShortVersion = CFStringRef(shortVersion);
99 if (CFTypeRef executable = CFDictionaryGetValue(infoPlist, kCFBundleExecutableKey))
100 if (CFGetTypeID(executable) == CFStringGetTypeID())
101 cfExecutable = CFStringRef(executable);
102 }
103 }
104
105 // compute and attach opaque signature
106 attachOpaque(code->handle(false), feedback);
107 opaque = code->cdHash();
108
109 // lookup current cdhash in whitelist
110 SQLite::Statement lookup(*this, "SELECT opaque FROM whitelist WHERE current=:current"
111 " AND opaque != 'disable override'");
112 lookup.bind(":current") = current.get();
113 while (lookup.nextRow()) {
114 CFRef<CFDataRef> expected = lookup[0].data();
115 if (CFEqual(opaque, expected)) {
116 match = true; // actual opaque cdhash matches expected
117 break;
118 }
119 }
120
121 // prepare strings for use inside block
122 std::string currentHash = hashString(current);
123 std::string opaqueHash = hashString(opaque);
124
125 // send a trace indicating the result
126 MessageTrace trace("com.apple.security.assessment.whitelist2", code->identifier().c_str());
127 trace.add("signature2", "%s", currentHash.c_str());
128 trace.add("signature3", "%s", opaqueHash.c_str());
129 trace.add("result", match ? "pass" : "fail");
130 trace.add("reason", "%d", reason);
131 if (!team.empty())
132 trace.add("teamid", "%s", team.c_str());
133 if (cfVersion)
134 trace.add("version", "%s", cfString(cfVersion).c_str());
135 if (cfShortVersion)
136 trace.add("version2", "%s", cfString(cfShortVersion).c_str());
137 if (cfExecutable)
138 trace.add("execname", "%s", cfString(cfExecutable).c_str());
139 trace.send("");
140
141 return match;
142 }
143
144
145 //
146 // Obtain special validation conditions for a static code, based on database configuration.
147 //
148 CFDictionaryRef OpaqueWhitelist::validationConditionsFor(SecStaticCodeRef code)
149 {
150 // figure out which team key to use
151 std::string team = "UNKNOWN";
152 CFStringRef cfId = NULL;
153 CFStringRef cfVersion = NULL;
154 CFRef<CFDictionaryRef> info; // holds lifetimes for the above
155 if (errSecSuccess == SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref())) {
156 if (CFStringRef cfTeam = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoTeamIdentifier)))
157 team = cfString(cfTeam);
158 cfId = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoIdentifier));
159 if (CFDictionaryRef infoPlist = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList)))
160 if (CFTypeRef version = CFDictionaryGetValue(infoPlist, _kCFBundleShortVersionStringKey))
161 if (CFGetTypeID(version) == CFStringGetTypeID())
162 cfVersion = CFStringRef(version);
163 }
164 if (cfId == NULL) // unsigned; punt
165 return NULL;
166
167 // find the highest weight matching condition. We perform no merging and the heaviest rule wins
168 SQLite::Statement matches(*this,
169 "SELECT conditions FROM conditions"
170 " WHERE (source = :source or source IS NULL)"
171 " AND (identifier = :identifier or identifier is NULL)"
172 " AND ((:version IS NULL AND version IS NULL) OR (version = :version OR version IS NULL))"
173 " ORDER BY weight DESC"
174 " LIMIT 1"
175 );
176 matches.bind(":source") = team;
177 matches.bind(":identifier") = cfString(cfId);
178 if (cfVersion)
179 matches.bind(":version") = cfString(cfVersion);
180 if (matches.nextRow()) {
181 CFTemp<CFDictionaryRef> conditions((const char*)matches[0]);
182 return conditions.yield();
183 }
184 // no matches
185 return NULL;
186 }
187
188
189 //
190 // Convert a SHA1 hash to a hex string
191 //
192 static std::string hashString(CFDataRef hash)
193 {
194 if (CFDataGetLength(hash) != sizeof(SHA1::Digest)) {
195 return std::string();
196 } else {
197 const UInt8 *bytes = CFDataGetBytePtr(hash);
198 char s[2 * SHA1::digestLength + 1];
199 for (unsigned n = 0; n < SHA1::digestLength; n++)
200 sprintf(&s[2*n], "%2.2x", bytes[n]);
201 return std::string(s);
202 }
203 }
204
205
206 //
207 // Add a code object to the whitelist
208 //
209 void OpaqueWhitelist::add(SecStaticCodeRef codeRef)
210 {
211 // make our own copy of the code object
212 SecPointer<SecStaticCode> code = new SecStaticCode(SecStaticCode::requiredStatic(codeRef)->diskRep());
213
214 CFCopyRef<CFDataRef> current = code->cdHash();
215 attachOpaque(code->handle(false), NULL); // compute and attach an opaque signature
216 CFDataRef opaque = code->cdHash();
217
218 SQLite::Statement insert(*this, "INSERT OR REPLACE INTO whitelist (current,opaque) VALUES (:current, :opaque)");
219 insert.bind(":current") = current.get();
220 insert.bind(":opaque") = opaque;
221 insert.execute();
222 }
223
224
225 //
226 // Generate and attach an ad-hoc opaque signature
227 //
228 static void attachOpaque(SecStaticCodeRef code, SecAssessmentFeedback feedback)
229 {
230 CFTemp<CFDictionaryRef> rules("{" // same resource rules as used for collection
231 "rules={"
232 "'^.*' = #T"
233 "'^Info\\.plist$' = {omit=#T,weight=10}"
234 "},rules2={"
235 "'^(Frameworks|SharedFrameworks|Plugins|Plug-ins|XPCServices|Helpers|MacOS)/' = {nested=#T, weight=0}"
236 "'^.*' = #T"
237 "'^Info\\.plist$' = {omit=#T,weight=10}"
238 "'^[^/]+$' = {top=#T, weight=0}"
239 "}"
240 "}");
241
242 CFRef<CFDataRef> signature = CFDataCreateMutable(NULL, 0);
243 CFTemp<CFDictionaryRef> arguments("{%O=%O, %O=#N, %O=%O}",
244 kSecCodeSignerDetached, signature.get(),
245 kSecCodeSignerIdentity, /* kCFNull, */
246 kSecCodeSignerResourceRules, rules.get());
247 CFRef<SecCodeSignerRef> signer;
248 SecCSFlags creationFlags = kSecCSSignOpaque | kSecCSSignNoV1 | kSecCSSignBundleRoot;
249 SecCSFlags operationFlags = 0;
250
251 if (feedback)
252 operationFlags |= kSecCSReportProgress;
253 MacOSError::check(SecStaticCodeSetCallback(code, kSecCSDefaultFlags, NULL, ^CFTypeRef(SecStaticCodeRef code, CFStringRef stage, CFDictionaryRef info) {
254 if (CFEqual(stage, CFSTR("progress"))) {
255 bool proceed = feedback(kSecAssessmentFeedbackProgress, info);
256 if (!proceed)
257 SecStaticCodeCancelValidation(code, kSecCSDefaultFlags);
258 }
259 return NULL;
260 }));
261
262 MacOSError::check(SecCodeSignerCreate(arguments, creationFlags, &signer.aref()));
263 MacOSError::check(SecCodeSignerAddSignature(signer, code, operationFlags));
264 MacOSError::check(SecCodeSetDetachedSignature(code, signature, kSecCSDefaultFlags));
265 }
266
267
268 } // end namespace CodeSigning
269 } // end namespace Security