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