1# Authors: Karl MacMillan <[email protected]> 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_compare_value(p): 422 '''ifelse_compare_value : TICK IDENTIFIER SQUOTE 423 | TICK TRUE SQUOTE 424 | TICK FALSE SQUOTE 425 | TICK SQUOTE 426 | empty 427 ''' 428 if len(p) == 4: 429 p[0] = p[2] 430 else: 431 p[0] = None 432 433def p_ifelse_section(p): 434 '''ifelse_section : TICK IDENTIFIER SQUOTE COMMA ifelse_compare_value COMMA TICK interface_stmts SQUOTE 435 ''' 436 x = refpolicy.IfElse(p[2]) 437 collect(p[8], x, val=True) 438 p[0] = [x] 439 440def p_ifelse_sections(p): 441 '''ifelse_sections : ifelse_sections COMMA ifelse_section 442 | ifelse_section 443 ''' 444 if len(p) == 4: 445 p[0] = p[1] + p[3] 446 else: 447 p[0] = p[1] 448 449def p_ifelse(p): 450 '''ifelse : IFELSE OPAREN ifelse_sections COMMA TICK interface_stmts SQUOTE CPAREN optional_semi 451 ''' 452 x = refpolicy.IfElse(p[3]) 453 collect(p[3], x, val=True) 454 collect(p[6], x, val=False) 455 p[0] = [x] 456 457def p_ifdef(p): 458 '''ifdef : IFDEF OPAREN TICK IDENTIFIER SQUOTE COMMA TICK statements SQUOTE CPAREN optional_semi 459 | IFNDEF OPAREN TICK IDENTIFIER SQUOTE COMMA TICK statements SQUOTE CPAREN optional_semi 460 | IFDEF OPAREN TICK IDENTIFIER SQUOTE COMMA TICK statements SQUOTE COMMA TICK statements SQUOTE CPAREN optional_semi 461 ''' 462 x = refpolicy.IfDef(p[4]) 463 if p[1] == 'ifdef': 464 v = True 465 else: 466 v = False 467 collect(p[8], x, val=v) 468 if len(p) > 12: 469 collect(p[12], x, val=False) 470 p[0] = [x] 471 472def p_interface_call(p): 473 '''interface_call : IDENTIFIER OPAREN interface_call_param_list CPAREN 474 | IDENTIFIER OPAREN CPAREN 475 | IDENTIFIER OPAREN interface_call_param_list CPAREN SEMI''' 476 # Allow spurious semi-colons at the end of interface calls 477 i = refpolicy.InterfaceCall(ifname=p[1]) 478 if len(p) > 4: 479 i.args.extend(p[3]) 480 p[0] = i 481 482def p_interface_call_param(p): 483 '''interface_call_param : IDENTIFIER 484 | IDENTIFIER MINUS IDENTIFIER 485 | MINUS IDENTIFIER 486 | nested_id_set 487 | TRUE 488 | FALSE 489 | FILENAME 490 ''' 491 # Intentionally let single identifiers pass through 492 # List means set, non-list identifier 493 if len(p) == 2: 494 p[0] = p[1] 495 elif len(p) == 3: 496 p[0] = "-" + p[2] 497 else: 498 p[0] = [p[1], "-" + p[3]] 499 500def p_interface_call_param_list(p): 501 '''interface_call_param_list : interface_call_param 502 | interface_call_param_list COMMA interface_call_param 503 ''' 504 if len(p) == 2: 505 p[0] = [p[1]] 506 else: 507 p[0] = p[1] + [p[3]] 508 509 510def p_obj_perm_set(p): 511 'obj_perm_set : DEFINE OPAREN TICK IDENTIFIER SQUOTE COMMA TICK names SQUOTE CPAREN' 512 s = refpolicy.ObjPermSet(p[4]) 513 s.perms = p[8] 514 p[0] = s 515 516# 517# Basic SELinux policy language 518# 519 520def p_policy(p): 521 '''policy : policy_stmt 522 | optional_policy 523 | tunable_policy 524 | ifdef 525 | ifelse 526 | conditional 527 ''' 528 p[0] = p[1] 529 530def p_policy_stmt(p): 531 '''policy_stmt : gen_require 532 | avrule_def 533 | typerule_def 534 | typebound_def 535 | typeattribute_def 536 | roleattribute_def 537 | interface_call 538 | role_def 539 | role_allow 540 | permissive 541 | type_def 542 | typealias_def 543 | attribute_def 544 | attribute_role_def 545 | range_transition_def 546 | role_transition_def 547 | bool 548 | gen_tunable 549 | define 550 | initial_sid 551 | genfscon 552 | fs_use 553 | portcon 554 | nodecon 555 | netifcon 556 | pirqcon 557 | iomemcon 558 | ioportcon 559 | pcidevicecon 560 | devicetreecon 561 ''' 562 if p[1]: 563 p[0] = [p[1]] 564 565def p_module_stmt(p): 566 'module_stmt : MODULE IDENTIFIER NUMBER SEMI' 567 m = refpolicy.ModuleDeclaration() 568 m.name = p[2] 569 m.version = p[3] 570 m.refpolicy = False 571 p[0] = m 572 573def p_gen_require(p): 574 '''gen_require : GEN_REQ OPAREN TICK requires SQUOTE CPAREN 575 | REQUIRE OBRACE requires CBRACE''' 576 # We ignore the require statements - they are redundant data from our point-of-view. 577 # Checkmodule will verify them later anyway so we just assume that they match what 578 # is in the rest of the interface. 579 pass 580 581def p_requires(p): 582 '''requires : require 583 | requires require 584 | ifdef 585 | requires ifdef 586 | ifelse 587 | requires ifelse 588 ''' 589 pass 590 591def p_require(p): 592 '''require : TYPE comma_list SEMI 593 | ROLE comma_list SEMI 594 | ATTRIBUTE comma_list SEMI 595 | ATTRIBUTE_ROLE comma_list SEMI 596 | CLASS comma_list SEMI 597 | BOOL comma_list SEMI 598 ''' 599 pass 600 601def p_security_context(p): 602 '''security_context : IDENTIFIER COLON IDENTIFIER COLON IDENTIFIER 603 | IDENTIFIER COLON IDENTIFIER COLON IDENTIFIER COLON mls_range_def''' 604 # This will likely need some updates to handle complex levels 605 s = refpolicy.SecurityContext() 606 s.user = p[1] 607 s.role = p[3] 608 s.type = p[5] 609 if len(p) > 6: 610 s.level = p[7] 611 612 p[0] = s 613 614def p_gen_context(p): 615 '''gen_context : GEN_CONTEXT OPAREN security_context COMMA mls_range_def CPAREN 616 ''' 617 # We actually store gen_context statements in a SecurityContext 618 # object - it knows how to output either a bare context or a 619 # gen_context statement. 620 s = p[3] 621 s.level = p[5] 622 623 p[0] = s 624 625def p_context(p): 626 '''context : security_context 627 | gen_context 628 ''' 629 p[0] = p[1] 630 631def p_initial_sid(p): 632 '''initial_sid : SID IDENTIFIER context''' 633 s = refpolicy.InitialSid() 634 s.name = p[2] 635 s.context = p[3] 636 p[0] = s 637 638def p_genfscon(p): 639 '''genfscon : GENFSCON IDENTIFIER PATH context 640 | GENFSCON IDENTIFIER PATH MINUS IDENTIFIER context 641 | GENFSCON IDENTIFIER PATH MINUS MINUS context 642 ''' 643 g = refpolicy.GenfsCon() 644 g.filesystem = p[2] 645 g.path = p[3] 646 if len(p) == 5: 647 g.context = p[4] 648 else: 649 g.context = p[6] 650 651 p[0] = g 652 653def p_fs_use(p): 654 '''fs_use : FS_USE_XATTR IDENTIFIER context SEMI 655 | FS_USE_TASK IDENTIFIER context SEMI 656 | FS_USE_TRANS IDENTIFIER context SEMI 657 ''' 658 f = refpolicy.FilesystemUse() 659 if p[1] == "fs_use_xattr": 660 f.type = refpolicy.FilesystemUse.XATTR 661 elif p[1] == "fs_use_task": 662 f.type = refpolicy.FilesystemUse.TASK 663 elif p[1] == "fs_use_trans": 664 f.type = refpolicy.FilesystemUse.TRANS 665 666 f.filesystem = p[2] 667 f.context = p[3] 668 669 p[0] = f 670 671def p_portcon(p): 672 '''portcon : PORTCON IDENTIFIER NUMBER context 673 | PORTCON IDENTIFIER NUMBER MINUS NUMBER context''' 674 c = refpolicy.PortCon() 675 c.port_type = p[2] 676 if len(p) == 5: 677 c.port_number = p[3] 678 c.context = p[4] 679 else: 680 c.port_number = p[3] + "-" + p[4] 681 c.context = p[5] 682 683 p[0] = c 684 685def p_nodecon(p): 686 '''nodecon : NODECON NUMBER NUMBER context 687 | NODECON IPV6_ADDR IPV6_ADDR context 688 ''' 689 n = refpolicy.NodeCon() 690 n.start = p[2] 691 n.end = p[3] 692 n.context = p[4] 693 694 p[0] = n 695 696def p_netifcon(p): 697 'netifcon : NETIFCON IDENTIFIER context context' 698 n = refpolicy.NetifCon() 699 n.interface = p[2] 700 n.interface_context = p[3] 701 n.packet_context = p[4] 702 703 p[0] = n 704 705def p_pirqcon(p): 706 'pirqcon : PIRQCON NUMBER context' 707 c = refpolicy.PirqCon() 708 c.pirq_number = p[2] 709 c.context = p[3] 710 711 p[0] = c 712 713def p_iomemcon(p): 714 '''iomemcon : IOMEMCON NUMBER context 715 | IOMEMCON NUMBER MINUS NUMBER context''' 716 c = refpolicy.IomemCon() 717 if len(p) == 4: 718 c.device_mem = p[2] 719 c.context = p[3] 720 else: 721 c.device_mem = p[2] + "-" + p[3] 722 c.context = p[4] 723 724 p[0] = c 725 726def p_ioportcon(p): 727 '''ioportcon : IOPORTCON NUMBER context 728 | IOPORTCON NUMBER MINUS NUMBER context''' 729 c = refpolicy.IoportCon() 730 if len(p) == 4: 731 c.ioport = p[2] 732 c.context = p[3] 733 else: 734 c.ioport = p[2] + "-" + p[3] 735 c.context = p[4] 736 737 p[0] = c 738 739def p_pcidevicecon(p): 740 'pcidevicecon : PCIDEVICECON NUMBER context' 741 c = refpolicy.PciDeviceCon() 742 c.device = p[2] 743 c.context = p[3] 744 745 p[0] = c 746 747def p_devicetreecon(p): 748 'devicetreecon : DEVICETREECON NUMBER context' 749 c = refpolicy.DevicetTeeCon() 750 c.path = p[2] 751 c.context = p[3] 752 753 p[0] = c 754 755def p_mls_range_def(p): 756 '''mls_range_def : mls_level_def MINUS mls_level_def 757 | mls_level_def 758 ''' 759 p[0] = p[1] 760 if len(p) > 2: 761 p[0] = p[0] + "-" + p[3] 762 763def p_mls_level_def(p): 764 '''mls_level_def : IDENTIFIER COLON comma_list 765 | IDENTIFIER 766 ''' 767 p[0] = p[1] 768 if len(p) > 2: 769 p[0] = p[0] + ":" + ",".join(p[3]) 770 771def p_type_def(p): 772 '''type_def : TYPE IDENTIFIER COMMA comma_list SEMI 773 | TYPE IDENTIFIER SEMI 774 | TYPE IDENTIFIER ALIAS names SEMI 775 | TYPE IDENTIFIER ALIAS names COMMA comma_list SEMI 776 ''' 777 t = refpolicy.Type(p[2]) 778 if len(p) == 6: 779 if p[3] == ',': 780 t.attributes.update(p[4]) 781 else: 782 t.aliases = p[4] 783 elif len(p) > 4: 784 t.aliases = p[4] 785 if len(p) == 8: 786 t.attributes.update(p[6]) 787 p[0] = t 788 789def p_attribute_def(p): 790 'attribute_def : ATTRIBUTE IDENTIFIER SEMI' 791 a = refpolicy.Attribute(p[2]) 792 p[0] = a 793 794def p_attribute_role_def(p): 795 'attribute_role_def : ATTRIBUTE_ROLE IDENTIFIER SEMI' 796 a = refpolicy.Attribute_Role(p[2]) 797 p[0] = a 798 799def p_typealias_def(p): 800 'typealias_def : TYPEALIAS IDENTIFIER ALIAS names SEMI' 801 t = refpolicy.TypeAlias() 802 t.type = p[2] 803 t.aliases = p[4] 804 p[0] = t 805 806def p_role_def(p): 807 '''role_def : ROLE IDENTIFIER TYPES comma_list SEMI 808 | ROLE IDENTIFIER SEMI''' 809 r = refpolicy.Role() 810 r.role = p[2] 811 if len(p) > 4: 812 r.types.update(p[4]) 813 p[0] = r 814 815def p_role_allow(p): 816 'role_allow : ALLOW names names SEMI' 817 r = refpolicy.RoleAllow() 818 r.src_roles = p[2] 819 r.tgt_roles = p[3] 820 p[0] = r 821 822def p_permissive(p): 823 'permissive : PERMISSIVE names SEMI' 824 pass 825 826def p_avrule_def(p): 827 '''avrule_def : ALLOW names names COLON names names SEMI 828 | DONTAUDIT names names COLON names names SEMI 829 | AUDITALLOW names names COLON names names SEMI 830 | NEVERALLOW names names COLON names names SEMI 831 ''' 832 a = refpolicy.AVRule() 833 if p[1] == 'dontaudit': 834 a.rule_type = refpolicy.AVRule.DONTAUDIT 835 elif p[1] == 'auditallow': 836 a.rule_type = refpolicy.AVRule.AUDITALLOW 837 elif p[1] == 'neverallow': 838 a.rule_type = refpolicy.AVRule.NEVERALLOW 839 a.src_types = p[2] 840 a.tgt_types = p[3] 841 a.obj_classes = p[5] 842 a.perms = p[6] 843 p[0] = a 844 845def p_typerule_def(p): 846 '''typerule_def : TYPE_TRANSITION names names COLON names IDENTIFIER SEMI 847 | TYPE_TRANSITION names names COLON names IDENTIFIER FILENAME SEMI 848 | TYPE_TRANSITION names names COLON names IDENTIFIER IDENTIFIER SEMI 849 | TYPE_CHANGE names names COLON names IDENTIFIER SEMI 850 | TYPE_MEMBER names names COLON names IDENTIFIER SEMI 851 ''' 852 t = refpolicy.TypeRule() 853 if p[1] == 'type_change': 854 t.rule_type = refpolicy.TypeRule.TYPE_CHANGE 855 elif p[1] == 'type_member': 856 t.rule_type = refpolicy.TypeRule.TYPE_MEMBER 857 t.src_types = p[2] 858 t.tgt_types = p[3] 859 t.obj_classes = p[5] 860 t.dest_type = p[6] 861 t.file_name = p[7] 862 p[0] = t 863 864def p_typebound_def(p): 865 '''typebound_def : TYPEBOUNDS IDENTIFIER comma_list SEMI''' 866 t = refpolicy.TypeBound() 867 t.type = p[2] 868 t.tgt_types.update(p[3]) 869 p[0] = t 870 871def p_bool(p): 872 '''bool : BOOL IDENTIFIER TRUE SEMI 873 | BOOL IDENTIFIER FALSE SEMI''' 874 b = refpolicy.Bool() 875 b.name = p[2] 876 if p[3] == "true": 877 b.state = True 878 else: 879 b.state = False 880 p[0] = b 881 882def p_gen_tunable(p): 883 '''gen_tunable : GEN_TUNABLE OPAREN IDENTIFIER COMMA TRUE CPAREN 884 | GEN_TUNABLE OPAREN IDENTIFIER COMMA FALSE CPAREN 885 | GEN_TUNABLE OPAREN TICK IDENTIFIER SQUOTE COMMA TRUE CPAREN 886 | GEN_TUNABLE OPAREN TICK IDENTIFIER SQUOTE COMMA FALSE CPAREN''' 887 b = refpolicy.Bool() 888 if len(p) == 7: 889 id_pos = 3 890 state_pos = 5 891 else: 892 id_pos = 4 893 state_pos = 7 894 b.name = p[id_pos] 895 if p[state_pos] == "true": 896 b.state = True 897 else: 898 b.state = False 899 p[0] = b 900 901def p_conditional(p): 902 ''' conditional : IF OPAREN cond_expr CPAREN OBRACE interface_stmts CBRACE 903 | IF OPAREN cond_expr CPAREN OBRACE interface_stmts CBRACE ELSE OBRACE interface_stmts CBRACE 904 ''' 905 c = refpolicy.Conditional() 906 c.cond_expr = p[3] 907 collect(p[6], c, val=True) 908 if len(p) > 8: 909 collect(p[10], c, val=False) 910 p[0] = [c] 911 912def p_typeattribute_def(p): 913 '''typeattribute_def : TYPEATTRIBUTE IDENTIFIER comma_list SEMI''' 914 t = refpolicy.TypeAttribute() 915 t.type = p[2] 916 t.attributes.update(p[3]) 917 p[0] = t 918 919def p_roleattribute_def(p): 920 '''roleattribute_def : ROLEATTRIBUTE IDENTIFIER comma_list SEMI''' 921 t = refpolicy.RoleAttribute() 922 t.role = p[2] 923 t.roleattributes.update(p[3]) 924 p[0] = t 925 926def p_range_transition_def(p): 927 '''range_transition_def : RANGE_TRANSITION names names COLON names mls_range_def SEMI 928 | RANGE_TRANSITION names names names SEMI''' 929 pass 930 931def p_role_transition_def(p): 932 '''role_transition_def : ROLE_TRANSITION names names names SEMI''' 933 pass 934 935def p_cond_expr(p): 936 '''cond_expr : IDENTIFIER 937 | EXPL cond_expr 938 | cond_expr AMP AMP cond_expr 939 | cond_expr BAR BAR cond_expr 940 | cond_expr EQUAL EQUAL cond_expr 941 | cond_expr EXPL EQUAL cond_expr 942 ''' 943 l = len(p) 944 if l == 2: 945 p[0] = [p[1]] 946 elif l == 3: 947 p[0] = [p[1]] + p[2] 948 else: 949 p[0] = p[1] + [p[2] + p[3]] + p[4] 950 951 952# 953# Basic terminals 954# 955 956# Identifiers and lists of identifiers. These must 957# be handled somewhat gracefully. Names returns an IdSet and care must 958# be taken that this is _assigned_ to an object to correctly update 959# all of the flags (as opposed to using update). The other terminals 960# return list - this is to preserve ordering if it is important for 961# parsing (for example, interface_call must retain the ordering). Other 962# times the list should be used to update an IdSet. 963 964def p_names(p): 965 '''names : identifier 966 | nested_id_set 967 | asterisk 968 | TILDE identifier 969 | TILDE nested_id_set 970 | IDENTIFIER MINUS IDENTIFIER 971 ''' 972 s = refpolicy.IdSet() 973 if len(p) < 3: 974 expand(p[1], s) 975 elif len(p) == 3: 976 expand(p[2], s) 977 s.compliment = True 978 else: 979 expand([p[1]]) 980 s.add("-" + p[3]) 981 p[0] = s 982 983def p_identifier(p): 984 'identifier : IDENTIFIER' 985 p[0] = [p[1]] 986 987def p_asterisk(p): 988 'asterisk : ASTERISK' 989 p[0] = [p[1]] 990 991def p_nested_id_set(p): 992 '''nested_id_set : OBRACE nested_id_list CBRACE 993 ''' 994 p[0] = p[2] 995 996def p_nested_id_list(p): 997 '''nested_id_list : nested_id_element 998 | nested_id_list nested_id_element 999 ''' 1000 if len(p) == 2: 1001 p[0] = p[1] 1002 else: 1003 p[0] = p[1] + p[2] 1004 1005def p_nested_id_element(p): 1006 '''nested_id_element : identifier 1007 | MINUS IDENTIFIER 1008 | nested_id_set 1009 ''' 1010 if len(p) == 2: 1011 p[0] = p[1] 1012 else: 1013 # For now just leave the '-' 1014 str = "-" + p[2] 1015 p[0] = [str] 1016 1017def p_comma_list(p): 1018 '''comma_list : nested_id_list 1019 | comma_list COMMA nested_id_list 1020 ''' 1021 if len(p) > 2: 1022 p[1] = p[1] + p[3] 1023 p[0] = p[1] 1024 1025def p_optional_semi(p): 1026 '''optional_semi : SEMI 1027 | empty''' 1028 pass 1029 1030 1031# 1032# Interface to the parser 1033# 1034 1035def p_error(tok): 1036 global error, parse_file, success, parser 1037 error = "%s: Syntax error on line %d %s [type=%s]" % (parse_file, tok.lineno, tok.value, tok.type) 1038 print(error) 1039 success = False 1040 1041def prep_spt(spt): 1042 if not spt: 1043 return { } 1044 map = {} 1045 for x in spt: 1046 map[x.name] = x 1047 1048parser = None 1049lexer = None 1050def create_globals(module, support, debug): 1051 global parser, lexer, m, spt 1052 1053 if not parser: 1054 lexer = lex.lex() 1055 parser = yacc.yacc(method="LALR", debug=debug, write_tables=0) 1056 1057 if module is not None: 1058 m = module 1059 else: 1060 m = refpolicy.Module() 1061 1062 if not support: 1063 spt = refpolicy.SupportMacros() 1064 else: 1065 spt = support 1066 1067def parse(text, module=None, support=None, debug=False): 1068 create_globals(module, support, debug) 1069 global error, parser, lexer, success 1070 1071 lexer.lineno = 1 1072 success = True 1073 1074 try: 1075 parser.parse(text, debug=debug, lexer=lexer) 1076 except Exception as e: 1077 parser = None 1078 lexer = None 1079 error = "internal parser error: %s" % str(e) + "\n" + traceback.format_exc() 1080 1081 if not success: 1082 # force the parser and lexer to be rebuilt - we have some problems otherwise 1083 parser = None 1084 msg = 'could not parse text: "%s"' % error 1085 raise ValueError(msg) 1086 return m 1087 1088def list_headers(root): 1089 modules = [] 1090 support_macros = None 1091 1092 for dirpath, dirnames, filenames in os.walk(root): 1093 for name in filenames: 1094 modname = os.path.splitext(name) 1095 filename = os.path.join(dirpath, name) 1096 1097 if modname[1] == '.spt': 1098 if name == "obj_perm_sets.spt": 1099 support_macros = filename 1100 elif len(re.findall("patterns", modname[0])): 1101 modules.append((modname[0], filename)) 1102 elif modname[1] == '.if': 1103 modules.append((modname[0], filename)) 1104 1105 return (modules, support_macros) 1106 1107 1108def parse_headers(root, output=None, expand=True, debug=False): 1109 from . import util 1110 1111 headers = refpolicy.Headers() 1112 1113 modules = [] 1114 support_macros = None 1115 1116 if os.path.isfile(root): 1117 name = os.path.split(root)[1] 1118 if name == '': 1119 raise ValueError("Invalid file name %s" % root) 1120 modname = os.path.splitext(name) 1121 modules.append((modname[0], root)) 1122 all_modules, support_macros = list_headers(defaults.headers()) 1123 else: 1124 modules, support_macros = list_headers(root) 1125 1126 if expand and not support_macros: 1127 raise ValueError("could not find support macros (obj_perm_sets.spt)") 1128 1129 def o(msg): 1130 if output: 1131 output.write(msg) 1132 1133 def parse_file(f, module, spt=None): 1134 global parse_file 1135 if debug: 1136 o("parsing file %s\n" % f) 1137 try: 1138 fd = open(f) 1139 txt = fd.read() 1140 fd.close() 1141 parse_file = f 1142 parse(txt, module, spt, debug) 1143 except IOError as e: 1144 return 1145 except ValueError as e: 1146 raise ValueError("error parsing file %s: %s" % (f, str(e))) 1147 1148 spt = None 1149 if support_macros: 1150 o("Parsing support macros (%s): " % support_macros) 1151 spt = refpolicy.SupportMacros() 1152 parse_file(support_macros, spt) 1153 1154 headers.children.append(spt) 1155 1156 # FIXME: Total hack - add in can_exec rather than parse the insanity 1157 # of misc_macros. We are just going to pretend that this is an interface 1158 # to make the expansion work correctly. 1159 can_exec = refpolicy.Interface("can_exec") 1160 av = access.AccessVector(["$1","$2","file","execute_no_trans","open", "read", 1161 "getattr","lock","execute","ioctl"]) 1162 1163 can_exec.children.append(refpolicy.AVRule(av)) 1164 headers.children.append(can_exec) 1165 1166 o("done.\n") 1167 1168 if output and not debug: 1169 status = util.ConsoleProgressBar(sys.stdout, steps=len(modules)) 1170 status.start("Parsing interface files") 1171 1172 failures = [] 1173 for x in modules: 1174 m = refpolicy.Module() 1175 m.name = x[0] 1176 try: 1177 if expand: 1178 parse_file(x[1], m, spt) 1179 else: 1180 parse_file(x[1], m) 1181 except ValueError as e: 1182 o(str(e) + "\n") 1183 failures.append(x[1]) 1184 continue 1185 1186 headers.children.append(m) 1187 if output and not debug: 1188 status.step() 1189 1190 if len(failures): 1191 o("failed to parse some headers: %s\n" % ", ".join(failures)) 1192 1193 return headers 1194