summaryrefslogtreecommitdiff
path: root/tests/fc_sort.py
blob: cbb0e5e23c5e6a2e9411ea3663cfd508ecfe8ed5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python
import sys
import os
import argparse

class FileContextsNode:
    path = None
    fileType = None
    context = None
    Type = None
    meta = None
    stemLen = None
    strLen = None
    Type = None
    line = None
    def __init__(self, path, fileType, context, meta, stemLen, strLen, line):
        self.path = path
        self.fileType = fileType
        self.context = context
        self.meta = meta
        self.stemLen = stemLen
        self.strlen = strLen
        self.Type = context.split(":")[2]
        self.line = line

metaChars = frozenset(['.', '^', '$', '?', '*', '+', '|', '[', '(', '{'])
escapedMetaChars = frozenset(['\.', '\^', '\$', '\?', '\*', '\+', '\|', '\[', '\(', '\{'])

def getStemLen(path):
    global metaChars
    stemLen = 0
    i = 0
    while i < len(path):
        if path[i] == "\\":
            i += 1
        elif path[i] in metaChars:
            break
        stemLen += 1
        i += 1
    return stemLen


def getIsMeta(path):
    global metaChars
    global escapedMetaChars
    metaCharsCount = 0
    escapedMetaCharsCount = 0
    for c in metaChars:
        if c in path:
            metaCharsCount += 1
    for c in escapedMetaChars:
        if c in path:
            escapedMetaCharsCount += 1
    return metaCharsCount > escapedMetaCharsCount

def CreateNode(line):
    global metaChars
    if (len(line) == 0) or (line[0] == '#'):
        return None

    split = line.split()
    path = split[0].strip()
    context = split[-1].strip()
    fileType = None
    if len(split) == 3:
        fileType = split[1].strip()
    meta = getIsMeta(path)
    stemLen = getStemLen(path)
    strLen = len(path.replace("\\", ""))

    return FileContextsNode(path, fileType, context, meta, stemLen, strLen, line)

def ReadFileContexts(files):
    fc = []
    for f in files:
        fd = open(f)
        for line in fd:
            node = CreateNode(line.strip())
            if node != None:
                fc.append(node)
    return fc

# Comparator function for list.sort() based off of fc_sort.c
# Compares two FileContextNodes a and b and returns 1 if a is more
# specific or -1 if b is more specific.
def compare(a, b):
    # The regex without metachars is more specific
    if a.meta and not b.meta:
        return -1
    if b.meta and not a.meta:
        return 1

    # The regex with longer stemlen (regex before any meta characters) is more specific.
    if a.stemLen < b.stemLen:
        return -1
    if b.stemLen < a.stemLen:
        return 1

    # The regex with longer string length is more specific
    if a.strLen < b.strLen:
        return -1
    if b.strLen < a.strLen:
        return 1

    # A regex with a fileType defined (e.g. file, dir) is more specific.
    if a.fileType is None and b.fileType is not None:
        return -1
    if b.fileType is None and a.fileType is not None:
        return 1

    # Regexes are equally specific.
    return 0

def FcSort(files):
    for f in files:
        if not os.path.exists(f):
            sys.exit("Error: File_contexts file " + f + " does not exist\n")

    Fc = ReadFileContexts(files)
    Fc.sort(cmp=compare)

    return Fc

def PrintFc(Fc, out):
    if not out:
        f = sys.stdout
    else:
        f = open(out, "w")
    for node in Fc:
        f.write(node.line + "\n")

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="SELinux file_contexts sorting tool.")
    parser.add_argument("-i", dest="input", help="Path to the file_contexts file(s).", nargs="?", action='append')
    parser.add_argument("-o", dest="output", help="Path to the output file", nargs=1)
    args = parser.parse_args()
    if not args.input:
        parser.error("Must include path to policy")
    if not not args.output:
        args.output = args.output[0]

    PrintFc(FcSort(args.input),args.output)