]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_codesigning/requirements.grammar
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_codesigning / requirements.grammar
diff --git a/Security/libsecurity_codesigning/requirements.grammar b/Security/libsecurity_codesigning/requirements.grammar
new file mode 100644 (file)
index 0000000..6b83f05
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * Copyright (c) 2006-2008 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@
+ */
+
+//
+// Requirements Language Grammar
+//
+// This file describes two distinct (related) grammars:
+//     Requirement => single requirement (Requirement *)
+//     RequirementSet => set of labeled requirements (Requirements *)
+// The grammar can "autosense" - i.e. recognize which one it's fed and
+// return appropriate semantic data.
+//
+// The semantic data compiled is a malloc'ed BlobCore * - a Requirement
+// object or a SuperBlob containing multiple Requirements.
+//
+// Errors are indicated to the caller by accumulating error message strings
+// in the errors member variable. Any non-empty error value indicates failure.
+// Presence of semantic data is not a reliable indication of success.
+//
+header "post_include_hpp" {
+#include "requirement.h"
+using namespace CodeSigning;
+typedef Requirement::Maker Maker;
+}
+
+header "post_include_cpp" {
+#include "requirement.h"
+#include "reqmaker.h"
+#include "csutilities.h"
+#include <security_utilities/cfutilities.h>
+#include <security_utilities/hashing.h>
+#include <security_cdsa_utilities/cssmdata.h>  // OID coding
+using namespace CodeSigning;
+typedef Requirement::Maker Maker;
+}
+
+options {
+       language="Cpp";
+       namespace="Security_CodeSigning";
+       namespaceStd="std";
+       namespaceAntlr="antlr";
+       genHashLines=false;
+}
+
+
+{
+       //
+       // Collect error messages.
+       // Note that the immediate caller takes the absence of collected error messages
+       // to indicate compilation success.
+       //
+       void RequirementParser::reportError(const antlr::RecognitionException &ex)
+       {
+               errors += ex.toString() + "\n";
+       }
+       
+       void RequirementParser::reportError(const std::string &s)
+       {
+               errors += s + "\n";
+       }
+
+       
+       //
+       // Parser helper functions
+       //
+       string RequirementParser::hexString(const string &s)
+       {
+               if (s.size() % 2)
+                       throw antlr::SemanticException("odd number of digits");
+               const char *p = s.data();
+               string result;
+               for (unsigned n = 0; n < s.length(); n += 2) {
+                       char c;
+                       sscanf(p+n, "%2hhx", &c);
+                       result.push_back(c);
+               }
+               return result;
+       }
+
+       void RequirementParser::hashString(const string &s, SHA1::Digest hash)
+       {
+               if (s.size() != 2 * SHA1::digestLength)
+                       throw antlr::SemanticException("invalid hash length");
+               memcpy(hash, hexString(s).data(), SHA1::digestLength);
+       }
+       
+       static const char *matchPrefix(const string &key, const char *prefix)
+       {
+               unsigned pLength = strlen(prefix);
+               if (!key.compare(0, pLength, prefix, 0, pLength))
+                       return key.c_str() + pLength;
+               else
+                       return NULL;
+       }
+       
+       void RequirementParser::certMatchOperation(Maker &maker, int32_t slot, string key)
+       {
+               if (matchPrefix(key, "subject.")) {
+                       maker.put(opCertField);
+                       maker.put(slot);
+                       maker.put(key);
+               } else if (const char *oids = matchPrefix(key, "field.")) {
+                       maker.put(opCertGeneric);
+                       maker.put(slot);
+                       CssmAutoData oid(Allocator::standard()); oid.fromOid(oids);
+                       maker.putData(oid.data(), oid.length());
+               } else if (const char *oids = matchPrefix(key, "extension.")) {
+                       maker.put(opCertGeneric);
+                       maker.put(slot);
+                       CssmAutoData oid(Allocator::standard()); oid.fromOid(oids);
+                       maker.putData(oid.data(), oid.length());
+               } else if (const char *oids = matchPrefix(key, "policy.")) {
+                       maker.put(opCertPolicy);
+                       maker.put(slot);
+                       CssmAutoData oid(Allocator::standard()); oid.fromOid(oids);
+                       maker.putData(oid.data(), oid.length());
+               } else {
+                       throw antlr::SemanticException(key + ": unrecognized certificate field");
+               }
+       }
+}
+
+
+class RequirementParser extends Parser;
+
+options {
+       k=2;
+}
+
+{
+public:
+       std::string errors;
+       void reportError(const antlr::RecognitionException &ex);
+       void reportError(const std::string &s);
+
+private:
+       static string hexString(const string &s);
+       static void hashString(const string &s, SHA1::Digest hash);
+       void certMatchOperation(Maker &maker, int32_t slot, string key);
+}
+
+
+//
+// Compound target; compiles single requirements or requirement sets
+// and returns them as a BlobCore.
+//
+autosense returns [BlobCore *result = NULL]
+       :       result=requirement
+       |       result=requirementSet
+       ;
+
+
+//
+// A Requirements Set.
+//
+requirementSet returns [Requirements *result = NULL]
+               { Requirements::Maker maker; }
+       :       (               { uint32_t t; Requirement *req; }
+                       t=requirementType ARROW req=requirementElement
+                               { maker.add(t, req); }
+               )+
+                       { result = errors.empty() ? maker() : NULL; }
+               EOF
+       ;
+
+requirementType returns [uint32_t type = kSecInvalidRequirementType]
+       :       "guest"
+                       { type = kSecGuestRequirementType; }
+       |       "host"
+                       { type = kSecHostRequirementType; }
+       |       "designated"
+                       { type = kSecDesignatedRequirementType; }
+       |       "library"
+                       { type = kSecLibraryRequirementType; }
+       |       "plugin"
+                       { type = kSecPluginRequirementType; }
+       |       stype:INTEGER
+                       { type = atol(stype->getText().c_str()); }
+       ;
+
+
+//
+// A single Requirement (untyped)
+//
+requirement returns [Requirement *result = NULL]
+       :       result = requirementElement
+               EOF
+       ;
+
+requirementElement returns [Requirement *result = NULL]
+               { Requirement::Maker maker; }
+       :       expr[maker]
+               { result = maker(); }
+               ( fluff )*
+       ;
+
+
+//
+// Classic recursive expressions
+// 
+expr[Maker &maker]
+               { Maker::Label label(maker); }
+       :       term[maker] ( "or" { maker.insert<ExprOp>(label) = opOr; } term[maker] )*
+       ;
+
+term[Maker &maker]
+               { Maker::Label label(maker); }
+       :       primary[maker] ( "and" { maker.insert<ExprOp>(label) = opAnd; } primary[maker] )*
+       ;
+
+primary[Maker &maker]
+       :       LPAREN expr[maker] RPAREN
+       |       NOT { maker.put(opNot); } primary[maker]
+       |       ( "always" | "true" )
+                       { maker.put(opTrue); }
+       |       ( "never" | "false" )
+                       { maker.put(opFalse); }
+       |       certspec[maker]
+       |       infospec[maker]
+       |       entitlementspec[maker]
+       |       "identifier" { string code; } eql code=identifierString
+                       { maker.ident(code); }
+       |       "cdhash" { SHA1::Digest digest; } eql hash[digest]
+                       { maker.cdhash(digest); }
+       |       LPAREN { string name; } name=identifierString RPAREN
+                       { maker.put(opNamedCode); maker.put(name); }
+       ;
+
+
+//
+// Certificate specifications restrict certificates in the signing chain
+//
+certspec[Maker &maker]
+       :       "anchor" "apple" appleanchor[maker]
+       |       "anchor" "generic" "apple"              // alternate form
+                       { maker.put(opAppleGenericAnchor); }
+       |       ( "certificate" | "cert" | "anchor" ) "trusted"
+                       { maker.trustedAnchor(); }
+       |       ( "certificate" | "cert" ) { int32_t slot; } slot=certSlot
+               ( certslotspec[maker, slot] | "trusted" { maker.trustedAnchor(slot); } )
+       |       "anchor" certslotspec[maker, Requirement::anchorCert]
+       ;
+
+appleanchor[Maker &maker]
+       :       empty
+                       { maker.put(opAppleAnchor); }
+       |       "generic"
+                       { maker.put(opAppleGenericAnchor); }
+       |       { string name; } name=identifierString
+                       { maker.put(opNamedAnchor); maker.put(name); }
+       ;
+
+certslotspec[Maker &maker, int32_t slot]       { string key; }
+       :       eql { SHA1::Digest digest; } certificateDigest[digest]
+            { maker.anchor(slot, digest); }
+       |       key=bracketKey
+                       { certMatchOperation(maker, slot, key); }
+               match_suffix[maker]
+       ;
+
+
+//
+// Info specifications place conditions on entries in the Info.plist
+//
+infospec[Maker &maker]         { string key; }
+       :       "info" key=bracketKey
+                       { maker.put(opInfoKeyField); maker.put(key); }
+               match_suffix[maker]
+       ;
+
+
+//
+// Entitlement specifications place conditions on embedded entitlement entries
+//
+entitlementspec[Maker &maker]  { string key; }
+       :       "entitlement" key=bracketKey
+                       { maker.put(opEntitlementField); maker.put(key); }
+               match_suffix[maker]
+       ;
+
+
+//
+// Common match operations, written as a syntactic suffix (the operand precedes this)
+//
+match_suffix[Maker &maker]
+       :       empty ( "exists" ) ?
+                       { maker.put(matchExists); }
+       |       ( EQL | EQQL )
+                       { MatchOperation mop = matchEqual; string value; }
+               ( STAR { mop = matchEndsWith; } ) ?
+               value=datavalue
+               ( STAR { mop = (mop == matchEndsWith) ? matchContains : matchBeginsWith; } ) ?
+                       { maker.put(mop); maker.put(value); }
+       |       SUBS { string value; } value=datavalue
+                       { maker.put(matchContains); maker.put(value); }
+       |       LESS { string value; } value=datavalue
+                       { maker.put(matchLessThan); maker.put(value); }
+       |       GT { string value; } value=datavalue
+                       { maker.put(matchGreaterThan); maker.put(value); }
+       |       LE { string value; } value=datavalue
+                       { maker.put(matchLessEqual); maker.put(value); }
+       |       GE { string value; } value=datavalue
+                       { maker.put(matchGreaterEqual); maker.put(value); }
+       ;
+
+bracketKey returns [string key]
+       :       LBRACK key=stringvalue RBRACK
+       ;
+
+//
+// A certSlot identifies one certificate from the certificate chain
+//
+certSlot returns [int32_t slot = 0]
+       :       s:INTEGER               // counting from the anchor up
+                       { slot = atol(s->getText().c_str()); }
+       |       NEG ss:INTEGER  // counting from the leaf down
+                       { slot = -atol(ss->getText().c_str()); }
+       |       "leaf"                  // the leaf ( == -1)
+                       { slot = Requirement::leafCert; }
+       |       "root"                  // the root ( == 0)
+                       { slot = Requirement::anchorCert; }
+       ;
+
+// an arbitrary digest value
+hash[SHA1::Digest digest]
+       :       hash:HASHCONSTANT
+                       { hashString(hash->getText(), digest); }
+       ;
+
+// various forms to specify a certificate hash
+certificateDigest[SHA1::Digest digest]
+       :       hash[digest]
+       |       { string path; } path=pathstring
+                       { if (CFRef<CFDataRef> certData = cfLoadFile(path))
+                               hashOfCertificate(CFDataGetBytePtr(certData), CFDataGetLength(certData), digest);
+                         else
+                               throw antlr::SemanticException(path + ": not found");
+                       }
+       ;
+
+// generic data - can be simple string, quoted string, or 0x-style hex
+datavalue returns [string result]
+       :       result=stringvalue
+       |       hex:HEXCONSTANT { result = hexString(hex->getText()); }
+       ;
+
+// strings can always be quoted, but DOTKEYs don't need to be
+stringvalue returns [string result]
+       :       dk:DOTKEY       { result = dk->getText(); }
+       |       s:STRING        { result = s->getText(); }
+       ;
+
+// pathstrings are like strings, but PATHNAMEs don't need to be quoted either
+pathstring returns [string result]
+       :       dk:DOTKEY       { result = dk->getText(); }
+       |       s:STRING        { result = s->getText(); }
+       |       pn:PATHNAME     { result = pn->getText(); }
+       ;
+
+// unique identifier value
+identifierString returns [string result]
+       :       dk:DOTKEY       { result = dk->getText(); }
+       |       s:STRING        { result = s->getText(); }
+       ;
+
+// syntactic cavity generators
+fluff
+       :       SEMI
+       ;
+
+eql
+       :       EQL
+       |       EQQL
+       |       empty
+       ;
+
+empty : ;
+
+
+//
+// The lexer for the Requirement language.
+// Really straightforward and conventional.
+// A subset of strings don't need to be quoted (DOTKEYs). Neither do some simple
+//  pathnames starting with "/".
+// Hash values have a special syntax H"abcd" (abcd in straight hex).
+// Hex constants of the form 0xabcd can have any length; they are carried
+// around as strings (which are in turn stored as data in the language binary).
+//
+class RequirementLexer extends Lexer;
+
+options {
+       k=2;
+       testLiterals=false;
+}
+
+protected
+IDENT options { testLiterals=true; }
+       :       ( 'A' .. 'Z' | 'a' .. 'z' ) ( 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' )*
+       ;
+
+DOTKEY options { testLiterals=true; }
+       :       IDENT ( "." ( IDENT | INTEGER ) )*
+       ;
+
+PATHNAME
+       :       "/" IDENT ( "/" IDENT )+
+       ;
+
+HASHCONSTANT
+       :       'H'! '"'! ( HEX )+ '"'!
+       ;
+
+HEXCONSTANT
+       :       '0'! 'x'! ( HEX )+
+       ;
+
+STRING
+       :       '"'! ( ( '\\'! '"' ) | ( ~ ( '"' | '\\' ) ) )* '"'!
+       ;
+
+INTEGER
+       :       ( '0' .. '9' ) +
+       ;
+
+protected
+HEX    :       '0' .. '9' | 'a' .. 'f' | 'A' .. 'F' ;
+
+// operator tokens
+ARROW  : "=>" ;
+SEMI   : ';' ;
+LPAREN : '(' ;
+RPAREN : ')' ;
+LBRACK : '[' ;
+RBRACK : ']' ;
+LESS   : '<' ;
+GT             : '>' ;
+LE             : "<=" ;
+GE             : ">=" ;
+COMMA  : ',' ;
+EQL            : '=' ;
+EQQL   : "==" ;
+SUBS   : '~' ;
+NEG            : '-' ;
+NOT            : '!' ;
+STAR   : '*' ;
+
+
+//
+// White spaces
+//
+WS     :       ( ' ' | '\n' { newline(); } | '\t' )+
+       { $setType(antlr::Token::SKIP); }
+       ;
+
+SHELLCOMMENT
+       :       '#' ( ~ '\n' )*
+       { $setType(antlr::Token::SKIP); }
+       ;
+
+C_COMMENT
+       :       "/*" ( (~'*')|('*'(~'/')) )* "*/"
+       { $setType(antlr::Token::SKIP); }
+       ;
+
+CPP_COMMENT
+       :       "//" ( ~ '\n' )*
+       { $setType(antlr::Token::SKIP); }
+       ;