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