1# Copyright 2021 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15from os.path import basename 16import re 17import sys 18 19# A very limited parser whose job is to process the compatibility mapping 20# files and retrieve type and attribute information until proper support is 21# built into libsepol 22 23class MiniCilParser: 24 def __init__(self, policyFile): 25 self.types = set() # types declared in mapping 26 self.pubtypes = set() 27 self.expandtypeattributes = {} 28 self.typeattributes = set() # attributes declared in mapping 29 self.typeattributesets = {} # sets defined in mapping 30 self.rTypeattributesets = {} # reverse mapping of above sets 31 self.apiLevel = None 32 33 with open(policyFile, 'r') as infile: 34 s = self._getNextStmt(infile) 35 while s: 36 self._parseStmt(s) 37 s = self._getNextStmt(infile) 38 fn = basename(policyFile) 39 m = re.match(r"(\d+\.\d+).+\.cil", fn) 40 if m: 41 self.apiLevel = m.group(1) 42 43 def unparse(self): 44 def wrapParens(stmt): 45 return "(" + stmt + ")" 46 47 def joinWrapParens(entries): 48 return wrapParens(" ".join(entries)) 49 50 result = "" 51 for ty in sorted(self.types): 52 result += joinWrapParens(["type", ty]) + "\n" 53 54 for ta in sorted(self.typeattributes): 55 result += joinWrapParens(["typeattribute", ta]) + "\n" 56 57 for eta in sorted(self.expandtypeattributes.items(), 58 key=lambda x: x[0]): 59 result += joinWrapParens( 60 ["expandtypeattribute", wrapParens(eta[0]), eta[1]]) + "\n" 61 62 for tas in sorted(self.typeattributesets.items(), key=lambda x: x[0]): 63 result += joinWrapParens( 64 ["typeattributeset", tas[0], 65 joinWrapParens(sorted(tas[1]))]) + "\n" 66 67 return result 68 69 def _getNextStmt(self, infile): 70 parens = 0 71 s = "" 72 c = infile.read(1) 73 # get to first statement 74 while c and c != "(": 75 c = infile.read(1) 76 77 parens += 1 78 c = infile.read(1) 79 while c and parens != 0: 80 s += c 81 c = infile.read(1) 82 if c == ';': 83 # comment, get rid of rest of the line 84 while c != '\n': 85 c = infile.read(1) 86 elif c == '(': 87 parens += 1 88 elif c == ')': 89 parens -= 1 90 return s 91 92 def _parseType(self, stmt): 93 m = re.match(r"type\s+(.+)", stmt) 94 self.types.add(m.group(1)) 95 return 96 97 def _parseExpandtypeattribute(self, stmt): 98 m = re.match(r"expandtypeattribute\s+\((.+)\)\s+(true|false)", stmt) 99 self.expandtypeattributes[m.group(1)] = m.group(2) 100 return 101 102 def _parseTypeattribute(self, stmt): 103 m = re.match(r"typeattribute\s+(.+)", stmt) 104 self.typeattributes.add(m.group(1)) 105 return 106 107 def _parseTypeattributeset(self, stmt): 108 m = re.match(r"typeattributeset\s+(.+?)\s+\((.+?)\)", stmt, flags = re.M |re.S) 109 ta = m.group(1) 110 # this isn't proper expression parsing, but will do for our 111 # current use 112 tas = m.group(2).split() 113 114 if self.typeattributesets.get(ta) is None: 115 self.typeattributesets[ta] = set() 116 self.typeattributesets[ta].update(set(tas)) 117 for t in tas: 118 if self.rTypeattributesets.get(t) is None: 119 self.rTypeattributesets[t] = set() 120 self.rTypeattributesets[t].update([ta]) 121 122 # check to see if this typeattributeset is a versioned public type 123 pub = re.match(r"(\w+)_\d+_\d+", ta) 124 if pub is not None: 125 self.pubtypes.add(pub.group(1)) 126 return 127 128 def _parseStmt(self, stmt): 129 if re.match(r"type\s+.+", stmt): 130 self._parseType(stmt) 131 elif re.match(r"typeattribute\s+.+", stmt): 132 self._parseTypeattribute(stmt) 133 elif re.match(r"typeattributeset\s+.+", stmt): 134 self._parseTypeattributeset(stmt) 135 elif re.match(r"expandtypeattribute\s+.+", stmt): 136 self._parseExpandtypeattribute(stmt) 137 return 138 139if __name__ == '__main__': 140 f = sys.argv[1] 141 p = MiniCilParser(f) 142