-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsemodrequire
More file actions
executable file
·151 lines (142 loc) · 6.56 KB
/
semodrequire
File metadata and controls
executable file
·151 lines (142 loc) · 6.56 KB
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
143
144
145
146
147
148
149
150
151
#!/usr/bin/env python3
from pathlib import Path
from re import search
from sys import argv
#############
### USAGE ###
#############
def usage():
# Name of this script without any paths
name = Path(argv[0]).name
print(f"NAME\n\t{name} - python script to generate the 'require' section within a SELinux policy module file\n")
print(f"SYNOPSIS\n\t{name} <module>.te\n")
print(f"DESCRIPTION\n\t{name} is a Python script provided by the HardHat project to generate the 'require' section for SELinux policy modules. The only argument this script takes is the path to the <module>.te file.\n")
print(f"EXAMPLE\n\t1. {name} chromium-browser.te\n")
print("\n")
#################
### FUNCTIONS ###
#################
def args():
# Check if the user has specified the help flag (any argument)
if any([True for arg in argv if arg == '-h']):
# If so, then display the usage information
usage()
# Exit
exit(0)
try:
# Argument 1: Path to the SELinux module file
filename = Path(argv[1]).resolve().expanduser()
except IndexError:
# Display the usage information
usage()
# If no arguments were specified, then display an error message to stdout
print("ERROR: Argument 1: Specify the SELinux '.te' file")
# Exit with an error
exit(1)
# Return the $filename
return(filename)
def verify(filename):
if Path(filename).is_file():
# If the $filename is an existing file, return True
return(True)
else:
# Otherwise, display an error message to stdout
print(f"ERROR: Unable to locate file: '{filename}'")
# Exit with an error
exit(1)
def read(filename):
# Open and read the contents of $filename
with open(filename, 'r') as f: contents = f.readlines()
# Remove extraneous spaces
contents = [x.strip() for x in contents]
# Return $contents
return(contents)
def classes(contents_allow):
# Define the set to contain the SELinux classes used in $filename
classes = set()
# Iterate through each entry in the $contents_allow list
for entry in contents_allow:
# The first part of the entry will be the type, and the last part will be the class
name = entry.split(':')[-1].split(' ')[0]
# Define only the class name
classes.add(name)
# Define a new set to contain only the classes and permissions required for the current $filename
all_classes = set()
# Iterate through each class in $classes
for c in classes:
# For the current class, define only the permissions (eg. '{ create getattr read write }')
permissions = [perm for entry in contents_allow if f":{c}" in entry for perm in entry.split(f":{c}", 1)[-1].split(' ')]
# Iterate though each entry in $c_entries, and then only select the text that contains valid characters
permissions = [search(r'[a-zA-Z_ ]+', p) for p in permissions]
# Only use permissions that are valid (i.e. are not Nonetype) and sort the $permissions set
permissions = sorted(set([p.group().strip() for p in permissions if p]))
# Define the proper format and add it to the $all_classes set
all_classes.add('class %s { %s };' % (c, ' '.join(permissions)))
# Join $all_classes via newline
all_classes = '\n'.join(sorted(all_classes))
# Return the $all_classes set
return(all_classes)
def roles(contents):
# Find all entries in $contents that start with 'role' AND does not contain the string 'types', which is the SELinux permission string
contents_role = [entry for entry in contents if entry.startswith('role') and ('types' not in entry)]
# Define the set to contain the SELinux roles used in $filename
roles = set()
# Iterate through each entry in the $contents_role list
for entry in contents_role:
# Split via the specified text in order to obtain only the name of the current role
name = entry.split('role ')[-1]
try:
# Define only the alphabetical characters and underscore as the name (eg. 'user_r;' will become 'user_r')
name = search('[a-zA-Z_]+', name).group()
except AttributeError:
# If there are no roles, then return an empty string
return('')
# Define the proper format and add it to the $roles set
roles.add(f"role {name};")
# Sort alphabetically and join all entries via newline to create one string that contains all roles
roles = '\n'.join(sorted(roles))
# Return the $roles string
return(roles)
def types(contents_allow):
# Define the set to contain the SELinux types used in $filename
types = set()
# Iterate through each entry in the $contents_allow list
for entry in contents_allow:
# The first part of the entry will be the type, and the last part will be the class. Keep only the part with the type (eg. 'allow test_t named_t') and then split this via spaces (eg. ['allow', 'test_t', 'named_t'])
entry_s = entry.split(':')[0].split(' ')
# For every entry in $entry_s that ends with the string '_t', add to the $types set
[types.add(t) for t in entry_s if t.endswith('_t')]
# For each type in $types, except for 'self' that isn't a proper type, create the string 'type $name;' where $name is the current entry. Finally, join the entire list via the newline delimiter
types = '\n'.join([f"type {t};" for t in sorted(types) if t not in ('self')])
# Return the string containing all types
return(types)
############
### MAIN ###
############
def main(filename):
# Verify the $filename is a valid file
verify(filename)
# Read and obtain $filename contents
contents = read(filename)
# Define a string containing all requires roles for $filename
all_roles = roles(contents)
# Find all entries in $contents that start with 'allow', which will contain the classes and types that will need to be added to the require section
contents_allow = [entry for entry in contents if entry.startswith('allow')]
# Define a string containing all required classes and permissions for $filename
all_classes = classes(contents_allow)
# Define a string containing all required types for $filename
all_types = types(contents_allow)
# Display all classes to stdout
print(all_classes)
# If applicable, display the roles to stdout
if all_roles: print(all_roles)
# Display all types to stdout
print(all_types)
#############
### START ###
#############
if __name__ == '__main__':
# Obtain the SELinux module file
filename = args()
# Start the script
main(filename)