• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
75            if c == ';':
76                # comment, get rid of rest of the line
77                while c != '\n':
78                    c = infile.read(1)
79            elif c == '(':
80                break
81            c = infile.read(1)
82
83        parens += 1
84        c = infile.read(1)
85        while c and parens != 0:
86            s += c
87            c = infile.read(1)
88            if c == ';':
89                # comment, get rid of rest of the line
90                while c != '\n':
91                    c = infile.read(1)
92            elif c == '(':
93                parens += 1
94            elif c == ')':
95                parens -= 1
96        return s
97
98    def _parseType(self, stmt):
99        m = re.match(r"type\s+(.+)", stmt)
100        self.types.add(m.group(1))
101        return
102
103    def _parseExpandtypeattribute(self, stmt):
104        m = re.match(r"expandtypeattribute\s+\((.+)\)\s+(true|false)", stmt)
105        self.expandtypeattributes[m.group(1)] = m.group(2)
106        return
107
108    def _parseTypeattribute(self, stmt):
109        m = re.match(r"typeattribute\s+(.+)", stmt)
110        self.typeattributes.add(m.group(1))
111        return
112
113    def _parseTypeattributeset(self, stmt):
114        m = re.match(r"typeattributeset\s+(.+?)\s+\((.+?)\)", stmt, flags = re.M |re.S)
115        ta = m.group(1)
116        # this isn't proper expression parsing, but will do for our
117        # current use
118        tas = m.group(2).split()
119
120        if self.typeattributesets.get(ta) is None:
121            self.typeattributesets[ta] = set()
122        self.typeattributesets[ta].update(set(tas))
123        for t in tas:
124            if self.rTypeattributesets.get(t) is None:
125                self.rTypeattributesets[t] = set()
126            self.rTypeattributesets[t].update([ta])
127
128        # check to see if this typeattributeset is a versioned public type
129        pub = re.match(r"(\w+)_\d+_\d+", ta)
130        if pub is not None:
131            self.pubtypes.add(pub.group(1))
132        return
133
134    def _parseStmt(self, stmt):
135        if re.match(r"type\s+.+", stmt):
136            self._parseType(stmt)
137        elif re.match(r"typeattribute\s+.+", stmt):
138            self._parseTypeattribute(stmt)
139        elif re.match(r"typeattributeset\s+.+", stmt):
140            self._parseTypeattributeset(stmt)
141        elif re.match(r"expandtypeattribute\s+.+", stmt):
142            self._parseExpandtypeattribute(stmt)
143        return
144
145if __name__ == '__main__':
146    f = sys.argv[1]
147    p = MiniCilParser(f)
148