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