]> git.saurik.com Git - apple/libsecurity_codesigning.git/blame - requirements.grammar
libsecurity_codesigning-55005.tar.gz
[apple/libsecurity_codesigning.git] / requirements.grammar
CommitLineData
7d31e928 1/*
d1c1ab47 2 * Copyright (c) 2006-2008 Apple Inc. All Rights Reserved.
7d31e928
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// Requirements Language Grammar
26//
27// This file describes two distinct (related) grammars:
28// Requirement => single requirement (Requirement *)
29// RequirementSet => set of labeled requirements (Requirements *)
30// The grammar can "autosense" - i.e. recognize which one it's fed and
31// return appropriate semantic data.
516ae477 32//
7d31e928
A
33// The semantic data compiled is a malloc'ed BlobCore * - a Requirement
34// object or a SuperBlob containing multiple Requirements.
35//
516ae477
A
36// Errors are indicated to the caller by accumulating error message strings
37// in the errors member variable. Any non-empty error value indicates failure.
38// Presence of semantic data is not a reliable indication of success.
39//
7d31e928
A
40header "post_include_hpp" {
41#include "requirement.h"
42using namespace CodeSigning;
43typedef Requirement::Maker Maker;
44}
45
46header "post_include_cpp" {
47#include "requirement.h"
48#include "reqmaker.h"
49#include "csutilities.h"
50#include <security_utilities/cfutilities.h>
51#include <security_utilities/hashing.h>
516ae477 52#include <security_cdsa_utilities/cssmdata.h> // OID coding
7d31e928
A
53using namespace CodeSigning;
54typedef Requirement::Maker Maker;
55}
56
57options {
58 language="Cpp";
59 namespace="Security_CodeSigning";
60 namespaceStd="std";
61 namespaceAntlr="antlr";
62 genHashLines=false;
63}
64
65
66{
67 //
516ae477
A
68 // Collect error messages.
69 // Note that the immediate caller takes the absence of collected error messages
70 // to indicate compilation success.
7d31e928
A
71 //
72 void RequirementParser::reportError(const antlr::RecognitionException &ex)
73 {
74 errors += ex.toString() + "\n";
75 }
76
77 void RequirementParser::reportError(const std::string &s)
78 {
79 errors += s + "\n";
80 }
81
82
83 //
84 // Parser helper functions
85 //
86 string RequirementParser::hexString(const string &s)
87 {
88 if (s.size() % 2)
89 throw antlr::SemanticException("odd number of digits");
90 const char *p = s.data();
91 string result;
92 for (unsigned n = 0; n < s.length(); n += 2) {
93 char c;
94 sscanf(p+n, "%2hhx", &c);
95 result.push_back(c);
96 }
97 return result;
98 }
99
100 void RequirementParser::hashString(const string &s, SHA1::Digest hash)
101 {
102 if (s.size() != 2 * SHA1::digestLength)
103 throw antlr::SemanticException("invalid hash length");
104 memcpy(hash, hexString(s).data(), SHA1::digestLength);
105 }
516ae477 106
d1c1ab47 107 static const char *matchPrefix(const string &key, const char *prefix)
516ae477 108 {
d1c1ab47
A
109 unsigned pLength = strlen(prefix);
110 if (!key.compare(0, pLength, prefix, 0, pLength))
111 return key.c_str() + pLength;
112 else
113 return NULL;
114 }
115
116 void RequirementParser::certMatchOperation(Maker &maker, int32_t slot, string key)
117 {
118 if (matchPrefix(key, "subject.")) {
516ae477
A
119 maker.put(opCertField);
120 maker.put(slot);
121 maker.put(key);
d1c1ab47
A
122 } else if (const char *oids = matchPrefix(key, "field.")) {
123 maker.put(opCertGeneric);
124 maker.put(slot);
125 CssmAutoData oid(Allocator::standard()); oid.fromOid(oids);
126 maker.putData(oid.data(), oid.length());
127 } else if (const char *oids = matchPrefix(key, "extension.")) {
516ae477
A
128 maker.put(opCertGeneric);
129 maker.put(slot);
d1c1ab47
A
130 CssmAutoData oid(Allocator::standard()); oid.fromOid(oids);
131 maker.putData(oid.data(), oid.length());
516ae477
A
132 } else {
133 throw antlr::SemanticException(key + ": unrecognized certificate field");
134 }
135 }
7d31e928
A
136}
137
516ae477 138
7d31e928
A
139class RequirementParser extends Parser;
140
141options {
142 k=2;
143}
144
145{
146public:
147 std::string errors;
148 void reportError(const antlr::RecognitionException &ex);
149 void reportError(const std::string &s);
150
151private:
152 static string hexString(const string &s);
153 static void hashString(const string &s, SHA1::Digest hash);
d1c1ab47 154 void certMatchOperation(Maker &maker, int32_t slot, string key);
7d31e928
A
155}
156
157
158//
159// Compound target; compiles single requirements or requirement sets
160// and returns them as a BlobCore.
161//
162autosense returns [BlobCore *result = NULL]
163 : result=requirement
164 | result=requirementSet
165 ;
166
167
168//
169// A Requirements Set.
170//
171requirementSet returns [Requirements *result = NULL]
172 { Requirements::Maker maker; }
173 : ( { uint32_t t; Requirement *req; }
174 t=requirementType ARROW req=requirementElement
175 { maker.add(t, req); }
176 )+
177 { result = errors.empty() ? maker() : NULL; }
178 EOF
179 ;
180
181requirementType returns [uint32_t type = kSecInvalidRequirementType]
182 : "guest"
183 { type = kSecGuestRequirementType; }
184 | "host"
185 { type = kSecHostRequirementType; }
186 | "designated"
187 { type = kSecDesignatedRequirementType; }
188 | "library"
189 { type = kSecLibraryRequirementType; }
190 | stype:INTEGER
191 { type = atol(stype->getText().c_str()); }
192 ;
193
194
195//
196// A single Requirement (untyped)
197//
198requirement returns [Requirement *result = NULL]
199 : result = requirementElement
200 EOF
201 ;
202
203requirementElement returns [Requirement *result = NULL]
204 { Requirement::Maker maker; }
205 : expr[maker]
206 { result = maker(); }
207 ( fluff )*
208 ;
209
210
211//
212// Classic recursive expressions
213//
214expr[Maker &maker]
215 { Maker::Label label(maker); }
d1c1ab47 216 : term[maker] ( "or" { maker.insert<ExprOp>(label) = opOr; } term[maker] )*
7d31e928
A
217 ;
218
219term[Maker &maker]
220 { Maker::Label label(maker); }
d1c1ab47 221 : primary[maker] ( "and" { maker.insert<ExprOp>(label) = opAnd; } primary[maker] )*
7d31e928
A
222 ;
223
224primary[Maker &maker]
225 : LPAREN expr[maker] RPAREN
226 | NOT { maker.put(opNot); } primary[maker]
227 | ( "always" | "true" )
228 { maker.put(opTrue); }
229 | ( "never" | "false" )
230 { maker.put(opFalse); }
231 | certspec[maker]
232 | infospec[maker]
516ae477 233 | entitlementspec[maker]
7d31e928
A
234 | "identifier" { string code; } eql code=identifierString
235 { maker.ident(code); }
516ae477 236 | "cdhash" { SHA1::Digest digest; } eql hash[digest]
7d31e928
A
237 { maker.cdhash(digest); }
238 ;
239
240
241//
242// Certificate specifications restrict certificates in the signing chain
243//
244certspec[Maker &maker]
516ae477
A
245 : "anchor" "apple" appleanchor[maker]
246 | "anchor" "generic" "apple" // alternate form
247 { maker.put(opAppleGenericAnchor); }
7d31e928
A
248 | ( "certificate" | "cert" | "anchor" ) "trusted"
249 { maker.trustedAnchor(); }
d1c1ab47 250 | ( "certificate" | "cert" ) { int32_t slot; } slot=certSlot
7d31e928
A
251 ( certslotspec[maker, slot] | "trusted" { maker.trustedAnchor(slot); } )
252 | "anchor" certslotspec[maker, Requirement::anchorCert]
253 ;
254
516ae477
A
255appleanchor[Maker &maker]
256 : empty
257 { maker.put(opAppleAnchor); }
258 | "generic"
259 { maker.put(opAppleGenericAnchor); }
260 ;
261
d1c1ab47 262certslotspec[Maker &maker, int32_t slot] { string key; }
7d31e928
A
263 : eql { SHA1::Digest digest; } certificateDigest[digest]
264 { maker.anchor(slot, digest); }
265 | key=bracketKey
516ae477 266 { certMatchOperation(maker, slot, key); }
7d31e928
A
267 match_suffix[maker]
268 ;
269
270
271//
272// Info specifications place conditions on entries in the Info.plist
273//
274infospec[Maker &maker] { string key; }
275 : "info" key=bracketKey
276 { maker.put(opInfoKeyField); maker.put(key); }
277 match_suffix[maker]
278 ;
279
516ae477
A
280
281//
282// Entitlement specifications place conditions on embedded entitlement entries
283//
284entitlementspec[Maker &maker] { string key; }
285 : "entitlement" key=bracketKey
286 { maker.put(opEntitlementField); maker.put(key); }
287 match_suffix[maker]
288 ;
289
290
291//
292// Common match operations, written as a syntactic suffix (the operand precedes this)
293//
7d31e928 294match_suffix[Maker &maker]
516ae477 295 : empty ( "exists" ) ?
7d31e928 296 { maker.put(matchExists); }
516ae477
A
297 | ( EQL | EQQL )
298 { MatchOperation mop = matchEqual; string value; }
299 ( STAR { mop = matchEndsWith; } ) ?
300 value=datavalue
301 ( STAR { mop = (mop == matchEndsWith) ? matchContains : matchBeginsWith; } ) ?
302 { maker.put(mop); maker.put(value); }
7d31e928
A
303 | SUBS { string value; } value=datavalue
304 { maker.put(matchContains); maker.put(value); }
516ae477
A
305 | LESS { string value; } value=datavalue
306 { maker.put(matchLessThan); maker.put(value); }
307 | GT { string value; } value=datavalue
308 { maker.put(matchGreaterThan); maker.put(value); }
309 | LE { string value; } value=datavalue
310 { maker.put(matchLessEqual); maker.put(value); }
311 | GE { string value; } value=datavalue
312 { maker.put(matchGreaterEqual); maker.put(value); }
7d31e928
A
313 ;
314
315bracketKey returns [string key]
316 : LBRACK key=stringvalue RBRACK
317 ;
318
319//
d1c1ab47 320// A certSlot identifies one certificate from the certificate chain
7d31e928 321//
d1c1ab47 322certSlot returns [int32_t slot = 0]
7d31e928
A
323 : s:INTEGER // counting from the anchor up
324 { slot = atol(s->getText().c_str()); }
325 | NEG ss:INTEGER // counting from the leaf down
326 { slot = -atol(ss->getText().c_str()); }
327 | "leaf" // the leaf ( == -1)
328 { slot = Requirement::leafCert; }
329 | "root" // the root ( == 0)
330 { slot = Requirement::anchorCert; }
331 ;
332
7d31e928
A
333// an arbitrary digest value
334hash[SHA1::Digest digest]
335 : hash:HASHCONSTANT
336 { hashString(hash->getText(), digest); }
337 ;
338
339// various forms to specify a certificate hash
340certificateDigest[SHA1::Digest digest]
341 : hash[digest]
342 | { string path; } path=pathstring
343 { if (CFRef<CFDataRef> certData = cfLoadFile(path))
344 hashOfCertificate(CFDataGetBytePtr(certData), CFDataGetLength(certData), digest);
345 else
346 throw antlr::SemanticException(path + ": not found");
347 }
348 ;
349
350// generic data - can be simple string, quoted string, or 0x-style hex
351datavalue returns [string result]
352 : result=stringvalue
353 | hex:HEXCONSTANT { result = hexString(hex->getText()); }
354 ;
355
356// strings can always be quoted, but DOTKEYs don't need to be
357stringvalue returns [string result]
358 : dk:DOTKEY { result = dk->getText(); }
359 | s:STRING { result = s->getText(); }
360 ;
361
362// pathstrings are like strings, but PATHNAMEs don't need to be quoted either
363pathstring returns [string result]
364 : dk:DOTKEY { result = dk->getText(); }
365 | s:STRING { result = s->getText(); }
366 | pn:PATHNAME { result = pn->getText(); }
367 ;
368
369// unique identifier value
370identifierString returns [string result]
371 : dk:DOTKEY { result = dk->getText(); }
372 | s:STRING { result = s->getText(); }
373 ;
374
375// syntactic cavity generators
376fluff
377 : SEMI
378 ;
379
380eql
381 : EQL
516ae477 382 | EQQL
7d31e928
A
383 | empty
384 ;
385
386empty : ;
387
388
389//
390// The lexer for the Requirement language.
391// Really straightforward and conventional.
516ae477
A
392// A subset of strings don't need to be quoted (DOTKEYs). Neither do some simple
393// pathnames starting with "/".
394// Hash values have a special syntax H"abcd" (abcd in straight hex).
395// Hex constants of the form 0xabcd can have any length; they are carried
396// around as strings (which are in turn stored as data in the language binary).
7d31e928
A
397//
398class RequirementLexer extends Lexer;
399
400options {
401 k=2;
402 testLiterals=false;
403}
404
405protected
406IDENT options { testLiterals=true; }
407 : ( 'A' .. 'Z' | 'a' .. 'z' ) ( 'A' .. 'Z' | 'a' .. 'z' | '0' .. '9' )*
408 ;
409
410DOTKEY options { testLiterals=true; }
516ae477 411 : IDENT ( "." ( IDENT | INTEGER ) )*
7d31e928
A
412 ;
413
414PATHNAME
415 : "/" IDENT ( "/" IDENT )+
416 ;
417
418HASHCONSTANT
419 : 'H'! '"'! ( HEX )+ '"'!
420 ;
421
422HEXCONSTANT
423 : '0'! 'x'! ( HEX )+
424 ;
425
426STRING
427 : '"'! ( ( '\\'! '"' ) | ( ~ ( '"' | '\\' ) ) )* '"'!
428 ;
429
430INTEGER
431 : ( '0' .. '9' ) +
432 ;
433
434protected
435HEX : '0' .. '9' | 'a' .. 'f' | 'A' .. 'F' ;
436
437// operator tokens
438ARROW : "=>" ;
439SEMI : ';' ;
440LPAREN : '(' ;
441RPAREN : ')' ;
442LBRACK : '[' ;
443RBRACK : ']' ;
516ae477
A
444LESS : '<' ;
445GT : '>' ;
446LE : "<=" ;
447GE : ">=" ;
7d31e928
A
448COMMA : ',' ;
449EQL : '=' ;
516ae477 450EQQL : "==" ;
7d31e928
A
451SUBS : '~' ;
452NEG : '-' ;
453NOT : '!' ;
516ae477 454STAR : '*' ;
7d31e928
A
455
456
457//
458// White spaces
459//
460WS : ( ' ' | '\n' { newline(); } | '\t' )+
461 { $setType(antlr::Token::SKIP); }
462 ;
463
464SHELLCOMMENT
465 : '#' ( ~ '\n' )*
466 { $setType(antlr::Token::SKIP); }
467 ;
468
469C_COMMENT
470 : "/*" ( (~'*')|('*'(~'/')) )* "*/"
471 { $setType(antlr::Token::SKIP); }
472 ;
473
474CPP_COMMENT
475 : "//" ( ~ '\n' )*
476 { $setType(antlr::Token::SKIP); }
477 ;