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 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 '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. 138reserved = { 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 206t_TICK = r'\`' 207t_SQUOTE = r'\'' 208t_OBRACE = r'\{' 209t_CBRACE = r'\}' 210# This will handle spurious extra ';' via the + 211t_SEMI = r'\;+' 212t_COLON = r'\:' 213t_OPAREN = r'\(' 214t_CPAREN = r'\)' 215t_COMMA = r'\,' 216t_MINUS = r'\-' 217t_TILDE = r'\~' 218t_ASTERISK = r'\*' 219t_AMP = r'\&' 220t_BAR = r'\|' 221t_EXPL = r'\!' 222t_EQUAL = r'\=' 223t_NUMBER = r'[0-9\.]+' 224t_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. 229t_ignore = " \t" 230 231# More complex tokens 232def 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 238def t_m4comment(t): 239 r'dnl.*\n' 240 # Ignore all comments 241 t.lexer.lineno += 1 242 243def 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 249def 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 255def 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 261def 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 267def t_comment(t): 268 r'\#.*\n' 269 # Ignore all comments 270 t.lexer.lineno += 1 271 272def t_error(t): 273 print("Illegal character '%s'" % t.value[0]) 274 t.skip(1) 275 276def 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). 290m = None 291# error is either None (indicating no error) or a string error message. 292error = None 293parse_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. 297spt = None 298success = True 299 300# utilities 301def 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 313def expand(ids, s): 314 for id in ids: 315 if spt.has_key(id): # noqa 316 s.update(spt.by_name(id)) 317 else: 318 s.add(id) 319 320# Top-level non-terminal 321def 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 331def 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 341def 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. 352def 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 360def 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 367def 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 375def 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 382def 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 398def 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 408def 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 419def 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 433def 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 448def 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 458def 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 473def 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 483def 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 493def 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 503def 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 537def 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 545def 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 553def p_requires(p): 554 '''requires : require 555 | requires require 556 | ifdef 557 | requires ifdef 558 ''' 559 pass 560 561def 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 571def 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 584def 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 595def p_context(p): 596 '''context : security_context 597 | gen_context 598 ''' 599 p[0] = p[1] 600 601def 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 608def 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 618def 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 636def 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 650def 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 661def 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 670def 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 678def 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 691def 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 704def 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 712def 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 720def 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 728def 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 736def 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 754def p_attribute_def(p): 755 'attribute_def : ATTRIBUTE IDENTIFIER SEMI' 756 a = refpolicy.Attribute(p[2]) 757 p[0] = a 758 759def 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 764def 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 771def 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 780def 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 787def p_permissive(p): 788 'permissive : PERMISSIVE names SEMI' 789 pass 790 791def 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 810def 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 829def 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 836def 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 847def 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 858def 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 865def 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 872def 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 877def p_role_transition_def(p): 878 '''role_transition_def : ROLE_TRANSITION names names names SEMI''' 879 pass 880 881def 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 910def 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 929def p_identifier(p): 930 'identifier : IDENTIFIER' 931 p[0] = [p[1]] 932 933def p_asterisk(p): 934 'asterisk : ASTERISK' 935 p[0] = [p[1]] 936 937def p_nested_id_set(p): 938 '''nested_id_set : OBRACE nested_id_list CBRACE 939 ''' 940 p[0] = p[2] 941 942def 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 951def 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 963def 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 971def p_optional_semi(p): 972 '''optional_semi : SEMI 973 | empty''' 974 pass 975 976 977# 978# Interface to the parser 979# 980 981def 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 987def prep_spt(spt): 988 if not spt: 989 return { } 990 map = {} 991 for x in spt: 992 map[x.name] = x 993 994parser = None 995lexer = None 996def 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 1013def 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 1034def 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 1054def 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