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