root/sepolgen/src/sepolgen/refparser.py

Revision a0af38a531788d2ffc4fd1c03c38fb66c3a61c17, 28.6 kB (checked in by Eric Paris <eparis@redhat.com>, 6 months ago)

sepolgen: Allow ~ as a file identifier

We already allow this in policy, so allow it in sepolgen as well.

Signed-off-by: Eric Paris <eparis@redhat.com>
Acked-by: Dan Walsh <dwalsh@redhat.com>

  • Property mode set to 100644
Line 
1 # Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
2 #
3 # Copyright (C) 2006-2007 Red Hat
4 # see file 'COPYING' for use and warranty information
5 #
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License as
8 # published by the Free Software Foundation; version 2 only
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 #
19
20 # OVERVIEW
21 #
22 #
23 # This is a parser for the refpolicy policy "language" - i.e., the
24 # normal SELinux policy language plus the refpolicy style M4 macro
25 # constructs on top of that base language. This parser is primarily
26 # aimed at parsing the policy headers in order to create an abstract
27 # policy representation suitable for generating policy.
28 #
29 # Both the lexer and parser are included in this file. The are implemented
30 # using the Ply library (included with sepolgen).
31
32 import sys
33 import os
34 import re
35 import traceback
36
37 import refpolicy
38 import access
39 import defaults
40
41 import lex
42 import yacc
43
44 # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
45 #
46 # lexer
47 #
48 # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
49
50 tokens = (
51     # basic tokens, punctuation
52     'TICK',
53     'SQUOTE',
54     'OBRACE',
55     'CBRACE',
56     'SEMI',
57     'COLON',
58     'OPAREN',
59     'CPAREN',
60     'COMMA',
61     'MINUS',
62     'TILDE',
63     'ASTERISK',
64     'AMP',
65     'BAR',
66     'EXPL',
67     'EQUAL',
68     'IDENTIFIER',
69     'NUMBER',
70     'PATH',
71     'IPV6_ADDR',
72     # reserved words
73     #   module
74     'MODULE',
75     'POLICY_MODULE',
76     'REQUIRE',
77     #   flask
78     'SID',
79     'GENFSCON',
80     'FS_USE_XATTR',
81     'FS_USE_TRANS',
82     'FS_USE_TASK',
83     'PORTCON',
84     'NODECON',
85     'NETIFCON',
86     'PIRQCON',
87     'IOMEMCON',
88     'IOPORTCON',
89     'PCIDEVICECON',
90     #   object classes
91     'CLASS',
92     #   types and attributes
93     'TYPEATTRIBUTE',
94     'TYPE',
95     'ATTRIBUTE',
96     'ALIAS',
97     'TYPEALIAS',
98     #   conditional policy
99     'BOOL',
100     'TRUE',
101     'FALSE',
102     'IF',
103     'ELSE',
104     #   users and roles
105     'ROLE',
106     'TYPES',
107     #   rules
108     'ALLOW',
109     'DONTAUDIT',
110     'AUDITALLOW',
111     'NEVERALLOW',
112     'PERMISSIVE',
113     'TYPE_TRANSITION',
114     'TYPE_CHANGE',
115     'TYPE_MEMBER',
116     'RANGE_TRANSITION',
117     'ROLE_TRANSITION',
118     #   refpolicy keywords
119     'OPT_POLICY',
120     'INTERFACE',
121     'TUNABLE_POLICY',
122     'GEN_REQ',
123     'TEMPLATE',
124     'GEN_CONTEXT',
125     #   m4
126     'IFELSE',
127     'IFDEF',
128     'IFNDEF',
129     'DEFINE'
130     )
131
132 # All reserved keywords - see t_IDENTIFIER for how these are matched in
133 # the lexer.
134 reserved = {
135     # module
136     'module' : 'MODULE',
137     'policy_module' : 'POLICY_MODULE',
138     'require' : 'REQUIRE',
139     # flask
140     'sid' : 'SID',
141     'genfscon' : 'GENFSCON',
142     'fs_use_xattr' : 'FS_USE_XATTR',
143     'fs_use_trans' : 'FS_USE_TRANS',
144     'fs_use_task' : 'FS_USE_TASK',
145     'portcon' : 'PORTCON',
146     'nodecon' : 'NODECON',
147     'netifcon' : 'NETIFCON',
148     'pirqcon' : 'PIRQCON',
149     'iomemcon' : 'IOMEMCON',
150     'ioportcon' : 'IOPORTCON',
151     'pcidevicecon' : 'PCIDEVICECON',
152     # object classes
153     'class' : 'CLASS',
154     # types and attributes
155     'typeattribute' : 'TYPEATTRIBUTE',
156     'type' : 'TYPE',
157     'attribute' : 'ATTRIBUTE',
158     'alias' : 'ALIAS',
159     'typealias' : 'TYPEALIAS',
160     # conditional policy
161     'bool' : 'BOOL',
162     'true' : 'TRUE',
163     'false' : 'FALSE',
164     'if' : 'IF',
165     'else' : 'ELSE',
166     # users and roles
167     'role' : 'ROLE',
168     'types' : 'TYPES',
169     # rules
170     'allow' : 'ALLOW',
171     'dontaudit' : 'DONTAUDIT',
172     'auditallow' : 'AUDITALLOW',
173     'neverallow' : 'NEVERALLOW',
174     'permissive' : 'PERMISSIVE',
175     'type_transition' : 'TYPE_TRANSITION',
176     'type_change' : 'TYPE_CHANGE',
177     'type_member' : 'TYPE_MEMBER',
178     'range_transition' : 'RANGE_TRANSITION',
179     'role_transition' : 'ROLE_TRANSITION',
180     # refpolicy keywords
181     'optional_policy' : 'OPT_POLICY',
182     'interface' : 'INTERFACE',
183     'tunable_policy' : 'TUNABLE_POLICY',
184     'gen_require' : 'GEN_REQ',
185     'template' : 'TEMPLATE',
186     'gen_context' : 'GEN_CONTEXT',
187     # M4
188     'ifelse' : 'IFELSE',
189     'ifndef' : 'IFNDEF',
190     'ifdef' : 'IFDEF',
191     'define' : 'DEFINE'
192     }
193
194 # The ply lexer allows definition of tokens in 2 ways: regular expressions
195 # or functions.
196
197 # Simple regex tokens
198 t_TICK      = r'\`'
199 t_SQUOTE    = r'\''
200 t_OBRACE    = r'\{'
201 t_CBRACE    = r'\}'
202 # This will handle spurios extra ';' via the +
203 t_SEMI      = r'\;+'
204 t_COLON     = r'\:'
205 t_OPAREN    = r'\('
206 t_CPAREN    = r'\)'
207 t_COMMA     = r'\,'
208 t_MINUS     = r'\-'
209 t_TILDE     = r'\~'
210 t_ASTERISK  = r'\*'
211 t_AMP       = r'\&'
212 t_BAR       = r'\|'
213 t_EXPL      = r'\!'
214 t_EQUAL     = r'\='
215 t_NUMBER    = r'[0-9\.]+'
216 t_PATH      = r'/[a-zA-Z0-9)_\.\*/]*'
217 #t_IPV6_ADDR = r'[a-fA-F0-9]{0,4}:[a-fA-F0-9]{0,4}:([a-fA-F0-9]{0,4}:)*'
218
219 # Ignore whitespace - this is a special token for ply that more efficiently
220 # ignores uninteresting tokens.
221 t_ignore    = " \t"
222
223 # More complex tokens
224 def t_IPV6_ADDR(t):
225     r'[a-fA-F0-9]{0,4}:[a-fA-F0-9]{0,4}:([a-fA-F0-9]|:)*'
226     # This is a function simply to force it sooner into
227     # the regex list
228     return t
229
230 def t_m4comment(t):
231     r'dnl.*\n'
232     # Ignore all comments
233     t.lexer.lineno += 1
234
235 def t_refpolicywarn1(t):
236     r'define.*refpolicywarn\(.*\n'
237     # Ignore refpolicywarn statements - they sometimes
238     # contain text that we can't parse.
239     t.skip(1)
240
241 def t_refpolicywarn(t):
242     r'refpolicywarn\(.*\n'
243     # Ignore refpolicywarn statements - they sometimes
244     # contain text that we can't parse.
245     t.lexer.lineno += 1
246
247 def t_IDENTIFIER(t):
248     r'[a-zA-Z_\$\"][a-zA-Z0-9_\-\.\$\*\"~]*'
249     # Handle any keywords
250     t.type = reserved.get(t.value,'IDENTIFIER')
251     return t
252
253 def t_comment(t):
254     r'\#.*\n'
255     # Ignore all comments
256     t.lexer.lineno += 1
257
258 def t_error(t):
259     print "Illegal character '%s'" % t.value[0]
260     t.skip(1)
261
262 def t_newline(t):
263     r'\n+'
264     t.lexer.lineno += len(t.value)
265
266 # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
267 #
268 # Parser
269 #
270 # :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
271
272 # Global data used during parsing - making it global is easier than
273 # passing the state through the parsing functions.
274
275 #   m is the top-level data structure (stands for modules).
276 m = None
277 #   error is either None (indicating no error) or a string error message.
278 error = None
279 parse_file = ""
280 #   spt is the support macros (e.g., obj/perm sets) - it is an instance of
281 #     refpolicy.SupportMacros and should always be present during parsing
282 #     though it may not contain any macros.
283 spt = None
284 success = True
285
286 # utilities
287 def collect(stmts, parent, val=None):
288     if stmts is None:
289         return
290     for s in stmts:
291         if s is None:
292             continue
293         s.parent = parent
294         if val is not None:
295             parent.children.insert(0, (val, s))
296         else:
297             parent.children.insert(0, s)
298
299 def expand(ids, s):
300     for id in ids:
301         if spt.has_key(id):
302             s.update(spt.by_name(id))
303         else:
304             s.add(id)
305
306 # Top-level non-terminal
307 def p_statements(p):
308     '''statements : statement
309                   | statements statement
310                   | empty
311     '''
312     if len(p) == 2 and p[1]:
313         m.children.append(p[1])
314     elif len(p) > 2 and p[2]:
315         m.children.append(p[2])
316
317 def p_statement(p):
318     '''statement : interface
319                  | template
320                  | obj_perm_set
321                  | policy
322                  | policy_module_stmt
323                  | module_stmt
324     '''
325     p[0] = p[1]
326
327 def p_empty(p):
328     'empty :'
329     pass
330
331 #
332 # Reference policy language constructs
333 #
334
335 # This is for the policy module statement (e.g., policy_module(foo,1.2.0)).
336 # We have a separate terminal for either the basic language module statement
337 # and interface calls to make it easier to identifier.
338 def p_policy_module_stmt(p):
339     'policy_module_stmt : POLICY_MODULE OPAREN IDENTIFIER COMMA NUMBER CPAREN'
340     m = refpolicy.ModuleDeclaration()
341     m.name = p[3]
342     m.version = p[5]
343     m.refpolicy = True
344     p[0] = m
345
346 def p_interface(p):
347     '''interface : INTERFACE OPAREN TICK IDENTIFIER SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN
348     '''
349     x = refpolicy.Interface(p[4])
350     collect(p[8], x)
351     p[0] = x
352
353 def p_template(p):
354     '''template : TEMPLATE OPAREN TICK IDENTIFIER SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN
355                 | DEFINE OPAREN TICK IDENTIFIER SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN
356     '''
357     x = refpolicy.Template(p[4])
358     collect(p[8], x)
359     p[0] = x
360
361 def p_define(p):
362     '''define : DEFINE OPAREN TICK IDENTIFIER SQUOTE CPAREN'''
363     # This is for defining single M4 values (to be used later in ifdef statements).
364     # Example: define(`sulogin_no_pam'). We don't currently do anything with these
365     # but we should in the future when we correctly resolve ifdef statements.
366     p[0] = None
367
368 def p_interface_stmts(p):
369     '''interface_stmts : policy
370                        | interface_stmts policy
371                        | empty
372     '''
373     if len(p) == 2 and p[1]:
374         p[0] = p[1]
375     elif len(p) > 2:
376         if not p[1]:
377             if p[2]:
378                 p[0] = p[2]
379         elif not p[2]:
380             p[0] = p[1]
381         else:
382             p[0] = p[1] + p[2]
383
384 def p_optional_policy(p):
385     '''optional_policy : OPT_POLICY OPAREN TICK interface_stmts SQUOTE CPAREN
386                        | OPT_POLICY OPAREN TICK interface_stmts SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN
387     '''
388     o = refpolicy.OptionalPolicy()
389     collect(p[4], o, val=True)
390     if len(p) > 7:
391         collect(p[8], o, val=False)
392     p[0] = [o]
393
394 def p_tunable_policy(p):
395     '''tunable_policy : TUNABLE_POLICY OPAREN TICK cond_expr SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN
396                       | TUNABLE_POLICY OPAREN TICK cond_expr SQUOTE COMMA TICK interface_stmts SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN
397     '''
398     x = refpolicy.TunablePolicy()
399     x.cond_expr = p[4]
400     collect(p[8], x, val=True)
401     if len(p) > 11:
402         collect(p[12], x, val=False)
403     p[0] = [x]
404
405 def p_ifelse(p):
406     '''ifelse : IFELSE OPAREN TICK IDENTIFIER SQUOTE COMMA COMMA TICK IDENTIFIER SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN optional_semi
407               | IFELSE OPAREN TICK IDENTIFIER SQUOTE COMMA TICK IDENTIFIER SQUOTE COMMA TICK interface_stmts SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN optional_semi
408     '''
409 #    x = refpolicy.IfDef(p[4])
410 #    v = True
411 #    collect(p[8], x, val=v)
412 #    if len(p) > 12:
413 #        collect(p[12], x, val=False)
414 #    p[0] = [x]
415     pass
416
417
418 def p_ifdef(p):
419     '''ifdef : IFDEF OPAREN TICK IDENTIFIER SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN optional_semi
420              | IFNDEF OPAREN TICK IDENTIFIER SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN optional_semi
421              | IFDEF OPAREN TICK IDENTIFIER SQUOTE COMMA TICK interface_stmts SQUOTE COMMA TICK interface_stmts SQUOTE CPAREN optional_semi
422     '''
423     x = refpolicy.IfDef(p[4])
424     if p[1] == 'ifdef':
425         v = True
426     else:
427         v = False
428     collect(p[8], x, val=v)
429     if len(p) > 12:
430         collect(p[12], x, val=False)
431     p[0] = [x]
432
433 def p_interface_call(p):
434     '''interface_call : IDENTIFIER OPAREN interface_call_param_list CPAREN
435                       | IDENTIFIER OPAREN CPAREN
436                       | IDENTIFIER OPAREN interface_call_param_list CPAREN SEMI'''
437     # Allow spurious semi-colons at the end of interface calls
438     i = refpolicy.InterfaceCall(ifname=p[1])
439     if len(p) > 4:
440         i.args.extend(p[3])
441     p[0] = i
442
443 def p_interface_call_param(p):
444     '''interface_call_param : IDENTIFIER
445                             | IDENTIFIER MINUS IDENTIFIER
446                             | nested_id_set
447                             | TRUE
448                             | FALSE
449     '''
450     # Intentionally let single identifiers pass through
451     # List means set, non-list identifier
452     if len(p) == 2:
453         p[0] = p[1]
454     else:
455         p[0] = [p[1], "-" + p[3]]
456
457 def p_interface_call_param_list(p):
458     '''interface_call_param_list : interface_call_param
459                                  | interface_call_param_list COMMA interface_call_param
460     '''
461     if len(p) == 2:
462         p[0] = [p[1]]
463     else:
464         p[0] = p[1] + [p[3]]
465
466
467 def p_obj_perm_set(p):
468     'obj_perm_set : DEFINE OPAREN TICK IDENTIFIER SQUOTE COMMA TICK names SQUOTE CPAREN'
469     s = refpolicy.ObjPermSet(p[4])
470     s.perms = p[8]
471     p[0] = s
472    
473 #
474 # Basic SELinux policy language
475 #
476
477 def p_policy(p):
478     '''policy : policy_stmt
479               | optional_policy
480               | tunable_policy
481               | ifdef
482               | ifelse
483               | conditional
484     '''
485     p[0] = p[1]
486
487 def p_policy_stmt(p):
488     '''policy_stmt : gen_require
489                    | avrule_def
490                    | typerule_def
491                    | typeattribute_def
492                    | interface_call
493                    | role_def
494                    | role_allow
495                    | permissive
496                    | type_def
497                    | typealias_def
498                    | attribute_def
499                    | range_transition_def
500                    | role_transition_def
501                    | bool
502                    | define
503                    | initial_sid
504                    | genfscon
505                    | fs_use
506                    | portcon
507                    | nodecon
508                    | netifcon
509                    | pirqcon
510                    | iomemcon
511                    | ioportcon
512                    | pcidevicecon
513     '''
514     if p[1]:
515         p[0] = [p[1]]
516
517 def p_module_stmt(p):
518     'module_stmt : MODULE IDENTIFIER NUMBER SEMI'
519     m = refpolicy.ModuleDeclaration()
520     m.name = p[2]
521     m.version = p[3]
522     m.refpolicy = False
523     p[0] = m
524
525 def p_gen_require(p):
526     '''gen_require : GEN_REQ OPAREN TICK requires SQUOTE CPAREN
527                    | REQUIRE OBRACE requires CBRACE'''
528     # We ignore the require statements - they are redundant data from our point-of-view.
529     # Checkmodule will verify them later anyway so we just assume that they match what
530     # is in the rest of the interface.
531     pass
532
533 def p_requires(p):
534     '''requires : require
535                 | requires require
536                 | ifdef
537                 | requires ifdef
538     '''
539     pass
540
541 def p_require(p):
542     '''require : TYPE comma_list SEMI
543                | ROLE comma_list SEMI
544                | ATTRIBUTE comma_list SEMI
545                | CLASS comma_list SEMI
546                | BOOL comma_list SEMI
547     '''
548     pass
549
550 def p_security_context(p):
551     '''security_context : IDENTIFIER COLON IDENTIFIER COLON IDENTIFIER
552                         | IDENTIFIER COLON IDENTIFIER COLON IDENTIFIER COLON mls_range_def'''
553     # This will likely need some updates to handle complex levels
554     s = refpolicy.SecurityContext()
555     s.user = p[1]
556     s.role = p[3]
557     s.type = p[5]
558     if len(p) > 6:
559         s.level = p[7]
560
561     p[0] = s
562
563 def p_gen_context(p):
564     '''gen_context : GEN_CONTEXT OPAREN security_context COMMA mls_range_def CPAREN
565     '''
566     # We actually store gen_context statements in a SecurityContext
567     # object - it knows how to output either a bare context or a
568     # gen_context statement.
569     s = p[3]
570     s.level = p[5]
571    
572     p[0] = s
573
574 def p_context(p):
575     '''context : security_context
576                | gen_context
577     '''
578     p[0] = p[1]
579
580 def p_initial_sid(p):
581     '''initial_sid : SID IDENTIFIER context'''
582     s = refpolicy.InitialSid()
583     s.name = p[2]
584     s.context = p[3]
585     p[0] = s
586
587 def p_genfscon(p):
588     '''genfscon : GENFSCON IDENTIFIER PATH context'''
589    
590     g = refpolicy.GenfsCon()
591     g.filesystem = p[2]
592     g.path = p[3]
593     g.context = p[4]
594
595     p[0] = g
596
597 def p_fs_use(p):
598     '''fs_use : FS_USE_XATTR IDENTIFIER context SEMI
599               | FS_USE_TASK IDENTIFIER context SEMI
600               | FS_USE_TRANS IDENTIFIER context SEMI
601     '''
602     f = refpolicy.FilesystemUse()
603     if p[1] == "fs_use_xattr":
604         f.type = refpolicy.FilesystemUse.XATTR
605     elif p[1] == "fs_use_task":
606         f.type = refpolicy.FilesystemUse.TASK
607     elif p[1] == "fs_use_trans":
608         f.type = refpolicy.FilesystemUse.TRANS
609
610     f.filesystem = p[2]
611     f.context = p[3]
612
613     p[0] = f
614
615 def p_portcon(p):
616     '''portcon : PORTCON IDENTIFIER NUMBER context
617                | PORTCON IDENTIFIER NUMBER MINUS NUMBER context'''
618     c = refpolicy.PortCon()
619     c.port_type = p[2]
620     if len(p) == 5:
621         c.port_number = p[3]
622         c.context = p[4]
623     else:
624         c.port_number = p[3] + "-" + p[4]
625         c.context = p[5]
626
627     p[0] = c
628
629 def p_nodecon(p):
630     '''nodecon : NODECON NUMBER NUMBER context
631                | NODECON IPV6_ADDR IPV6_ADDR context
632     '''
633     n = refpolicy.NodeCon()
634     n.start = p[2]
635     n.end = p[3]
636     n.context = p[4]
637
638     p[0] = n
639
640 def p_netifcon(p):
641     'netifcon : NETIFCON IDENTIFIER context context'
642     n = refpolicy.NetifCon()
643     n.interface = p[2]
644     n.interface_context = p[3]
645     n.packet_context = p[4]
646
647     p[0] = n
648
649 def p_pirqcon(p):
650     'pirqcon : PIRQCON NUMBER context'
651     c = refpolicy.PirqCon()
652     c.pirq_number = p[2]
653     c.context = p[3]
654
655     p[0] = c
656
657 def p_iomemcon(p):
658     '''iomemcon : IOMEMCON NUMBER context
659                 | IOMEMCON NUMBER MINUS NUMBER context'''
660     c = refpolicy.IomemCon()
661     if len(p) == 4:
662         c.device_mem = p[2]
663         c.context = p[3]
664     else:
665         c.device_mem = p[2] + "-" + p[3]
666         c.context = p[4]
667
668     p[0] = c
669
670 def p_ioportcon(p):
671     '''ioportcon : IOPORTCON NUMBER context
672                 | IOPORTCON NUMBER MINUS NUMBER context'''
673     c = refpolicy.IoportCon()
674     if len(p) == 4:
675         c.ioport = p[2]
676         c.context = p[3]
677     else:
678         c.ioport = p[2] + "-" + p[3]
679         c.context = p[4]
680
681     p[0] = c
682
683 def p_pcidevicecon(p):
684     'pcidevicecon : PCIDEVICECON NUMBER context'
685     c = refpolicy.PciDeviceCon()
686     c.device = p[2]
687     c.context = p[3]
688
689     p[0] = c
690
691 def p_mls_range_def(p):
692     '''mls_range_def : mls_level_def MINUS mls_level_def
693                      | mls_level_def
694     '''
695     p[0] = p[1]
696     if len(p) > 2:
697         p[0] = p[0] + "-" + p[3]
698
699 def p_mls_level_def(p):
700     '''mls_level_def : IDENTIFIER COLON comma_list
701                      | IDENTIFIER
702     '''
703     p[0] = p[1]
704     if len(p) > 2:
705         p[0] = p[0] + ":" + ",".join(p[3])
706    
707 def p_type_def(p):
708     '''type_def : TYPE IDENTIFIER COMMA comma_list SEMI
709                 | TYPE IDENTIFIER SEMI
710                 | TYPE IDENTIFIER ALIAS names SEMI
711                 | TYPE IDENTIFIER ALIAS names COMMA comma_list SEMI
712     '''
713     t = refpolicy.Type(p[2])
714     if len(p) == 6:
715         if p[3] == ',':
716             t.attributes.update(p[4])
717         else:
718             t.aliases = p[4]
719     elif len(p) > 4:
720         t.aliases = p[4]
721         if len(p) == 8:
722             t.attributes.update(p[6])
723     p[0] = t
724
725 def p_attribute_def(p):
726     'attribute_def : ATTRIBUTE IDENTIFIER SEMI'
727     a = refpolicy.Attribute(p[2])
728     p[0] = a
729
730 def p_typealias_def(p):
731     'typealias_def : TYPEALIAS IDENTIFIER ALIAS names SEMI'
732     t = refpolicy.TypeAlias()
733     t.type = p[2]
734     t.aliases = p[4]
735     p[0] = t
736
737 def p_role_def(p):
738     '''role_def : ROLE IDENTIFIER TYPES comma_list SEMI
739                 | ROLE IDENTIFIER SEMI'''
740     r = refpolicy.Role()
741     r.role = p[2]
742     if len(p) > 4:
743         r.types.update(p[4])
744     p[0] = r
745
746 def p_role_allow(p):
747     'role_allow : ALLOW names names SEMI'
748     r = refpolicy.RoleAllow()
749     r.src_roles = p[2]
750     r.tgt_roles = p[3]
751     p[0] = r
752
753 def p_permissive(p):
754     'permissive : PERMISSIVE names SEMI'
755     t.skip(1)
756
757 def p_avrule_def(p):
758     '''avrule_def : ALLOW names names COLON names names SEMI
759                   | DONTAUDIT names names COLON names names SEMI
760                   | AUDITALLOW names names COLON names names SEMI
761                   | NEVERALLOW names names COLON names names SEMI
762     '''
763     a = refpolicy.AVRule()
764     if p[1] == 'dontaudit':
765         a.rule_type = refpolicy.AVRule.DONTAUDIT
766     elif p[1] == 'auditallow':
767         a.rule_type = refpolicy.AVRule.AUDITALLOW
768     elif p[1] == 'neverallow':
769         a.rule_type = refpolicy.AVRule.NEVERALLOW
770     a.src_types = p[2]
771     a.tgt_types = p[3]
772     a.obj_classes = p[5]
773     a.perms = p[6]
774     p[0] = a
775
776 def p_typerule_def(p):
777     '''typerule_def : TYPE_TRANSITION names names COLON names IDENTIFIER SEMI
778                     | TYPE_TRANSITION names names COLON names IDENTIFIER IDENTIFIER SEMI
779                     | TYPE_CHANGE names names COLON names IDENTIFIER SEMI
780                     | TYPE_MEMBER names names COLON names IDENTIFIER SEMI
781     '''
782     t = refpolicy.TypeRule()
783     if p[1] == 'type_change':
784         t.rule_type = refpolicy.TypeRule.TYPE_CHANGE
785     elif p[1] == 'type_member':
786         t.rule_type = refpolicy.TypeRule.TYPE_MEMBER
787     t.src_types = p[2]
788     t.tgt_types = p[3]
789     t.obj_classes = p[5]
790     t.dest_type = p[6]
791     p[0] = t
792
793 def p_bool(p):
794     '''bool : BOOL IDENTIFIER TRUE SEMI
795             | BOOL IDENTIFIER FALSE SEMI'''
796     b = refpolicy.Bool()
797     b.name = p[2]
798     if p[3] == "true":
799         b.state = True
800     else:
801         b.state = False
802     p[0] = b
803
804 def p_conditional(p):
805     ''' conditional : IF OPAREN cond_expr CPAREN OBRACE interface_stmts CBRACE
806                     | IF OPAREN cond_expr CPAREN OBRACE interface_stmts CBRACE ELSE OBRACE interface_stmts CBRACE
807     '''
808     c = refpolicy.Conditional()
809     c.cond_expr = p[3]
810     collect(p[6], c, val=True)
811     if len(p) > 8:
812         collect(p[10], c, val=False)
813     p[0] = [c]
814
815 def p_typeattribute_def(p):
816     '''typeattribute_def : TYPEATTRIBUTE IDENTIFIER comma_list SEMI'''
817     t = refpolicy.TypeAttribute()
818     t.type = p[2]
819     t.attributes.update(p[3])
820     p[0] = t
821
822 def p_range_transition_def(p):
823     '''range_transition_def : RANGE_TRANSITION names names COLON names mls_range_def SEMI
824                             | RANGE_TRANSITION names names names SEMI'''
825     pass
826
827 def p_role_transition_def(p):
828     '''role_transition_def : ROLE_TRANSITION names names names SEMI'''
829     pass
830
831 def p_cond_expr(p):
832     '''cond_expr : IDENTIFIER
833                  | EXPL cond_expr
834                  | cond_expr AMP AMP cond_expr
835                  | cond_expr BAR BAR cond_expr
836                  | cond_expr EQUAL EQUAL cond_expr
837                  | cond_expr EXPL EQUAL cond_expr
838     '''
839     l = len(p)
840     if l == 2:
841         p[0] = [p[1]]
842     elif l == 3:
843         p[0] = [p[1]] + p[2]
844     else:
845         p[0] = p[1] + [p[2] + p[3]] + p[4]
846
847
848 #
849 # Basic terminals
850 #
851
852 # Identifiers and lists of identifiers. These must
853 # be handled somewhat gracefully. Names returns an IdSet and care must
854 # be taken that this is _assigned_ to an object to correctly update
855 # all of the flags (as opposed to using update). The other terminals
856 # return list - this is to preserve ordering if it is important for
857 # parsing (for example, interface_call must retain the ordering). Other
858 # times the list should be used to update an IdSet.
859
860 def p_names(p):
861     '''names : identifier
862              | nested_id_set
863              | asterisk
864              | TILDE identifier
865              | TILDE nested_id_set
866              | IDENTIFIER MINUS IDENTIFIER
867     '''
868     s = refpolicy.IdSet()
869     if len(p) < 3:
870         expand(p[1], s)
871     elif len(p) == 3:
872         expand(p[2], s)
873         s.compliment = True
874     else:
875         expand([p[1]])
876         s.add("-" + p[3])
877     p[0] = s
878
879 def p_identifier(p):
880     'identifier : IDENTIFIER'
881     p[0] = [p[1]]
882
883 def p_asterisk(p):
884     'asterisk : ASTERISK'
885     p[0] = [p[1]]
886
887 def p_nested_id_set(p):
888     '''nested_id_set : OBRACE nested_id_list CBRACE
889     '''
890     p[0] = p[2]
891
892 def p_nested_id_list(p):
893     '''nested_id_list : nested_id_element
894                       | nested_id_list nested_id_element
895     '''
896     if len(p) == 2:
897         p[0] = p[1]
898     else:
899         p[0] = p[1] + p[2]
900
901 def p_nested_id_element(p):
902     '''nested_id_element : identifier
903                          | MINUS IDENTIFIER
904                          | nested_id_set
905     '''
906     if len(p) == 2:
907         p[0] = p[1]
908     else:
909         # For now just leave the '-'
910         str = "-" + p[2]
911         p[0] = [str]
912
913 def p_comma_list(p):
914     '''comma_list : nested_id_list
915                   | comma_list COMMA nested_id_list
916     '''
917     if len(p) > 2:
918         p[1] = p[1] + p[3]
919     p[0] = p[1]
920
921 def p_optional_semi(p):
922     '''optional_semi : SEMI
923                    | empty'''
924     pass
925
926
927 #
928 # Interface to the parser
929 #
930
931 def p_error(tok):
932     global error, parse_file, success, parser
933     error = "%s: Syntax error on line %d %s [type=%s]" % (parse_file, tok.lineno, tok.value, tok.type)
934     print error
935     success = False
936
937 def prep_spt(spt):
938     if not spt:
939         return { }
940     map = {}
941     for x in spt:
942         map[x.name] = x
943
944 parser = None
945 lexer = None
946 def create_globals(module, support, debug):
947     global parser, lexer, m, spt
948
949     if not parser:
950         lexer = lex.lex()
951         parser = yacc.yacc(method="LALR", debug=debug, write_tables=0)
952
953     if module is not None:
954         m = module
955     else:
956         m = refpolicy.Module()
957
958     if not support:
959         spt = refpolicy.SupportMacros()
960     else:
961         spt = support
962
963 def parse(text, module=None, support=None, debug=False):
964     create_globals(module, support, debug)
965     global error, parser, lexer, success
966
967     success = True
968
969     try:
970         parser.parse(text, debug=debug, lexer=lexer)
971     except Exception, e:
972         parser = None
973         lexer = None
974         error = "internal parser error: %s" % str(e) + "\n" + traceback.format_exc()
975
976     if not success:
977         # force the parser and lexer to be rebuilt - we have some problems otherwise
978         parser = None
979         msg = 'could not parse text: "%s"' % error
980         raise ValueError(msg)
981     return m
982
983 def list_headers(root):
984     modules = []
985     support_macros = None
986
987     for dirpath, dirnames, filenames in os.walk(root):
988         for name in filenames:
989             modname = os.path.splitext(name)
990             filename = os.path.join(dirpath, name)
991
992             if modname[1] == '.spt':
993                 if name == "obj_perm_sets.spt":
994                     support_macros = filename
995                 elif len(re.findall("patterns", modname[0])):
996                          modules.append((modname[0], filename))
997             elif modname[1] == '.if':
998                 modules.append((modname[0], filename))
999
1000     return (modules, support_macros)
1001
1002
1003 def parse_headers(root, output=None, expand=True, debug=False):
1004     import util
1005
1006     headers = refpolicy.Headers()
1007
1008     modules = []
1009     support_macros = None
1010
1011     if os.path.isfile(root):
1012         name = os.path.split(root)[1]
1013         if name == '':
1014             raise ValueError("Invalid file name %s" % root)
1015         modname = os.path.splitext(name)
1016         modules.append((modname[0], root))
1017         all_modules, support_macros = list_headers(defaults.headers())
1018     else:
1019         modules, support_macros = list_headers(root)
1020
1021     if expand and not support_macros:
1022         raise ValueError("could not find support macros (obj_perm_sets.spt)")
1023
1024     def o(msg):
1025         if output:
1026             output.write(msg)
1027
1028     def parse_file(f, module, spt=None):
1029         global parse_file
1030         if debug:
1031             o("parsing file %s\n" % f)
1032         try:
1033             fd = open(f)
1034             txt = fd.read()
1035             fd.close()
1036             parse_file = f
1037             parse(txt, module, spt, debug)
1038         except IOError, e:
1039             return
1040         except ValueError, e:
1041             raise ValueError("error parsing file %s: %s" % (f, str(e)))
1042
1043     spt = None
1044     if support_macros:
1045         o("Parsing support macros (%s): " % support_macros)
1046         spt = refpolicy.SupportMacros()
1047         parse_file(support_macros, spt)
1048
1049         headers.children.append(spt)
1050
1051         # FIXME: Total hack - add in can_exec rather than parse the insanity
1052         # of misc_macros. We are just going to pretend that this is an interface
1053         # to make the expansion work correctly.
1054         can_exec = refpolicy.Interface("can_exec")
1055         av = access.AccessVector(["$1","$2","file","execute_no_trans","open", "read",
1056                                   "getattr","lock","execute","ioctl"])
1057
1058         can_exec.children.append(refpolicy.AVRule(av))
1059         headers.children.append(can_exec)
1060
1061         o("done.\n")
1062
1063     if output and not debug:
1064         status = util.ConsoleProgressBar(sys.stdout, steps=len(modules))
1065         status.start("Parsing interface files")
1066
1067     failures = []
1068     for x in modules:
1069         m = refpolicy.Module()
1070         m.name = x[0]
1071         try:
1072             if expand:
1073                 parse_file(x[1], m, spt)
1074             else:
1075                 parse_file(x[1], m)
1076         except ValueError, e:
1077             o(str(e) + "\n")
1078             failures.append(x[1])
1079             continue
1080
1081         headers.children.append(m)
1082         if output and not debug:
1083             status.step()
1084
1085     if len(failures):
1086         o("failed to parse some headers: %s" % ", ".join(failures))
1087
1088     return headers
Note: See TracBrowser for help on using the browser.