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