]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2006-2007,2011-2013 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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 | // reqdumper - Requirement un-parsing (disassembly) | |
26 | // | |
27 | #include "reqdumper.h" | |
866f8763 | 28 | #if TARGET_OS_OSX |
b1ab9ed8 | 29 | #include <security_cdsa_utilities/cssmdata.h> // OID encoder |
866f8763 | 30 | #endif |
b1ab9ed8 A |
31 | #include <cstdarg> |
32 | ||
33 | namespace Security { | |
34 | namespace CodeSigning { | |
35 | ||
36 | using namespace UnixPlusPlus; | |
37 | ||
38 | ||
39 | // | |
40 | // Table of reserved words (keywords), generated by ANTLR | |
41 | // | |
42 | static const char * const keywords[] = { | |
43 | #include "RequirementKeywords.h" | |
44 | "", | |
45 | NULL | |
46 | }; | |
47 | ||
48 | ||
49 | // | |
50 | // Printf to established output channel | |
51 | // | |
52 | void Dumper::print(const char *format, ...) | |
53 | { | |
54 | char buffer[256]; | |
55 | va_list args; | |
56 | va_start(args, format); | |
57 | vsnprintf(buffer, sizeof(buffer), format, args); | |
58 | va_end(args); | |
59 | mOutput += buffer; | |
60 | } | |
61 | ||
62 | ||
63 | // | |
64 | // Dump the underlying Requirement program | |
65 | // | |
66 | void Dumper::dump() | |
67 | { | |
68 | this->expr(); | |
69 | ||
70 | // remove any initial space | |
71 | if (mOutput[0] == ' ') | |
72 | mOutput = mOutput.substr(1); | |
73 | } | |
74 | ||
75 | ||
76 | // | |
77 | // Dump an entire Requirements set, using temporary Dumper objects. | |
78 | // | |
79 | // This detects single Requirement inputs and dumps them successfully (using | |
80 | // single-requirement syntax). No indication of error is returned in this case. | |
81 | // | |
82 | string Dumper::dump(const Requirements *reqs, bool debug /* = false */) | |
83 | { | |
84 | if (!reqs) { | |
85 | return "# no requirement(s)"; | |
86 | } else if (reqs->magic() == Requirement::typeMagic) { // single requirement | |
87 | return dump((const Requirement *)reqs) + "\n"; | |
88 | } else { | |
89 | string result; | |
90 | for (unsigned n = 0; n < reqs->count(); n++) { | |
91 | char prefix[200]; | |
92 | if (reqs->type(n) < kSecRequirementTypeCount) | |
93 | snprintf(prefix, sizeof(prefix), | |
94 | "%s => ", Requirement::typeNames[reqs->type(n)]); | |
95 | else | |
96 | snprintf(prefix, sizeof(prefix), "/*unknown type*/ %d => ", reqs->type(n)); | |
97 | Dumper dumper(reqs->blob<Requirement>(n), debug); | |
98 | dumper.expr(); | |
99 | result += prefix + dumper.value() + "\n"; | |
100 | } | |
101 | return result; | |
102 | } | |
103 | } | |
104 | ||
105 | string Dumper::dump(const Requirement *req, bool debug /* = false */) | |
106 | { | |
107 | Dumper dumper(req, debug); | |
108 | try { | |
109 | dumper.dump(); | |
110 | return dumper; | |
111 | } catch (const CommonError &err) { | |
112 | if (debug) { | |
113 | char errstr[80]; | |
114 | snprintf(errstr, sizeof(errstr), " !! error %ld !!", (unsigned long)err.osStatus()); | |
115 | return dumper.value() + errstr; | |
116 | } | |
117 | throw; | |
118 | } | |
119 | } | |
120 | ||
121 | string Dumper::dump(const BlobCore *req, bool debug /* = false */) | |
122 | { | |
123 | switch (req->magic()) { | |
124 | case Requirement::typeMagic: | |
125 | return dump(static_cast<const Requirement *>(req), debug); | |
b1ab9ed8 A |
126 | case Requirements::typeMagic: |
127 | return dump(static_cast<const Requirements *>(req), debug); | |
b1ab9ed8 A |
128 | default: |
129 | return "invalid data type"; | |
130 | } | |
131 | } | |
132 | ||
133 | ||
134 | // | |
135 | // Element dumpers. Output accumulates in internal buffer. | |
136 | // | |
137 | void Dumper::expr(SyntaxLevel level) | |
138 | { | |
139 | if (mDebug) | |
140 | print("/*@0x%x*/", pc()); | |
141 | ExprOp op = ExprOp(get<uint32_t>()); | |
142 | switch (op & ~opFlagMask) { | |
143 | case opFalse: | |
144 | print("never"); | |
145 | break; | |
146 | case opTrue: | |
147 | print("always"); | |
148 | break; | |
149 | case opIdent: | |
150 | print("identifier "); | |
151 | data(); | |
152 | break; | |
153 | case opAppleAnchor: | |
154 | print("anchor apple"); | |
155 | break; | |
156 | case opAppleGenericAnchor: | |
157 | print("anchor apple generic"); | |
158 | break; | |
159 | case opAnchorHash: | |
160 | print("certificate"); certSlot(); print(" = "); hashData(); | |
161 | break; | |
162 | case opInfoKeyValue: | |
163 | if (mDebug) | |
164 | print("/*legacy*/"); | |
165 | print("info["); dotString(); print("] = "); data(); | |
166 | break; | |
167 | case opAnd: | |
168 | if (level < slAnd) | |
169 | print("("); | |
170 | expr(slAnd); | |
171 | print(" and "); | |
172 | expr(slAnd); | |
173 | if (level < slAnd) | |
174 | print(")"); | |
175 | break; | |
176 | case opOr: | |
177 | if (level < slOr) | |
178 | print("("); | |
179 | expr(slOr); | |
180 | print(" or "); | |
181 | expr(slOr); | |
182 | if (level < slOr) | |
183 | print(")"); | |
184 | break; | |
185 | case opNot: | |
186 | print("! "); | |
187 | expr(slPrimary); | |
188 | break; | |
189 | case opCDHash: | |
427c49bc | 190 | print("cdhash "); |
b1ab9ed8 A |
191 | hashData(); |
192 | break; | |
193 | case opInfoKeyField: | |
194 | print("info["); dotString(); print("]"); match(); | |
195 | break; | |
196 | case opEntitlementField: | |
197 | print("entitlement["); dotString(); print("]"); match(); | |
198 | break; | |
199 | case opCertField: | |
200 | print("certificate"); certSlot(); print("["); dotString(); print("]"); match(); | |
201 | break; | |
202 | case opCertGeneric: | |
203 | print("certificate"); certSlot(); print("["); | |
866f8763 | 204 | #if TARGET_OS_OSX |
b1ab9ed8 A |
205 | { |
206 | const unsigned char *data; size_t length; | |
207 | getData(data, length); | |
208 | print("field.%s", CssmOid((unsigned char *)data, length).toOid().c_str()); | |
209 | } | |
866f8763 | 210 | #endif |
b1ab9ed8 A |
211 | print("]"); match(); |
212 | break; | |
213 | case opCertPolicy: | |
214 | print("certificate"); certSlot(); print("["); | |
866f8763 | 215 | #if TARGET_OS_OSX |
b1ab9ed8 A |
216 | { |
217 | const unsigned char *data; size_t length; | |
218 | getData(data, length); | |
219 | print("policy.%s", CssmOid((unsigned char *)data, length).toOid().c_str()); | |
220 | } | |
866f8763 | 221 | #endif |
b1ab9ed8 A |
222 | print("]"); match(); |
223 | break; | |
224 | case opTrustedCert: | |
225 | print("certificate"); certSlot(); print("trusted"); | |
226 | break; | |
227 | case opTrustedCerts: | |
228 | print("anchor trusted"); | |
229 | break; | |
230 | case opNamedAnchor: | |
231 | print("anchor apple "); data(); | |
232 | break; | |
233 | case opNamedCode: | |
234 | print("("); data(); print(")"); | |
235 | break; | |
5c19dc3a A |
236 | case opPlatform: |
237 | print("platform = %d", get<int32_t>()); | |
238 | break; | |
79b9da22 A |
239 | case opNotarized: |
240 | print("notarized"); | |
241 | break; | |
b1ab9ed8 A |
242 | default: |
243 | if (op & opGenericFalse) { | |
244 | print(" false /* opcode %d */", op & ~opFlagMask); | |
245 | break; | |
246 | } else if (op & opGenericSkip) { | |
247 | print(" /* opcode %d */", op & ~opFlagMask); | |
248 | break; | |
249 | } else { | |
250 | print("OPCODE %d NOT UNDERSTOOD (ending print)", op); | |
251 | return; | |
252 | } | |
253 | } | |
254 | } | |
255 | ||
256 | void Dumper::certSlot() | |
257 | { | |
258 | switch (int32_t slot = get<int32_t>()) { | |
259 | case Requirement::anchorCert: | |
260 | print(" root"); | |
261 | break; | |
262 | case Requirement::leafCert: | |
263 | print(" leaf"); | |
264 | break; | |
265 | default: | |
266 | print(" %d", slot); | |
267 | break; | |
268 | } | |
269 | } | |
270 | ||
271 | void Dumper::match() | |
272 | { | |
273 | switch (MatchOperation op = MatchOperation(get<uint32_t>())) { | |
274 | case matchExists: | |
275 | print(" /* exists */"); | |
276 | break; | |
277 | case matchEqual: | |
278 | print(" = "); data(); | |
279 | break; | |
280 | case matchContains: | |
281 | print(" ~ "); data(); | |
282 | break; | |
283 | case matchBeginsWith: | |
284 | print(" = "); data(); print("*"); | |
285 | break; | |
286 | case matchEndsWith: | |
287 | print(" = *"); data(); | |
288 | break; | |
289 | case matchLessThan: | |
290 | print(" < "); data(); | |
291 | break; | |
292 | case matchGreaterEqual: | |
293 | print(" >= "); data(); | |
294 | break; | |
295 | case matchLessEqual: | |
296 | print(" <= "); data(); | |
297 | break; | |
298 | case matchGreaterThan: | |
299 | print(" > "); data(); | |
300 | break; | |
301 | default: | |
302 | print("MATCH OPCODE %d NOT UNDERSTOOD", op); | |
303 | break; | |
304 | } | |
305 | } | |
306 | ||
307 | void Dumper::hashData() | |
308 | { | |
309 | print("H\""); | |
310 | const unsigned char *data; size_t length; | |
311 | getData(data, length); | |
312 | printBytes(data, length); | |
313 | print("\""); | |
314 | } | |
315 | ||
316 | void Dumper::data(PrintMode bestMode /* = isSimple */, bool dotOkay /* = false */) | |
317 | { | |
318 | const unsigned char *data; size_t length; | |
319 | getData(data, length); | |
320 | for (unsigned n = 0; n < length; n++) | |
321 | if ((isalnum(data[n]) || (data[n] == '.' && dotOkay))) { // simple | |
322 | if (n == 0 && isdigit(data[n])) // unquoted idents can't start with a digit | |
323 | bestMode = isPrintable; | |
324 | } else if (isgraph(data[n]) || isspace(data[n])) { | |
325 | if (bestMode == isSimple) | |
326 | bestMode = isPrintable; | |
327 | } else { | |
328 | bestMode = isBinary; | |
329 | break; // pessimal | |
330 | } | |
331 | ||
332 | if (bestMode == isSimple) { | |
333 | string s((const char *)data, length); | |
334 | for (const char * const * k = keywords; *k; k++) | |
335 | if (s == *k) { | |
336 | bestMode = isPrintable; // reserved word; need quotes | |
337 | break; | |
338 | } | |
339 | } | |
340 | ||
341 | switch (bestMode) { | |
342 | case isSimple: | |
866f8763 | 343 | print("%.*s", (int)length, data); |
b1ab9ed8 A |
344 | break; |
345 | case isPrintable: | |
5c19dc3a A |
346 | print("\""); |
347 | for (unsigned n = 0; n < length; n++) | |
348 | switch (data[n]) { | |
349 | case '\\': | |
350 | case '"': | |
351 | print("\\%c", data[n]); | |
352 | break; | |
353 | default: | |
354 | print("%c", data[n]); | |
355 | break; | |
356 | } | |
357 | print("\""); | |
b1ab9ed8 A |
358 | break; |
359 | default: | |
360 | print("0x"); | |
361 | printBytes(data, length); | |
362 | break; | |
363 | } | |
364 | } | |
365 | ||
366 | void Dumper::printBytes(const Byte *data, size_t length) | |
367 | { | |
368 | for (unsigned n = 0; n < length; n++) | |
369 | print("%02.2x", data[n]); | |
370 | } | |
371 | ||
372 | ||
373 | } // CodeSigning | |
374 | } // Security |