1 | #============================================================================ |
---|
2 | # This library is free software; you can redistribute it and/or |
---|
3 | # modify it under the terms of version 2.1 of the GNU Lesser General Public |
---|
4 | # License as published by the Free Software Foundation. |
---|
5 | # |
---|
6 | # This library is distributed in the hope that it will be useful, |
---|
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
9 | # Lesser General Public License for more details. |
---|
10 | # |
---|
11 | # You should have received a copy of the GNU Lesser General Public |
---|
12 | # License along with this library; if not, write to the Free Software |
---|
13 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
14 | #============================================================================ |
---|
15 | # Copyright (C) 2006 International Business Machines Corp. |
---|
16 | # Author: Reiner Sailer <sailer@us.ibm.com> |
---|
17 | # Contributions: Stefan Berger <stefanb@us.ibm.com> |
---|
18 | #============================================================================ |
---|
19 | """Configuring a security policy into the boot configuration |
---|
20 | """ |
---|
21 | |
---|
22 | import sys |
---|
23 | import traceback |
---|
24 | import tempfile |
---|
25 | import os, stat |
---|
26 | import shutil |
---|
27 | import string |
---|
28 | import re |
---|
29 | from xen.util.security import err |
---|
30 | from xen.util.security import policy_dir_prefix, xen_title_re |
---|
31 | from xen.util.security import boot_filename, altboot_filename |
---|
32 | from xen.util.security import any_title_re, xen_kernel_re, any_module_re |
---|
33 | from xen.util.security import empty_line_re, binary_name_re, policy_name_re |
---|
34 | from xen.xm.opts import OptionError |
---|
35 | |
---|
36 | def help(): |
---|
37 | return """ |
---|
38 | Adds a 'module' line to the Xen grub configuration file entry |
---|
39 | so that Xen boots with a specific access control policy. If |
---|
40 | boot-title is not given, then this script tries to determine |
---|
41 | it by looking for a title starting with \"XEN\". If there are |
---|
42 | multiple entries matching, then it must be called with the unique |
---|
43 | beginning of the title's name.\n""" |
---|
44 | |
---|
45 | def strip_title(line): |
---|
46 | """ |
---|
47 | strips whitespace left and right and cuts 'title' |
---|
48 | """ |
---|
49 | s_title = string.strip(line) |
---|
50 | pos = string.index(s_title, "title") |
---|
51 | if pos >= 0: |
---|
52 | return s_title[pos+6:] |
---|
53 | else: |
---|
54 | return s_title |
---|
55 | |
---|
56 | |
---|
57 | def insert_policy(boot_file, alt_boot_file, user_title, policy_name): |
---|
58 | """ |
---|
59 | inserts policy binary file as last line of the grub entry |
---|
60 | matching the user_title or default title |
---|
61 | """ |
---|
62 | if user_title: |
---|
63 | #replace "(" by "\(" and ")" by "\)" for matching |
---|
64 | user_title = string.replace(user_title, "(", "\(") |
---|
65 | user_title = string.replace(user_title, ")", "\)") |
---|
66 | user_title_re = re.compile("\s*title\s+.*%s" \ |
---|
67 | % user_title, re.IGNORECASE) |
---|
68 | else: |
---|
69 | user_title_re = xen_title_re |
---|
70 | |
---|
71 | within_xen_title = 0 |
---|
72 | within_xen_entry = 0 |
---|
73 | insert_at_end_of_entry = 0 |
---|
74 | path_prefix = '' |
---|
75 | this_title = '' |
---|
76 | extended_titles = [] |
---|
77 | (tmp_fd, tmp_grub) = tempfile.mkstemp() |
---|
78 | #First check whether menu.lst exists |
---|
79 | if not os.path.isfile(boot_file): |
---|
80 | #take alternate boot file (grub.conf) instead |
---|
81 | boot_file = alt_boot_file |
---|
82 | #follow symlink since menue.lst might be linked to grub.conf |
---|
83 | if stat.S_ISLNK(os.lstat(boot_file)[stat.ST_MODE]): |
---|
84 | new_name = os.readlink(boot_file) |
---|
85 | if new_name[0] == "/": |
---|
86 | boot_file = new_name |
---|
87 | else: |
---|
88 | path = boot_file.split('/') |
---|
89 | path[len(path)-1] = new_name |
---|
90 | boot_file = '/'.join(path) |
---|
91 | if not os.path.exists(boot_file): |
---|
92 | err("Boot file \'%s\' not found." % boot_file) |
---|
93 | grub_fd = open(boot_file) |
---|
94 | for line in grub_fd: |
---|
95 | if user_title_re.match(line): |
---|
96 | this_title = strip_title(line) |
---|
97 | within_xen_title = 1 |
---|
98 | elif within_xen_title and xen_kernel_re.match(line): |
---|
99 | insert_at_end_of_entry = 1 |
---|
100 | #use prefix from xen.gz path for policy |
---|
101 | path_prefix = line.split()[1] |
---|
102 | idx = path_prefix.rfind('/') |
---|
103 | if idx >= 0: |
---|
104 | path_prefix = path_prefix[0:idx+1] |
---|
105 | else: |
---|
106 | path_prefix = '' |
---|
107 | elif any_module_re.match(line) and insert_at_end_of_entry: |
---|
108 | if binary_name_re.match(line): |
---|
109 | #delete existing policy module line |
---|
110 | line='' |
---|
111 | elif any_title_re.match(line): |
---|
112 | within_xen_title = 0 |
---|
113 | |
---|
114 | if (empty_line_re.match(line) or any_title_re.match(line)) and \ |
---|
115 | insert_at_end_of_entry: |
---|
116 | #newline or new title: we insert the policy module line here |
---|
117 | os.write(tmp_fd, "\tmodule " + path_prefix + policy_name + ".bin\n") |
---|
118 | extended_titles.append(this_title) |
---|
119 | insert_at_end_of_entry = 0 |
---|
120 | #write the line that was read (except potential existing policy entry) |
---|
121 | os.write(tmp_fd, line) |
---|
122 | |
---|
123 | if insert_at_end_of_entry: |
---|
124 | #last entry, no empty line at end of file |
---|
125 | os.write(tmp_fd, "\tmodule " + path_prefix + policy_name + ".bin\n") |
---|
126 | extended_titles.append(this_title) |
---|
127 | |
---|
128 | #if more than one entry was changed, abort |
---|
129 | if len(extended_titles) > 1: |
---|
130 | err("Following boot entries matched: %s. \nPlease specify " |
---|
131 | "unique part of the boot title." % extended_titles) |
---|
132 | if len(extended_titles) == 0: |
---|
133 | err("Boot entry not found. Please specify unique part " |
---|
134 | "of the boot title.") |
---|
135 | |
---|
136 | #temp file might be destroyed when closing it, first copy it |
---|
137 | shutil.move(boot_file, boot_file+"_save") |
---|
138 | shutil.copyfile(tmp_grub, boot_file) |
---|
139 | os.close(tmp_fd) |
---|
140 | #sometimes the temp file does not disappear |
---|
141 | try: |
---|
142 | os.remove(tmp_grub) |
---|
143 | except: |
---|
144 | pass |
---|
145 | return extended_titles[0] |
---|
146 | |
---|
147 | |
---|
148 | def main(argv): |
---|
149 | user_kver = None |
---|
150 | user_title = None |
---|
151 | if len(argv) == 2: |
---|
152 | policy = argv[1] |
---|
153 | elif len(argv) == 3: |
---|
154 | policy = argv[1] |
---|
155 | user_title = argv[2] |
---|
156 | else: |
---|
157 | raise OptionError('Invalid number of arguments') |
---|
158 | |
---|
159 | if not policy_name_re.match(policy): |
---|
160 | raise OptionError("Illegal policy name: '%s'" % policy) |
---|
161 | |
---|
162 | policy_file = '/'.join([policy_dir_prefix] + policy.split('.')) |
---|
163 | src_binary_policy_file = policy_file + ".bin" |
---|
164 | #check if .bin exists or if policy file exists |
---|
165 | if not os.path.isfile(src_binary_policy_file): |
---|
166 | if not os.path.isfile(policy_file + "-security_policy.xml"): |
---|
167 | raise OptionError("Unknown policy '%s'" % policy) |
---|
168 | else: |
---|
169 | err_msg = "Cannot find binary file for policy '%s'." % policy |
---|
170 | err_msg += " Please use makepolicy to create binary file." |
---|
171 | raise OptionError(err_msg) |
---|
172 | |
---|
173 | dst_binary_policy_file = "/boot/" + policy + ".bin" |
---|
174 | shutil.copyfile(src_binary_policy_file, dst_binary_policy_file) |
---|
175 | |
---|
176 | entryname = insert_policy(boot_filename, altboot_filename, |
---|
177 | user_title, policy) |
---|
178 | print "Boot entry '%s' extended and \'%s\' copied to /boot" \ |
---|
179 | % (entryname, policy + ".bin") |
---|
180 | |
---|
181 | if __name__ == '__main__': |
---|
182 | try: |
---|
183 | main(sys.argv) |
---|
184 | except Exception, e: |
---|
185 | sys.stderr.write('Error: ' + str(e) + '\n') |
---|
186 | sys.exit(-1) |
---|