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