X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_codesigning/lib/reqdumper.cpp diff --git a/Security/libsecurity_codesigning/lib/reqdumper.cpp b/Security/libsecurity_codesigning/lib/reqdumper.cpp new file mode 100644 index 00000000..b2f003b9 --- /dev/null +++ b/Security/libsecurity_codesigning/lib/reqdumper.cpp @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2006-2007,2011-2013 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +// +// reqdumper - Requirement un-parsing (disassembly) +// +#include "reqdumper.h" +#include // OID encoder +#include + +namespace Security { +namespace CodeSigning { + +using namespace UnixPlusPlus; + + +// +// Table of reserved words (keywords), generated by ANTLR +// +static const char * const keywords[] = { +#include "RequirementKeywords.h" + "", + NULL +}; + + +// +// Printf to established output channel +// +void Dumper::print(const char *format, ...) +{ + char buffer[256]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + mOutput += buffer; +} + + +// +// Dump the underlying Requirement program +// +void Dumper::dump() +{ + this->expr(); + + // remove any initial space + if (mOutput[0] == ' ') + mOutput = mOutput.substr(1); +} + + +// +// Dump an entire Requirements set, using temporary Dumper objects. +// +// This detects single Requirement inputs and dumps them successfully (using +// single-requirement syntax). No indication of error is returned in this case. +// +string Dumper::dump(const Requirements *reqs, bool debug /* = false */) +{ + if (!reqs) { + return "# no requirement(s)"; + } else if (reqs->magic() == Requirement::typeMagic) { // single requirement + return dump((const Requirement *)reqs) + "\n"; + } else { + string result; + for (unsigned n = 0; n < reqs->count(); n++) { + char prefix[200]; + if (reqs->type(n) < kSecRequirementTypeCount) + snprintf(prefix, sizeof(prefix), + "%s => ", Requirement::typeNames[reqs->type(n)]); + else + snprintf(prefix, sizeof(prefix), "/*unknown type*/ %d => ", reqs->type(n)); + Dumper dumper(reqs->blob(n), debug); + dumper.expr(); + result += prefix + dumper.value() + "\n"; + } + return result; + } +} + +string Dumper::dump(const Requirement *req, bool debug /* = false */) +{ + Dumper dumper(req, debug); + try { + dumper.dump(); + return dumper; + } catch (const CommonError &err) { + if (debug) { + char errstr[80]; + snprintf(errstr, sizeof(errstr), " !! error %ld !!", (unsigned long)err.osStatus()); + return dumper.value() + errstr; + } + throw; + } +} + +string Dumper::dump(const BlobCore *req, bool debug /* = false */) +{ + switch (req->magic()) { + case Requirement::typeMagic: + return dump(static_cast(req), debug); + break; + case Requirements::typeMagic: + return dump(static_cast(req), debug); + break; + default: + return "invalid data type"; + } +} + + +// +// Element dumpers. Output accumulates in internal buffer. +// +void Dumper::expr(SyntaxLevel level) +{ + if (mDebug) + print("/*@0x%x*/", pc()); + ExprOp op = ExprOp(get()); + switch (op & ~opFlagMask) { + case opFalse: + print("never"); + break; + case opTrue: + print("always"); + break; + case opIdent: + print("identifier "); + data(); + break; + case opAppleAnchor: + print("anchor apple"); + break; + case opAppleGenericAnchor: + print("anchor apple generic"); + break; + case opAnchorHash: + print("certificate"); certSlot(); print(" = "); hashData(); + break; + case opInfoKeyValue: + if (mDebug) + print("/*legacy*/"); + print("info["); dotString(); print("] = "); data(); + break; + case opAnd: + if (level < slAnd) + print("("); + expr(slAnd); + print(" and "); + expr(slAnd); + if (level < slAnd) + print(")"); + break; + case opOr: + if (level < slOr) + print("("); + expr(slOr); + print(" or "); + expr(slOr); + if (level < slOr) + print(")"); + break; + case opNot: + print("! "); + expr(slPrimary); + break; + case opCDHash: + print("cdhash "); + hashData(); + break; + case opInfoKeyField: + print("info["); dotString(); print("]"); match(); + break; + case opEntitlementField: + print("entitlement["); dotString(); print("]"); match(); + break; + case opCertField: + print("certificate"); certSlot(); print("["); dotString(); print("]"); match(); + break; + case opCertGeneric: + print("certificate"); certSlot(); print("["); + { + const unsigned char *data; size_t length; + getData(data, length); + print("field.%s", CssmOid((unsigned char *)data, length).toOid().c_str()); + } + print("]"); match(); + break; + case opCertPolicy: + print("certificate"); certSlot(); print("["); + { + const unsigned char *data; size_t length; + getData(data, length); + print("policy.%s", CssmOid((unsigned char *)data, length).toOid().c_str()); + } + print("]"); match(); + break; + case opTrustedCert: + print("certificate"); certSlot(); print("trusted"); + break; + case opTrustedCerts: + print("anchor trusted"); + break; + case opNamedAnchor: + print("anchor apple "); data(); + break; + case opNamedCode: + print("("); data(); print(")"); + break; + default: + if (op & opGenericFalse) { + print(" false /* opcode %d */", op & ~opFlagMask); + break; + } else if (op & opGenericSkip) { + print(" /* opcode %d */", op & ~opFlagMask); + break; + } else { + print("OPCODE %d NOT UNDERSTOOD (ending print)", op); + return; + } + } +} + +void Dumper::certSlot() +{ + switch (int32_t slot = get()) { + case Requirement::anchorCert: + print(" root"); + break; + case Requirement::leafCert: + print(" leaf"); + break; + default: + print(" %d", slot); + break; + } +} + +void Dumper::match() +{ + switch (MatchOperation op = MatchOperation(get())) { + case matchExists: + print(" /* exists */"); + break; + case matchEqual: + print(" = "); data(); + break; + case matchContains: + print(" ~ "); data(); + break; + case matchBeginsWith: + print(" = "); data(); print("*"); + break; + case matchEndsWith: + print(" = *"); data(); + break; + case matchLessThan: + print(" < "); data(); + break; + case matchGreaterEqual: + print(" >= "); data(); + break; + case matchLessEqual: + print(" <= "); data(); + break; + case matchGreaterThan: + print(" > "); data(); + break; + default: + print("MATCH OPCODE %d NOT UNDERSTOOD", op); + break; + } +} + +void Dumper::hashData() +{ + print("H\""); + const unsigned char *data; size_t length; + getData(data, length); + printBytes(data, length); + print("\""); +} + +void Dumper::data(PrintMode bestMode /* = isSimple */, bool dotOkay /* = false */) +{ + const unsigned char *data; size_t length; + getData(data, length); + for (unsigned n = 0; n < length; n++) + if ((isalnum(data[n]) || (data[n] == '.' && dotOkay))) { // simple + if (n == 0 && isdigit(data[n])) // unquoted idents can't start with a digit + bestMode = isPrintable; + } else if (isgraph(data[n]) || isspace(data[n])) { + if (bestMode == isSimple) + bestMode = isPrintable; + } else { + bestMode = isBinary; + break; // pessimal + } + + if (bestMode == isSimple) { + string s((const char *)data, length); + for (const char * const * k = keywords; *k; k++) + if (s == *k) { + bestMode = isPrintable; // reserved word; need quotes + break; + } + } + + switch (bestMode) { + case isSimple: + print("%.*s", length, data); + break; + case isPrintable: + print("\"%.*s\"", length, data); + break; + default: + print("0x"); + printBytes(data, length); + break; + } +} + +void Dumper::printBytes(const Byte *data, size_t length) +{ + for (unsigned n = 0; n < length; n++) + print("%02.2x", data[n]); +} + + +} // CodeSigning +} // Security