]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* |
2 | * Copyright (c) 2006-2007 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 | // reqdumper - Requirement un-parsing (disassembly) | |
26 | // | |
27 | #include "reqdumper.h" | |
28 | #include <security_cdsa_utilities/cssmdata.h> // OID encoder | |
29 | #include <cstdarg> | |
30 | ||
31 | namespace Security { | |
32 | namespace CodeSigning { | |
33 | ||
34 | using namespace UnixPlusPlus; | |
35 | ||
36 | ||
37 | // | |
38 | // Table of reserved words (keywords), generated by ANTLR | |
39 | // | |
40 | static const char * const keywords[] = { | |
41 | #include "RequirementKeywords.h" | |
42 | "", | |
43 | NULL | |
44 | }; | |
45 | ||
46 | ||
47 | // | |
48 | // Printf to established output channel | |
49 | // | |
50 | void Dumper::print(const char *format, ...) | |
51 | { | |
52 | char buffer[256]; | |
53 | va_list args; | |
54 | va_start(args, format); | |
55 | vsnprintf(buffer, sizeof(buffer), format, args); | |
56 | va_end(args); | |
57 | mOutput += buffer; | |
58 | } | |
59 | ||
60 | ||
61 | // | |
62 | // Dump the underlying Requirement program | |
63 | // | |
64 | void Dumper::dump() | |
65 | { | |
66 | this->expr(); | |
67 | ||
68 | // remove any initial space | |
69 | if (mOutput[0] == ' ') | |
70 | mOutput = mOutput.substr(1); | |
71 | } | |
72 | ||
73 | ||
74 | // | |
75 | // Dump an entire Requirements set, using temporary Dumper objects. | |
76 | // | |
77 | // This detects single Requirement inputs and dumps them successfully (using | |
78 | // single-requirement syntax). No indication of error is returned in this case. | |
79 | // | |
80 | string Dumper::dump(const Requirements *reqs, bool debug /* = false */) | |
81 | { | |
82 | if (!reqs) { | |
83 | return "# no requirement(s)"; | |
84 | } else if (reqs->magic() == Requirement::typeMagic) { // single requirement | |
85 | return dump((const Requirement *)reqs) + "\n"; | |
86 | } else { | |
87 | string result; | |
88 | for (unsigned n = 0; n < reqs->count(); n++) { | |
89 | char prefix[200]; | |
90 | if (reqs->type(n) < kSecRequirementTypeCount) | |
91 | snprintf(prefix, sizeof(prefix), | |
92 | "%s => ", Requirement::typeNames[reqs->type(n)]); | |
93 | else | |
94 | snprintf(prefix, sizeof(prefix), "/*unknown type*/ %d => ", reqs->type(n)); | |
95 | Dumper dumper(reqs->blob<Requirement>(n), debug); | |
96 | dumper.expr(); | |
97 | result += prefix + dumper.value() + "\n"; | |
98 | } | |
99 | return result; | |
100 | } | |
101 | } | |
102 | ||
103 | string Dumper::dump(const Requirement *req, bool debug /* = false */) | |
104 | { | |
105 | Dumper dumper(req, debug); | |
106 | try { | |
107 | dumper.dump(); | |
108 | return dumper; | |
109 | } catch (const CommonError &err) { | |
110 | if (debug) { | |
111 | char errstr[80]; | |
112 | snprintf(errstr, sizeof(errstr), " !! error %ld !!", (unsigned long)err.osStatus()); | |
113 | return dumper.value() + errstr; | |
114 | } | |
115 | throw; | |
116 | } | |
117 | } | |
118 | ||
119 | string Dumper::dump(const BlobCore *req, bool debug /* = false */) | |
120 | { | |
121 | switch (req->magic()) { | |
122 | case Requirement::typeMagic: | |
123 | return dump(static_cast<const Requirement *>(req), debug); | |
124 | break; | |
125 | case Requirements::typeMagic: | |
126 | return dump(static_cast<const Requirements *>(req), debug); | |
127 | break; | |
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("["); | |
204 | { | |
205 | const unsigned char *data; size_t length; | |
206 | getData(data, length); | |
207 | print("field.%s", CssmOid((unsigned char *)data, length).toOid().c_str()); | |
208 | } | |
209 | print("]"); match(); | |
210 | break; | |
211 | case opCertPolicy: | |
212 | print("certificate"); certSlot(); print("["); | |
213 | { | |
214 | const unsigned char *data; size_t length; | |
215 | getData(data, length); | |
216 | print("policy.%s", CssmOid((unsigned char *)data, length).toOid().c_str()); | |
217 | } | |
218 | print("]"); match(); | |
219 | break; | |
220 | case opTrustedCert: | |
221 | print("certificate"); certSlot(); print("trusted"); | |
222 | break; | |
223 | case opTrustedCerts: | |
224 | print("anchor trusted"); | |
225 | break; | |
226 | case opNamedAnchor: | |
227 | print("anchor apple "); data(); | |
228 | break; | |
229 | case opNamedCode: | |
230 | print("("); data(); print(")"); | |
231 | break; | |
232 | default: | |
233 | if (op & opGenericFalse) { | |
234 | print(" false /* opcode %d */", op & ~opFlagMask); | |
235 | break; | |
236 | } else if (op & opGenericSkip) { | |
237 | print(" /* opcode %d */", op & ~opFlagMask); | |
238 | break; | |
239 | } else { | |
240 | print("OPCODE %d NOT UNDERSTOOD (ending print)", op); | |
241 | return; | |
242 | } | |
243 | } | |
244 | } | |
245 | ||
246 | void Dumper::certSlot() | |
247 | { | |
248 | switch (int32_t slot = get<int32_t>()) { | |
249 | case Requirement::anchorCert: | |
250 | print(" root"); | |
251 | break; | |
252 | case Requirement::leafCert: | |
253 | print(" leaf"); | |
254 | break; | |
255 | default: | |
256 | print(" %d", slot); | |
257 | break; | |
258 | } | |
259 | } | |
260 | ||
261 | void Dumper::match() | |
262 | { | |
263 | switch (MatchOperation op = MatchOperation(get<uint32_t>())) { | |
264 | case matchExists: | |
265 | print(" /* exists */"); | |
266 | break; | |
267 | case matchEqual: | |
268 | print(" = "); data(); | |
269 | break; | |
270 | case matchContains: | |
271 | print(" ~ "); data(); | |
272 | break; | |
273 | case matchBeginsWith: | |
274 | print(" = "); data(); print("*"); | |
275 | break; | |
276 | case matchEndsWith: | |
277 | print(" = *"); data(); | |
278 | break; | |
279 | case matchLessThan: | |
280 | print(" < "); data(); | |
281 | break; | |
282 | case matchGreaterEqual: | |
283 | print(" >= "); data(); | |
284 | break; | |
285 | case matchLessEqual: | |
286 | print(" <= "); data(); | |
287 | break; | |
288 | case matchGreaterThan: | |
289 | print(" > "); data(); | |
290 | break; | |
291 | default: | |
292 | print("MATCH OPCODE %d NOT UNDERSTOOD", op); | |
293 | break; | |
294 | } | |
295 | } | |
296 | ||
297 | void Dumper::hashData() | |
298 | { | |
299 | print("H\""); | |
300 | const unsigned char *data; size_t length; | |
301 | getData(data, length); | |
302 | printBytes(data, length); | |
303 | print("\""); | |
304 | } | |
305 | ||
306 | void Dumper::data(PrintMode bestMode /* = isSimple */, bool dotOkay /* = false */) | |
307 | { | |
308 | const unsigned char *data; size_t length; | |
309 | getData(data, length); | |
310 | for (unsigned n = 0; n < length; n++) | |
311 | if ((isalnum(data[n]) || (data[n] == '.' && dotOkay))) { // simple | |
312 | if (n == 0 && isdigit(data[n])) // unquoted idents can't start with a digit | |
313 | bestMode = isPrintable; | |
314 | } else if (isgraph(data[n]) || isspace(data[n])) { | |
315 | if (bestMode == isSimple) | |
316 | bestMode = isPrintable; | |
317 | } else { | |
318 | bestMode = isBinary; | |
319 | break; // pessimal | |
320 | } | |
321 | ||
322 | if (bestMode == isSimple) { | |
323 | string s((const char *)data, length); | |
324 | for (const char * const * k = keywords; *k; k++) | |
325 | if (s == *k) { | |
326 | bestMode = isPrintable; // reserved word; need quotes | |
327 | break; | |
328 | } | |
329 | } | |
330 | ||
331 | switch (bestMode) { | |
332 | case isSimple: | |
333 | print("%.*s", length, data); | |
334 | break; | |
335 | case isPrintable: | |
336 | print("\"%.*s\"", length, data); | |
337 | break; | |
338 | default: | |
339 | print("0x"); | |
340 | printBytes(data, length); | |
341 | break; | |
342 | } | |
343 | } | |
344 | ||
345 | void Dumper::printBytes(const Byte *data, size_t length) | |
346 | { | |
347 | for (unsigned n = 0; n < length; n++) | |
348 | print("%02.2x", data[n]); | |
349 | } | |
350 | ||
351 | ||
352 | } // CodeSigning | |
353 | } // Security |