blob: 88a1998acf92cfdf9eb7d9fcb638e33aacab1c99 [file] [log] [blame]
ThiƩbaud Weksteenf24b4572021-11-26 09:12:41 +11001# 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
Dan Cashman91d398d2017-09-26 12:58:29 -070015from 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
Dan Cashman91d398d2017-09-26 12:58:29 -070023class MiniCilParser:
Tri Voe3f4f772018-09-28 17:21:08 -070024 def __init__(self, policyFile):
25 self.types = set() # types declared in mapping
26 self.pubtypes = set()
Tri Vo438684b2018-09-29 17:47:10 -070027 self.expandtypeattributes = {}
Tri Voe3f4f772018-09-28 17:21:08 -070028 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)
Dan Cashman91d398d2017-09-26 12:58:29 -070042
Tri Vo438684b2018-09-29 17:47:10 -070043 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
Dan Cashman91d398d2017-09-26 12:58:29 -070069 def _getNextStmt(self, infile):
70 parens = 0
71 s = ""
72 c = infile.read(1)
73 # get to first statement
Alan Stokes39507ae2024-05-31 12:14:30 +010074 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
Dan Cashman91d398d2017-09-26 12:58:29 -070081 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
Tri Vo438684b2018-09-29 17:47:10 -0700103 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
Dan Cashman91d398d2017-09-26 12:58:29 -0700108 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()
Tri Vo438684b2018-09-29 17:47:10 -0700126 self.rTypeattributesets[t].update([ta])
Dan Cashman91d398d2017-09-26 12:58:29 -0700127
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)
Tri Vo438684b2018-09-29 17:47:10 -0700141 elif re.match(r"expandtypeattribute\s+.+", stmt):
142 self._parseExpandtypeattribute(stmt)
Dan Cashman91d398d2017-09-26 12:58:29 -0700143 return
144
Dan Cashman91d398d2017-09-26 12:58:29 -0700145if __name__ == '__main__':
146 f = sys.argv[1]
147 p = MiniCilParser(f)