/ OSX / libsecurity_codesigning / lib / reqdumper.cpp
reqdumper.cpp
  1  /*
  2   * Copyright (c) 2006-2007,2011-2013 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  #if TARGET_OS_OSX
 29  #include <security_cdsa_utilities/cssmdata.h>	// OID encoder
 30  #endif
 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);
126  	case Requirements::typeMagic:
127  		return dump(static_cast<const Requirements *>(req), debug);
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:
190  		print("cdhash ");
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 opCertFieldDate:
203  		print("certificate"); certSlot(); print("[");
204  #if TARGET_OS_OSX
205  		{
206  			const unsigned char *data; size_t length;
207  			getData(data, length);
208  			print("timestamp.%s", CssmOid((unsigned char *)data, length).toOid().c_str());
209  		}
210  #endif
211  	case opCertGeneric:
212  		print("certificate"); certSlot(); print("[");
213  #if TARGET_OS_OSX
214  		{
215  			const unsigned char *data; size_t length;
216  			getData(data, length);
217  			print("field.%s", CssmOid((unsigned char *)data, length).toOid().c_str());
218  		}
219  #endif
220  		print("]"); match();
221  		break;
222  	case opCertPolicy:
223  		print("certificate"); certSlot(); print("[");
224  #if TARGET_OS_OSX
225  		{
226  			const unsigned char *data; size_t length;
227  			getData(data, length);
228  			print("policy.%s", CssmOid((unsigned char *)data, length).toOid().c_str());
229  		}
230  #endif
231  		print("]"); match();
232  		break;
233  	case opTrustedCert:
234  		print("certificate"); certSlot(); print("trusted");
235  		break;
236  	case opTrustedCerts:
237  		print("anchor trusted");
238  		break;
239  	case opNamedAnchor:
240  		print("anchor apple "); data();
241  		break;
242  	case opNamedCode:
243  		print("("); data(); print(")");
244  		break;
245  	case opPlatform:
246  		print("platform = %d", get<int32_t>());
247  		break;
248  	case opNotarized:
249  		print("notarized");
250  		break;
251  	case opLegacyDevID:
252  		print("legacy");
253  		break;
254  	default:
255  		if (op & opGenericFalse) {
256  			print(" false /* opcode %d */", op & ~opFlagMask);
257  			break;
258  		} else if (op & opGenericSkip) {
259  			print(" /* opcode %d */", op & ~opFlagMask);
260  			break;
261  		} else {
262  			print("OPCODE %d NOT UNDERSTOOD (ending print)", op);
263  			return;
264  		}
265  	}
266  }
267  
268  void Dumper::certSlot()
269  {
270  	switch (int32_t slot = get<int32_t>()) {
271  	case Requirement::anchorCert:
272  		print(" root");
273  		break;
274  	case Requirement::leafCert:
275  		print(" leaf");
276  		break;
277  	default:
278  		print(" %d", slot);
279  		break; 
280  	}
281  }
282  
283  void Dumper::match()
284  {
285  	switch (MatchOperation op = MatchOperation(get<uint32_t>())) {
286  	case matchExists:
287  		print(" /* exists */");
288  		break;
289  	case matchAbsent:
290  		print(" absent ");
291  		break;
292  	case matchEqual:
293  		print(" = "); data();
294  		break;
295  	case matchContains:
296  		print(" ~ "); data();
297  		break;
298  	case matchBeginsWith:
299  		print(" = "); data(); print("*");
300  		break;
301  	case matchEndsWith:
302  		print(" = *"); data();
303  		break;
304  	case matchLessThan:
305  		print(" < "); data();
306  		break;
307  	case matchGreaterEqual:
308  		print(" >= "); data();
309  		break;
310  	case matchLessEqual:
311  		print(" <= "); data();
312  		break;
313  	case matchGreaterThan:
314  		print(" > "); data();
315  		break;
316  	case matchOn:
317  		print(" = "); timestamp();
318  		break;
319  	case matchBefore:
320  		print(" < "); timestamp();
321  		break;
322  	case matchAfter:
323  		print(" > "); timestamp();
324  		break;
325  	case matchOnOrBefore:
326  		print(" <= "); timestamp();
327  		break;
328  	case matchOnOrAfter:
329  		print(" >= "); timestamp();
330  		break;
331  	default:
332  		print("MATCH OPCODE %d NOT UNDERSTOOD", op);
333  		break;
334  	}
335  }
336  
337  void Dumper::hashData()
338  {
339  	print("H\"");
340  	const unsigned char *data; size_t length;
341  	getData(data, length);
342  	printBytes(data, length);
343  	print("\"");
344  }
345  
346  void Dumper::data(PrintMode bestMode /* = isSimple */, bool dotOkay /* = false */)
347  {
348  	const unsigned char *data; size_t length;
349  	getData(data, length);
350  	for (unsigned n = 0; n < length; n++)
351  		if ((isalnum(data[n]) || (data[n] == '.' && dotOkay))) {	// simple
352  			if (n == 0 && isdigit(data[n]))	// unquoted idents can't start with a digit
353  				bestMode = isPrintable;
354  		} else if (isgraph(data[n]) || isspace(data[n])) {
355  			if (bestMode == isSimple)
356  				bestMode = isPrintable;
357  		} else {
358  			bestMode = isBinary;
359  			break;		// pessimal
360  		}
361  	
362  	if (bestMode == isSimple) {
363  		string s((const char *)data, length);
364  		for (const char * const * k = keywords; *k; k++)
365  			if (s == *k) {
366  				bestMode = isPrintable;		// reserved word; need quotes
367  				break;
368  			}
369  	}
370  		
371  	switch (bestMode) {
372  	case isSimple:
373  		print("%.*s", (int)length, data);
374  		break;
375  	case isPrintable:
376  		print("\"");
377  		for (unsigned n = 0; n < length; n++)
378  			switch (data[n]) {
379  			case '\\':
380  			case '"':
381  				print("\\%c", data[n]);
382  				break;
383  			default:
384  				print("%c", data[n]);
385  				break;
386  			}
387  		print("\"");
388  		break;
389  	default:
390  		print("0x");
391  		printBytes(data, length);
392  		break;
393  	}
394  }
395  	
396  void Dumper::timestamp()
397  {
398  	CFAbsoluteTime at = static_cast<CFAbsoluteTime>(get<int64_t>());
399  	CFRef<CFDateRef> date = CFDateCreate(NULL, at);
400  	
401  	CFRef<CFStringRef> str = CFCopyDescription(date);
402  	
403  	print("<%s>", cfString(str).c_str());
404  }
405  
406  void Dumper::printBytes(const Byte *data, size_t length)
407  {
408  	for (unsigned n = 0; n < length; n++)
409  		print("%02.2x", data[n]);
410  }
411  
412  
413  }	// CodeSigning
414  }	// Security