1# statusPage.py - show selinux status
2## Copyright (C) 2006-2009 Red Hat, Inc.
3
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18## Author: Dan Walsh
19import string
20import gtk
21import gtk.glade
22import os
23import gobject
24import sys
25import tempfile
26import selinux
27
28INSTALLPATH = '/usr/share/system-config-selinux'
29sys.path.append(INSTALLPATH)
30
31try:
32    from subprocess import getstatusoutput
33except ImportError:
34    from commands import getstatusoutput
35
36ENFORCING = 1
37PERMISSIVE = 0
38DISABLED = -1
39modearray = ("disabled", "permissive", "enforcing")
40
41SELINUXDIR = "/etc/selinux/"
42RELABELFILE = "/.autorelabel"
43
44##
45## I18N
46##
47PROGNAME = "policycoreutils"
48try:
49    import gettext
50    kwargs = {}
51    if sys.version_info < (3,):
52        kwargs['unicode'] = True
53    gettext.install(PROGNAME,
54                    localedir="/usr/share/locale",
55                    codeset='utf-8',
56                    **kwargs)
57except:
58    try:
59        import builtins
60        builtins.__dict__['_'] = str
61    except ImportError:
62        import __builtin__
63        __builtin__.__dict__['_'] = unicode
64
65
66class statusPage:
67
68    def __init__(self, xml):
69        self.xml = xml
70        self.needRelabel = False
71
72        self.type = selinux.selinux_getpolicytype()
73        # Bring in widgets from glade file.
74        self.typeHBox = xml.get_widget("typeHBox")
75        self.selinuxTypeOptionMenu = xml.get_widget("selinuxTypeOptionMenu")
76        self.typeLabel = xml.get_widget("typeLabel")
77        self.enabledOptionMenu = xml.get_widget("enabledOptionMenu")
78        self.currentOptionMenu = xml.get_widget("currentOptionMenu")
79        self.relabel_checkbutton = xml.get_widget("relabelCheckbutton")
80        self.relabel_checkbutton.set_active(self.is_relabel())
81        self.relabel_checkbutton.connect("toggled", self.on_relabel_toggle)
82        if self.get_current_mode() == ENFORCING or self.get_current_mode() == PERMISSIVE:
83            self.currentOptionMenu.append_text(_("Permissive"))
84            self.currentOptionMenu.append_text(_("Enforcing"))
85            self.currentOptionMenu.set_active(self.get_current_mode())
86            self.currentOptionMenu.connect("changed", self.set_current_mode)
87            self.currentOptionMenu.set_sensitive(True)
88        else:
89            self.currentOptionMenu.append_text(_("Disabled"))
90            self.currentOptionMenu.set_active(0)
91            self.currentOptionMenu.set_sensitive(False)
92
93        if self.read_selinux_config() == None:
94            self.selinuxsupport = False
95        else:
96            self.enabledOptionMenu.connect("changed", self.enabled_changed)
97        #
98        # This line must come after read_selinux_config
99        #
100        self.selinuxTypeOptionMenu.connect("changed", self.typemenu_changed)
101
102        self.typeLabel.set_mnemonic_widget(self.selinuxTypeOptionMenu)
103
104    def use_menus(self):
105        return False
106
107    def get_description(self):
108        return _("Status")
109
110    def get_current_mode(self):
111        if selinux.is_selinux_enabled():
112            if selinux.security_getenforce() > 0:
113                return ENFORCING
114            else:
115                return PERMISSIVE
116        else:
117            return DISABLED
118
119    def set_current_mode(self, menu):
120        selinux.security_setenforce(menu.get_active() == 1)
121
122    def is_relabel(self):
123        return os.access(RELABELFILE, os.F_OK) != 0
124
125    def on_relabel_toggle(self, button):
126        if button.get_active():
127            fd = open(RELABELFILE, "w")
128            fd.close()
129        else:
130            if os.access(RELABELFILE, os.F_OK) != 0:
131                os.unlink(RELABELFILE)
132
133    def verify(self, message):
134        dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO,
135                                gtk.BUTTONS_YES_NO,
136                                message)
137        dlg.set_position(gtk.WIN_POS_MOUSE)
138        dlg.show_all()
139        rc = dlg.run()
140        dlg.destroy()
141        return rc
142
143    def typemenu_changed(self, menu):
144        type = self.get_type()
145        enabled = self.enabledOptionMenu.get_active()
146        if self.initialtype != type:
147            if self.verify(_("Changing the policy type will cause a relabel of the entire file system on the next boot. Relabeling takes a long time depending on the size of the file system.  Do you wish to continue?")) == gtk.RESPONSE_NO:
148                menu.set_active(self.typeHistory)
149                return None
150
151            self.relabel_checkbutton.set_active(True)
152
153        self.write_selinux_config(modearray[enabled], type)
154        self.typeHistory = menu.get_active()
155
156    def enabled_changed(self, combo):
157        enabled = combo.get_active()
158        type = self.get_type()
159
160        if self.initEnabled != DISABLED and enabled == DISABLED:
161            if self.verify(_("Changing to SELinux disabled requires a reboot.  It is not recommended.  If you later decide to turn SELinux back on, the system will be required to relabel.  If you just want to see if SELinux is causing a problem on your system, you can go to permissive mode which will only log errors and not enforce SELinux policy.  Permissive mode does not require a reboot    Do you wish to continue?")) == gtk.RESPONSE_NO:
162                combo.set_active(self.enabled)
163                return None
164
165        if self.initEnabled == DISABLED and enabled < 2:
166            if self.verify(_("Changing to SELinux enabled will cause a relabel of the entire file system on the next boot. Relabeling takes a long time depending on the size of the file system.  Do you wish to continue?")) == gtk.RESPONSE_NO:
167                combo.set_active(self.enabled)
168                return None
169            self.relabel_checkbutton.set_active(True)
170
171        self.write_selinux_config(modearray[enabled], type)
172        self.enabled = enabled
173
174    def write_selinux_config(self, enforcing, type):
175        path = selinux.selinux_path() + "config"
176        backup_path = path + ".bck"
177        fd = open(path)
178        lines = fd.readlines()
179        fd.close()
180        fd = open(backup_path, "w")
181        for l in lines:
182            if l.startswith("SELINUX="):
183                fd.write("SELINUX=%s\n" % enforcing)
184                continue
185            if l.startswith("SELINUXTYPE="):
186                fd.write("SELINUXTYPE=%s\n" % type)
187                continue
188            fd.write(l)
189        fd.close()
190        os.rename(backup_path, path)
191
192    def read_selinux_config(self):
193        self.initialtype = selinux.selinux_getpolicytype()[1]
194        try:
195            self.initEnabled = selinux.selinux_getenforcemode()[1]
196        except:
197            self.initEnabled = False
198            pass
199        self.enabled = self.initEnabled
200        self.enabledOptionMenu.set_active(self.enabled + 1)
201
202        self.types = []
203
204        n = 0
205        current = n
206
207        for i in os.listdir(SELINUXDIR):
208            if os.path.isdir(SELINUXDIR + i) and os.path.isdir(SELINUXDIR + i + "/policy"):
209                self.types.append(i)
210                self.selinuxTypeOptionMenu.append_text(i)
211                if i == self.initialtype:
212                    current = n
213                n = n + 1
214        self.selinuxTypeOptionMenu.set_active(current)
215        self.typeHistory = current
216
217        return 0
218
219    def get_type(self):
220        return self.types[self.selinuxTypeOptionMenu.get_active()]
221