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