1from os.path import basename 2import re 3import sys 4 5# A very limited parser whose job is to process the compatibility mapping 6# files and retrieve type and attribute information until proper support is 7# built into libsepol 8 9# get the text in the next matching parens 10 11class MiniCilParser: 12 def __init__(self, policyFile): 13 self.types = set() # types declared in mapping 14 self.pubtypes = set() 15 self.expandtypeattributes = {} 16 self.typeattributes = set() # attributes declared in mapping 17 self.typeattributesets = {} # sets defined in mapping 18 self.rTypeattributesets = {} # reverse mapping of above sets 19 self.apiLevel = None 20 21 with open(policyFile, 'r') as infile: 22 s = self._getNextStmt(infile) 23 while s: 24 self._parseStmt(s) 25 s = self._getNextStmt(infile) 26 fn = basename(policyFile) 27 m = re.match(r"(\d+\.\d+).+\.cil", fn) 28 if m: 29 self.apiLevel = m.group(1) 30 31 def unparse(self): 32 def wrapParens(stmt): 33 return "(" + stmt + ")" 34 35 def joinWrapParens(entries): 36 return wrapParens(" ".join(entries)) 37 38 result = "" 39 for ty in sorted(self.types): 40 result += joinWrapParens(["type", ty]) + "\n" 41 42 for ta in sorted(self.typeattributes): 43 result += joinWrapParens(["typeattribute", ta]) + "\n" 44 45 for eta in sorted(self.expandtypeattributes.items(), 46 key=lambda x: x[0]): 47 result += joinWrapParens( 48 ["expandtypeattribute", wrapParens(eta[0]), eta[1]]) + "\n" 49 50 for tas in sorted(self.typeattributesets.items(), key=lambda x: x[0]): 51 result += joinWrapParens( 52 ["typeattributeset", tas[0], 53 joinWrapParens(sorted(tas[1]))]) + "\n" 54 55 return result 56 57 def _getNextStmt(self, infile): 58 parens = 0 59 s = "" 60 c = infile.read(1) 61 # get to first statement 62 while c and c != "(": 63 c = infile.read(1) 64 65 parens += 1 66 c = infile.read(1) 67 while c and parens != 0: 68 s += c 69 c = infile.read(1) 70 if c == ';': 71 # comment, get rid of rest of the line 72 while c != '\n': 73 c = infile.read(1) 74 elif c == '(': 75 parens += 1 76 elif c == ')': 77 parens -= 1 78 return s 79 80 def _parseType(self, stmt): 81 m = re.match(r"type\s+(.+)", stmt) 82 self.types.add(m.group(1)) 83 return 84 85 def _parseExpandtypeattribute(self, stmt): 86 m = re.match(r"expandtypeattribute\s+\((.+)\)\s+(true|false)", stmt) 87 self.expandtypeattributes[m.group(1)] = m.group(2) 88 return 89 90 def _parseTypeattribute(self, stmt): 91 m = re.match(r"typeattribute\s+(.+)", stmt) 92 self.typeattributes.add(m.group(1)) 93 return 94 95 def _parseTypeattributeset(self, stmt): 96 m = re.match(r"typeattributeset\s+(.+?)\s+\((.+?)\)", stmt, flags = re.M |re.S) 97 ta = m.group(1) 98 # this isn't proper expression parsing, but will do for our 99 # current use 100 tas = m.group(2).split() 101 102 if self.typeattributesets.get(ta) is None: 103 self.typeattributesets[ta] = set() 104 self.typeattributesets[ta].update(set(tas)) 105 for t in tas: 106 if self.rTypeattributesets.get(t) is None: 107 self.rTypeattributesets[t] = set() 108 self.rTypeattributesets[t].update([ta]) 109 110 # check to see if this typeattributeset is a versioned public type 111 pub = re.match(r"(\w+)_\d+_\d+", ta) 112 if pub is not None: 113 self.pubtypes.add(pub.group(1)) 114 return 115 116 def _parseStmt(self, stmt): 117 if re.match(r"type\s+.+", stmt): 118 self._parseType(stmt) 119 elif re.match(r"typeattribute\s+.+", stmt): 120 self._parseTypeattribute(stmt) 121 elif re.match(r"typeattributeset\s+.+", stmt): 122 self._parseTypeattributeset(stmt) 123 elif re.match(r"expandtypeattribute\s+.+", stmt): 124 self._parseExpandtypeattribute(stmt) 125 return 126 127if __name__ == '__main__': 128 f = sys.argv[1] 129 p = MiniCilParser(f) 130