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
26
27INSTALLPATH = '/usr/share/system-config-selinux'
28sys.path.append(INSTALLPATH)
29
30import commands
31ENFORCING = 1
32PERMISSIVE = 0
33DISABLED = -1
34modearray = ("disabled", "permissive", "enforcing")
35
36SELINUXDIR = "/etc/selinux/"
37RELABELFILE = "/.autorelabel"
38
39##
40## I18N
41##
42PROGNAME = "policycoreutils"
43import gettext
44gettext.bindtextdomain(PROGNAME, "/usr/share/locale")
45gettext.textdomain(PROGNAME)
46import selinux
47try:
48    gettext.install(PROGNAME, localedir="/usr/share/locale", unicode=1)
49except IOError:
50    import __builtin__
51    __builtin__.__dict__['_'] = unicode
52
53
54class statusPage:
55
56    def __init__(self, xml):
57        self.xml = xml
58        self.needRelabel = False
59
60        self.type = selinux.selinux_getpolicytype()
61        # Bring in widgets from glade file.
62        self.typeHBox = xml.get_widget("typeHBox")
63        self.selinuxTypeOptionMenu = xml.get_widget("selinuxTypeOptionMenu")
64        self.typeLabel = xml.get_widget("typeLabel")
65        self.enabledOptionMenu = xml.get_widget("enabledOptionMenu")
66        self.currentOptionMenu = xml.get_widget("currentOptionMenu")
67        self.relabel_checkbutton = xml.get_widget("relabelCheckbutton")
68        self.relabel_checkbutton.set_active(self.is_relabel())
69        self.relabel_checkbutton.connect("toggled", self.on_relabel_toggle)
70        if self.get_current_mode() == ENFORCING or self.get_current_mode() == PERMISSIVE:
71            self.currentOptionMenu.append_text(_("Permissive"))
72            self.currentOptionMenu.append_text(_("Enforcing"))
73            self.currentOptionMenu.set_active(self.get_current_mode())
74            self.currentOptionMenu.connect("changed", self.set_current_mode)
75            self.currentOptionMenu.set_sensitive(True)
76        else:
77            self.currentOptionMenu.append_text(_("Disabled"))
78            self.currentOptionMenu.set_active(0)
79            self.currentOptionMenu.set_sensitive(False)
80
81        if self.read_selinux_config() == None:
82            self.selinuxsupport = False
83        else:
84            self.enabledOptionMenu.connect("changed", self.enabled_changed)
85        #
86        # This line must come after read_selinux_config
87        #
88        self.selinuxTypeOptionMenu.connect("changed", self.typemenu_changed)
89
90        self.typeLabel.set_mnemonic_widget(self.selinuxTypeOptionMenu)
91
92    def use_menus(self):
93        return False
94
95    def get_description(self):
96        return _("Status")
97
98    def get_current_mode(self):
99        if selinux.is_selinux_enabled():
100            if selinux.security_getenforce() > 0:
101                return ENFORCING
102            else:
103                return PERMISSIVE
104        else:
105            return DISABLED
106
107    def set_current_mode(self, menu):
108        selinux.security_setenforce(menu.get_active() == 1)
109
110    def is_relabel(self):
111        return os.access(RELABELFILE, os.F_OK) != 0
112
113    def on_relabel_toggle(self, button):
114        if button.get_active():
115            fd = open(RELABELFILE, "w")
116            fd.close()
117        else:
118            if os.access(RELABELFILE, os.F_OK) != 0:
119                os.unlink(RELABELFILE)
120
121    def verify(self, message):
122        dlg = gtk.MessageDialog(None, 0, gtk.MESSAGE_INFO,
123                                gtk.BUTTONS_YES_NO,
124                                message)
125        dlg.set_position(gtk.WIN_POS_MOUSE)
126        dlg.show_all()
127        rc = dlg.run()
128        dlg.destroy()
129        return rc
130
131    def typemenu_changed(self, menu):
132        type = self.get_type()
133        enabled = self.enabledOptionMenu.get_active()
134        if self.initialtype != type:
135            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:
136                menu.set_active(self.typeHistory)
137                return None
138
139            self.relabel_checkbutton.set_active(True)
140
141        self.write_selinux_config(modearray[enabled], type)
142        self.typeHistory = menu.get_active()
143
144    def enabled_changed(self, combo):
145        enabled = combo.get_active()
146        type = self.get_type()
147
148        if self.initEnabled != DISABLED and enabled == DISABLED:
149            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:
150                combo.set_active(self.enabled)
151                return None
152
153        if self.initEnabled == DISABLED and enabled < 2:
154            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:
155                combo.set_active(self.enabled)
156                return None
157            self.relabel_checkbutton.set_active(True)
158
159        self.write_selinux_config(modearray[enabled], type)
160        self.enabled = enabled
161
162    def write_selinux_config(self, enforcing, type):
163        path = selinux.selinux_path() + "config"
164        backup_path = path + ".bck"
165        fd = open(path)
166        lines = fd.readlines()
167        fd.close()
168        fd = open(backup_path, "w")
169        for l in lines:
170            if l.startswith("SELINUX="):
171                fd.write("SELINUX=%s\n" % enforcing)
172                continue
173            if l.startswith("SELINUXTYPE="):
174                fd.write("SELINUXTYPE=%s\n" % type)
175                continue
176            fd.write(l)
177        fd.close()
178        os.rename(backup_path, path)
179
180    def read_selinux_config(self):
181        self.initialtype = selinux.selinux_getpolicytype()[1]
182        try:
183            self.initEnabled = selinux.selinux_getenforcemode()[1]
184        except:
185            self.initEnabled = False
186            pass
187        self.enabled = self.initEnabled
188        self.enabledOptionMenu.set_active(self.enabled + 1)
189
190        self.types = []
191
192        n = 0
193        current = n
194
195        for i in os.listdir(SELINUXDIR):
196            if os.path.isdir(SELINUXDIR + i) and os.path.isdir(SELINUXDIR + i + "/policy"):
197                self.types.append(i)
198                self.selinuxTypeOptionMenu.append_text(i)
199                if i == self.initialtype:
200                    current = n
201                n = n + 1
202        self.selinuxTypeOptionMenu.set_active(current)
203        self.typeHistory = current
204
205        return 0
206
207    def get_type(self):
208        return self.types[self.selinuxTypeOptionMenu.get_active()]
209