17f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashmanfrom os.path import basename
27f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashmanimport re
37f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashmanimport sys
47f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
57f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman# A very limited parser whose job is to process the compatibility mapping
67f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman# files and retrieve type and attribute information until proper support is
77f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman# built into libsepol
87f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
97f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman# get the text in the next matching parens
107f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
117f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashmanclass MiniCilParser:
127f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    types = set() # types declared in mapping
137f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    pubtypes = set()
147f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    typeattributes = set() # attributes declared in mapping
157f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    typeattributesets = {} # sets defined in mapping
167f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    rTypeattributesets = {} # reverse mapping of above sets
177f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    apiLevel = None
187f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
197f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    def _getNextStmt(self, infile):
207f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        parens = 0
217f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        s = ""
227f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        c = infile.read(1)
237f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        # get to first statement
247f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        while c and c != "(":
257f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            c = infile.read(1)
267f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
277f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        parens += 1
287f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        c = infile.read(1)
297f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        while c and parens != 0:
307f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            s += c
317f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            c = infile.read(1)
327f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            if c == ';':
337f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman                # comment, get rid of rest of the line
347f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman                while c != '\n':
357f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman                    c = infile.read(1)
367f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            elif c == '(':
377f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman                parens += 1
387f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            elif c == ')':
397f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman                parens -= 1
407f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        return s
417f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
427f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    def _parseType(self, stmt):
437f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        m = re.match(r"type\s+(.+)", stmt)
448682d712d961d7a4937c648277f3dfeb47e27d52Dan Cashman        self.types.add(m.group(1))
457f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        return
467f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
477f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    def _parseTypeattribute(self, stmt):
487f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        m = re.match(r"typeattribute\s+(.+)", stmt)
498682d712d961d7a4937c648277f3dfeb47e27d52Dan Cashman        self.typeattributes.add(m.group(1))
507f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        return
517f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
527f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    def _parseTypeattributeset(self, stmt):
537f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        m = re.match(r"typeattributeset\s+(.+?)\s+\((.+?)\)", stmt, flags = re.M |re.S)
547f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        ta = m.group(1)
557f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        # this isn't proper expression parsing, but will do for our
567f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        # current use
577f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        tas = m.group(2).split()
587f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
597f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        if self.typeattributesets.get(ta) is None:
607f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            self.typeattributesets[ta] = set()
617f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        self.typeattributesets[ta].update(set(tas))
627f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        for t in tas:
637f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            if self.rTypeattributesets.get(t) is None:
647f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman                self.rTypeattributesets[t] = set()
657f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            self.rTypeattributesets[t].update(set(ta))
667f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
677f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        # check to see if this typeattributeset is a versioned public type
687f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        pub = re.match(r"(\w+)_\d+_\d+", ta)
697f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        if pub is not None:
708682d712d961d7a4937c648277f3dfeb47e27d52Dan Cashman            self.pubtypes.add(pub.group(1))
717f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        return
727f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
737f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    def _parseStmt(self, stmt):
747f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        if re.match(r"type\s+.+", stmt):
757f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            self._parseType(stmt)
767f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        elif re.match(r"typeattribute\s+.+", stmt):
777f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            self._parseTypeattribute(stmt)
787f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        elif re.match(r"typeattributeset\s+.+", stmt):
797f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            self._parseTypeattributeset(stmt)
807f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        else:
817f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            m = re.match(r"(\w+)\s+.+", stmt)
827f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            ret = "Warning: Unknown statement type (" + m.group(1) + ") in "
837f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            ret += "mapping file, perhaps consider adding support for it in "
847f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            ret += "system/sepolicy/tests/mini_parser.py!\n"
857f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            print ret
867f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        return
877f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
887f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    def __init__(self, policyFile):
897f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        with open(policyFile, 'r') as infile:
907f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            s = self._getNextStmt(infile)
917f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman            while s:
927f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman                self._parseStmt(s)
937f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman                s = self._getNextStmt(infile)
947f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        fn = basename(policyFile)
957f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        m = re.match(r"(\d+\.\d+).+\.cil", fn)
967f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman        self.apiLevel = m.group(1)
977f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman
987f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashmanif __name__ == '__main__':
997f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    f = sys.argv[1]
1007f7c3b8229b245bc4928cb4a52c0db0e52609f21Dan Cashman    p = MiniCilParser(f)
101