]>
Commit | Line | Data |
---|---|---|
c04f2abe CP |
1 | #!/usr/bin/python |
2 | ||
3 | # Author: Donald Miner <dminer@tresys.com> | |
4 | # | |
6b873c4d | 5 | # Copyright (C) 2005 Tresys Technology, LLC |
c04f2abe CP |
6 | # This program is free software; you can redistribute it and/or modify |
7 | # it under the terms of the GNU General Public License as published by | |
8 | # the Free Software Foundation, version 2. | |
9 | ||
10 | ||
11 | """ | |
12 | This script generates an object class perm definition file. | |
13 | """ | |
14 | ||
15 | import sys | |
16 | ||
17 | USERSPACE_CLASS = "userspace" | |
18 | ||
19 | class Class: | |
20 | """ | |
21 | This object stores an access vector class. | |
22 | """ | |
23 | ||
24 | def __init__(self, name, perms, common): | |
25 | # The name of the class. | |
26 | self.name = name | |
27 | ||
28 | # A list of permissions the class contains. | |
29 | self.perms = perms | |
30 | ||
31 | # True if the class is declared as common, False if not. | |
32 | self.common = common | |
33 | ||
6e0542eb | 34 | def get_perms(name, av_db, common): |
c04f2abe CP |
35 | """ |
36 | Returns the list of permissions contained within an access vector | |
37 | class that is stored in the access vector database av_db. | |
38 | Returns an empty list if the object name is not found. | |
6e0542eb CP |
39 | Specifiy whether get_perms is to return the class or the |
40 | common set of permissions with the boolean value 'common', | |
41 | which is important in the case of having duplicate names (such as | |
42 | class file and common file). | |
c04f2abe CP |
43 | """ |
44 | ||
45 | # Traverse through the access vector database and try to find the | |
46 | # object with the name passed. | |
47 | for obj in av_db: | |
6e0542eb | 48 | if obj.name == name and obj.common == common: |
c04f2abe CP |
49 | return obj.perms |
50 | ||
51 | return [] | |
52 | ||
53 | def get_av_db(file_name): | |
54 | """ | |
55 | Returns an access vector database generated from the file file_name. | |
56 | """ | |
a6df70c1 CP |
57 | # This function takes a file, reads the data, parses it and returns |
58 | # a list of access vector classes. | |
59 | # Reading into av_data: | |
60 | # The file specified will be read line by line. Each line will have | |
61 | # its comments removed. Once comments are removed, each 'word' (text | |
62 | # seperated by whitespace) and braces will be split up into seperate | |
63 | # strings and appended to the av_data list, in the order they were | |
64 | # read. | |
65 | # Parsing av_data: | |
66 | # Parsing is done using a queue implementation of the av_data list. | |
67 | # Each time a word is used, it is dequeued afterwards. Each loop in | |
68 | # the while loop below will read in key words and dequeue expected | |
69 | # words and values. At the end of each loop, a Class containing the | |
70 | # name, permissions and whether it is a common or not will be appended | |
71 | # to the database. Lots of errors are caught here, almost all checking | |
72 | # if a token is expected but EOF is reached. | |
73 | # Now the list of Class objects is returned. | |
c04f2abe CP |
74 | |
75 | av_file = open(file_name, "r") | |
76 | av_data = [] | |
77 | # Read the file and strip out comments on the way. | |
78 | # At the end of the loop, av_data will contain a list of individual | |
79 | # words. i.e. ['common', 'file', '{', ...]. All comments and whitespace | |
80 | # will be gone. | |
81 | while True: | |
82 | av_line = av_file.readline() | |
83 | ||
84 | # If EOF has been reached: | |
85 | if not av_line: | |
86 | break | |
87 | ||
88 | # Check if there is a comment, and if there is, remove it. | |
89 | comment_index = av_line.find("#") | |
90 | if comment_index != -1: | |
91 | av_line = av_line[:comment_index] | |
92 | ||
93 | # Pad the braces with whitespace so that they are split into | |
94 | # their own word. It doesn't matter if there will be extra | |
95 | # white space, it'll get thrown away when the string is split. | |
96 | av_line.replace("{"," { ") | |
97 | av_line.replace("}"," } ") | |
98 | ||
99 | # Split up the words on the line and add it to av_data. | |
100 | av_data += av_line.split() | |
101 | ||
102 | av_file.close() | |
103 | ||
104 | # Parsing the file: | |
105 | # The implementation of this parse is a queue. We use the list of words | |
106 | # from av_data and use the front element, then dequeue it. Each | |
107 | # loop of this while is a common or class declaration. Several | |
108 | # expected tokens are parsed and dequeued out of av_data for each loop. | |
a6df70c1 CP |
109 | # At the end of the loop, database will contain a list of Class objects. |
110 | # i.e. [Class('name',['perm1','perm2',...],'True'), ...] | |
c04f2abe CP |
111 | # Dequeue from the beginning of the list until av_data is empty: |
112 | database = [] | |
113 | while len(av_data) != 0: | |
114 | # At the beginning of every loop, the next word should be | |
115 | # "common" or "class", meaning that each loop is a common | |
116 | # or class declaration. | |
117 | # av_data = av_data[1:] removes the first element in the | |
118 | # list, this is what is dequeueing data. | |
119 | ||
120 | # Figure out whether the next class will be a common or a class. | |
121 | if av_data[0] == "class": | |
122 | common = False | |
123 | elif av_data[0] == "common": | |
124 | common = True | |
125 | else: | |
126 | error("Unexpected token in file " + file_name + ": "\ | |
127 | + av_data[0] + ".") | |
128 | ||
129 | # Dequeue the "class" or "common" key word. | |
130 | av_data = av_data[1:] | |
131 | ||
132 | if len(av_data) == 0: | |
133 | error("Missing token in file " + file_name + ".") | |
134 | ||
135 | # Get and dequeue the name of the class or common. | |
136 | name = av_data[0] | |
137 | av_data = av_data[1:] | |
138 | ||
139 | # Retrieve the permissions inherited from a common set: | |
140 | perms = [] | |
141 | # If the object we are working with is a class, since only | |
142 | # classes inherit: | |
143 | if common == False: | |
144 | if len(av_data) == 0: | |
145 | error("Missing token in file " + file_name + ".") | |
146 | ||
147 | # If the class inherits from something else: | |
148 | if av_data[0] == "inherits": | |
149 | # Dequeue the "inherits" key word. | |
150 | av_data = av_data[1:] | |
151 | ||
152 | if len(av_data) == 0: | |
153 | error("Missing token in file "\ | |
154 | + file_name + " for " +\ | |
155 | keyword + " " + name + ".") | |
156 | ||
157 | # av_data[0] is the name of the parent. | |
158 | # Append the permissions of the parent to | |
159 | # the current class' permissions. | |
6e0542eb CP |
160 | perms += get_perms(av_data[0], database, True) |
161 | ||
c04f2abe CP |
162 | # Dequeue the name of the parent. |
163 | av_data = av_data[1:] | |
164 | ||
165 | # Retrieve the permissions defined with this set. | |
166 | if len(av_data) > 0 and av_data[0] == "{": | |
167 | # Dequeue the "{" | |
168 | av_data = av_data[1:] | |
169 | ||
170 | # Keep appending permissions until a close brace is | |
171 | # found. | |
172 | while av_data[0] != "}": | |
173 | if av_data[0] == "{": | |
174 | error("Extra '{' in file " +\ | |
175 | file_name + ".") | |
176 | ||
177 | # Add the permission name. | |
178 | perms.append(av_data[0]) | |
179 | ||
180 | # Dequeue the permission name. | |
181 | av_data = av_data[1:] | |
182 | ||
183 | if len(av_data) == 0: | |
184 | error("Missing token '}' in file "\ | |
185 | + file_name + ".") | |
186 | ||
187 | # Dequeue the "}" | |
188 | av_data = av_data[1:] | |
189 | ||
190 | # Add the new access vector class to the database. | |
191 | database.append(Class(name, perms, common)) | |
192 | ||
193 | return database | |
194 | ||
195 | def get_sc_db(file_name): | |
196 | """ | |
197 | Returns a security class database generated from the file file_name. | |
198 | """ | |
199 | ||
200 | # Read the file then close it. | |
201 | sc_file = open(file_name) | |
202 | sc_data = sc_file.readlines() | |
203 | sc_file.close() | |
204 | ||
205 | # For each line in the security classes file, add the name of the class | |
206 | # and whether it is a userspace class or not to the security class | |
207 | # database. | |
208 | database = [] | |
209 | for line in sc_data: | |
210 | line = line.lstrip() | |
211 | # If the line is empty or the entire line is a comment, skip. | |
212 | if line == "" or line[0] == "#": | |
213 | continue | |
214 | ||
215 | # Check if the comment to the right of the permission matches | |
216 | # USERSPACE_CLASS. | |
217 | comment_index = line.find("#") | |
218 | if comment_index != -1 and line[comment_index+1:].strip() == USERSPACE_CLASS: | |
219 | userspace = True | |
220 | else: | |
221 | userspace = False | |
222 | ||
223 | # All lines should be in the format "class NAME", meaning | |
224 | # it should have two tokens and the first token should be | |
225 | # "class". | |
226 | split_line = line.split() | |
227 | if len(split_line) < 2 or split_line[0] != "class": | |
228 | error("Wrong syntax: " + line) | |
229 | ||
230 | # Add the class's name (split_line[1]) and whether it is a | |
231 | # userspace class or not to the database. | |
232 | # This is appending a tuple of (NAME,USERSPACE), where NAME is | |
233 | # the name of the security class and USERSPACE is True if | |
234 | # if it has "# USERSPACE_CLASS" on the end of the line, False | |
235 | # if not. | |
236 | database.append((split_line[1], userspace)) | |
237 | ||
238 | return database | |
239 | ||
240 | def gen_class_perms(av_db, sc_db): | |
241 | """ | |
242 | Generates a class permissions document and returns it. | |
243 | """ | |
244 | ||
245 | # Define class template: | |
246 | class_perms_line = "define(`all_%s_perms',`{ %s}')\n" | |
247 | ||
248 | # Generate the defines for the individual class permissions. | |
249 | class_perms = "" | |
250 | for obj in av_db: | |
251 | # Don't output commons | |
252 | if obj.common == True: | |
253 | continue | |
254 | ||
6e0542eb CP |
255 | # Get the list of permissions from the specified class. |
256 | perms = get_perms(obj.name, av_db, False) | |
c04f2abe CP |
257 | |
258 | # Merge all the permissions into one string with one space | |
259 | # padding. | |
260 | perm_str = "" | |
261 | for perm in perms: | |
262 | perm_str += perm + " " | |
263 | ||
264 | # Add the line to the class_perms | |
265 | class_perms += class_perms_line % (obj.name, perm_str) | |
266 | class_perms += "\n" | |
267 | ||
268 | # Generate the kernel_class_perms and userspace_class_perms sets. | |
269 | class_line = "\tclass %s all_%s_perms;\n" | |
270 | kernel_class_perms = "define(`all_kernel_class_perms',`\n" | |
271 | userspace_class_perms = "define(`all_userspace_class_perms',`\n" | |
272 | # For each (NAME,USERSPACE) tuple, add the class to the appropriate | |
273 | # class permission set. | |
274 | for name, userspace in sc_db: | |
275 | if userspace: | |
276 | userspace_class_perms += class_line % (name, name) | |
277 | else: | |
278 | kernel_class_perms += class_line % (name, name) | |
279 | kernel_class_perms += "')\n\n" | |
280 | userspace_class_perms += "')\n" | |
281 | ||
282 | # Throw all the strings together and return the string. | |
283 | return class_perms + kernel_class_perms + userspace_class_perms | |
284 | ||
285 | def error(error): | |
286 | """ | |
287 | Print an error message and exit. | |
288 | """ | |
289 | ||
290 | sys.stderr.write("%s exiting for: " % sys.argv[0]) | |
291 | sys.stderr.write("%s\n" % error) | |
292 | sys.stderr.flush() | |
293 | sys.exit(1) | |
294 | ||
295 | # MAIN PROGRAM | |
296 | app_name = sys.argv[0] | |
297 | ||
298 | if len(sys.argv) != 3: | |
299 | error("Incorrect input.\nUsage: " + sys.argv[0] + " access_vectors security_classes" ) | |
300 | ||
301 | # argv[1] is the access vector file. | |
302 | av_file = sys.argv[1] | |
303 | ||
304 | # argv[2] is the security class file. | |
305 | sc_file = sys.argv[2] | |
306 | ||
307 | # Output the class permissions document. | |
308 | sys.stdout.write(gen_class_perms(get_av_db(av_file), get_sc_db(sc_file))) |