| # Copyright 2021 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| from os.path import basename |
| import re |
| import sys |
| |
| # A very limited parser whose job is to process the compatibility mapping |
| # files and retrieve type and attribute information until proper support is |
| # built into libsepol |
| |
| class MiniCilParser: |
| def __init__(self, policyFile): |
| self.types = set() # types declared in mapping |
| self.pubtypes = set() |
| self.expandtypeattributes = {} |
| self.typeattributes = set() # attributes declared in mapping |
| self.typeattributesets = {} # sets defined in mapping |
| self.rTypeattributesets = {} # reverse mapping of above sets |
| self.apiLevel = None |
| |
| with open(policyFile, 'r') as infile: |
| s = self._getNextStmt(infile) |
| while s: |
| self._parseStmt(s) |
| s = self._getNextStmt(infile) |
| fn = basename(policyFile) |
| m = re.match(r"(\d+\.\d+).+\.cil", fn) |
| if m: |
| self.apiLevel = m.group(1) |
| |
| def unparse(self): |
| def wrapParens(stmt): |
| return "(" + stmt + ")" |
| |
| def joinWrapParens(entries): |
| return wrapParens(" ".join(entries)) |
| |
| result = "" |
| for ty in sorted(self.types): |
| result += joinWrapParens(["type", ty]) + "\n" |
| |
| for ta in sorted(self.typeattributes): |
| result += joinWrapParens(["typeattribute", ta]) + "\n" |
| |
| for eta in sorted(self.expandtypeattributes.items(), |
| key=lambda x: x[0]): |
| result += joinWrapParens( |
| ["expandtypeattribute", wrapParens(eta[0]), eta[1]]) + "\n" |
| |
| for tas in sorted(self.typeattributesets.items(), key=lambda x: x[0]): |
| result += joinWrapParens( |
| ["typeattributeset", tas[0], |
| joinWrapParens(sorted(tas[1]))]) + "\n" |
| |
| return result |
| |
| def _getNextStmt(self, infile): |
| parens = 0 |
| s = "" |
| c = infile.read(1) |
| # get to first statement |
| while c and c != "(": |
| c = infile.read(1) |
| |
| parens += 1 |
| c = infile.read(1) |
| while c and parens != 0: |
| s += c |
| c = infile.read(1) |
| if c == ';': |
| # comment, get rid of rest of the line |
| while c != '\n': |
| c = infile.read(1) |
| elif c == '(': |
| parens += 1 |
| elif c == ')': |
| parens -= 1 |
| return s |
| |
| def _parseType(self, stmt): |
| m = re.match(r"type\s+(.+)", stmt) |
| self.types.add(m.group(1)) |
| return |
| |
| def _parseExpandtypeattribute(self, stmt): |
| m = re.match(r"expandtypeattribute\s+\((.+)\)\s+(true|false)", stmt) |
| self.expandtypeattributes[m.group(1)] = m.group(2) |
| return |
| |
| def _parseTypeattribute(self, stmt): |
| m = re.match(r"typeattribute\s+(.+)", stmt) |
| self.typeattributes.add(m.group(1)) |
| return |
| |
| def _parseTypeattributeset(self, stmt): |
| m = re.match(r"typeattributeset\s+(.+?)\s+\((.+?)\)", stmt, flags = re.M |re.S) |
| ta = m.group(1) |
| # this isn't proper expression parsing, but will do for our |
| # current use |
| tas = m.group(2).split() |
| |
| if self.typeattributesets.get(ta) is None: |
| self.typeattributesets[ta] = set() |
| self.typeattributesets[ta].update(set(tas)) |
| for t in tas: |
| if self.rTypeattributesets.get(t) is None: |
| self.rTypeattributesets[t] = set() |
| self.rTypeattributesets[t].update([ta]) |
| |
| # check to see if this typeattributeset is a versioned public type |
| pub = re.match(r"(\w+)_\d+_\d+", ta) |
| if pub is not None: |
| self.pubtypes.add(pub.group(1)) |
| return |
| |
| def _parseStmt(self, stmt): |
| if re.match(r"type\s+.+", stmt): |
| self._parseType(stmt) |
| elif re.match(r"typeattribute\s+.+", stmt): |
| self._parseTypeattribute(stmt) |
| elif re.match(r"typeattributeset\s+.+", stmt): |
| self._parseTypeattributeset(stmt) |
| elif re.match(r"expandtypeattribute\s+.+", stmt): |
| self._parseExpandtypeattribute(stmt) |
| return |
| |
| if __name__ == '__main__': |
| f = sys.argv[1] |
| p = MiniCilParser(f) |