selinux/python/chcat/chcat

476 lines
14 KiB
Python
Executable File

#!/usr/bin/python3 -EsI
# Copyright (C) 2005 Red Hat
# see file 'COPYING' for use and warranty information
#
# chcat is a script that allows you modify the Security label on a file
#
# Author: Daniel Walsh <dwalsh@redhat.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# 02111-1307 USA
#
#
import subprocess
import sys
import os
import pwd
import getopt
import selinux
import seobject
PROGNAME = "selinux-python"
try:
import gettext
kwargs = {}
if sys.version_info < (3,):
kwargs['unicode'] = True
t = gettext.translation(PROGNAME,
localedir="/usr/share/locale",
**kwargs,
fallback=True)
_ = t.gettext
except:
try:
import builtins
builtins.__dict__['_'] = str
except ImportError:
import __builtin__
__builtin__.__dict__['_'] = unicode
def errorExit(error):
sys.stderr.write("%s: " % sys.argv[0])
sys.stderr.write("%s\n" % error)
sys.stderr.flush()
sys.exit(1)
def verify_users(users):
for u in users:
try:
pwd.getpwnam(u)
except KeyError:
error("User %s does not exist" % u)
def chcat_user_add(newcat, users):
errors = 0
logins = seobject.loginRecords()
seusers = logins.get_all()
add_ind = 0
verify_users(users)
for u in users:
if u in seusers.keys():
user = seusers[u]
else:
add_ind = 1
user = seusers["__default__"]
serange = user[1].split("-")
cats = []
top = ["s0"]
if len(serange) > 1:
top = serange[1].split(":")
if len(top) > 1:
cats = expandCats(top[1].split(','))
for i in newcat[1:]:
if i not in cats:
cats.append(i)
if len(cats) > 0:
new_serange = "%s-%s:%s" % (serange[0], top[0], ",".join(cats))
else:
new_serange = "%s-%s" % (serange[0], top[0])
if add_ind:
cmd = ["semanage", "login", "-a", "-r", new_serange, "-s", user[0], u]
else:
cmd = ["semanage", "login", "-m", "-r", new_serange, "-s", user[0], u]
try:
subprocess.check_call(cmd, stderr=subprocess.STDOUT, shell=False)
except subprocess.CalledProcessError:
errors += 1
return errors
def chcat_add(orig, newcat, objects, login_ind):
if len(newcat) == 1:
raise ValueError(_("Requires at least one category"))
if login_ind == 1:
return chcat_user_add(newcat, objects)
errors = 0
sensitivity = newcat[0]
cat = newcat[1]
for f in objects:
(rc, c) = selinux.getfilecon(f)
con = c.split(":")[3:]
clist = translate(con)
if sensitivity != clist[0]:
print(_("Can not modify sensitivity levels using '+' on %s") % f)
if len(clist) > 1:
if cat in clist[1:]:
print(_("{target} is already in {category}").format(target=f, category=orig))
continue
clist.append(cat)
cats = clist[1:]
cats.sort()
cat_string = cats[0]
for c in cats[1:]:
cat_string = "%s,%s" % (cat_string, c)
else:
cat_string = cat
cmd = ["chcon", "-l", "%s:%s" % (sensitivity, cat_string), f]
try:
subprocess.check_call(cmd, stderr=subprocess.STDOUT, shell=False)
except subprocess.CalledProcessError:
errors += 1
return errors
def chcat_user_remove(newcat, users):
errors = 0
logins = seobject.loginRecords()
seusers = logins.get_all()
add_ind = 0
verify_users(users)
for u in users:
if u in seusers.keys():
user = seusers[u]
else:
add_ind = 1
user = seusers["__default__"]
serange = user[1].split("-")
cats = []
top = ["s0"]
if len(serange) > 1:
top = serange[1].split(":")
if len(top) > 1:
cats = expandCats(top[1].split(','))
for i in newcat[1:]:
if i in cats:
cats.remove(i)
if len(cats) > 0:
new_serange = "%s-%s:%s" % (serange[0], top[0], ",".join(cats))
else:
new_serange = "%s-%s" % (serange[0], top[0])
if add_ind:
cmd = ["semanage", "login", "-a", "-r", new_serange, "-s", user[0], u]
else:
cmd = ["semanage", "login", "-m", "-r", new_serange, "-s", user[0], u]
try:
subprocess.check_call(cmd, stderr=subprocess.STDOUT, shell=False)
except subprocess.CalledProcessError:
errors += 1
return errors
def chcat_remove(orig, newcat, objects, login_ind):
if len(newcat) == 1:
raise ValueError(_("Requires at least one category"))
if login_ind == 1:
return chcat_user_remove(newcat, objects)
errors = 0
sensitivity = newcat[0]
cat = newcat[1]
for f in objects:
(rc, c) = selinux.getfilecon(f)
con = c.split(":")[3:]
clist = translate(con)
if sensitivity != clist[0]:
print(_("Can not modify sensitivity levels using '+' on %s") % f)
continue
if len(clist) > 1:
if cat not in clist[1:]:
print(_("{target} is not in {category}").format(target=f, category=orig))
continue
clist.remove(cat)
if len(clist) > 1:
cat = clist[1]
for c in clist[2:]:
cat = "%s,%s" % (cat, c)
else:
cat = ""
else:
print(_("{target} is not in {category}").format(target=f, category=orig))
continue
if len(cat) == 0:
new_serange = sensitivity
else:
new_serange = '%s:%s' % (sensitivity, cat)
cmd = ["chcon", "-l", new_serange, f]
try:
subprocess.check_call(cmd, stderr=subprocess.STDOUT, shell=False)
except subprocess.CalledProcessError:
errors += 1
return errors
def chcat_user_replace(newcat, users):
errors = 0
logins = seobject.loginRecords()
seusers = logins.get_all()
add_ind = 0
verify_users(users)
for u in users:
if u in seusers.keys():
user = seusers[u]
else:
add_ind = 1
user = seusers["__default__"]
serange = user[1].split("-")
new_serange = "%s-%s:%s" % (serange[0], newcat[0], ",".join(newcat[1:]))
if new_serange[-1:] == ":":
new_serange = new_serange[:-1]
if add_ind:
cmd = ["semanage", "login", "-a", "-r", new_serange, "-s", user[0], u]
else:
cmd = ["semanage", "login", "-m", "-r", new_serange, "-s", user[0], u]
try:
subprocess.check_call(cmd, stderr=subprocess.STDOUT, shell=False)
except subprocess.CalledProcessError:
errors += 1
return errors
def chcat_replace(newcat, objects, login_ind):
if login_ind == 1:
return chcat_user_replace(newcat, objects)
errors = 0
# newcat[0] is the sensitivity level, newcat[1:] are the categories
if len(newcat) == 1:
new_serange = newcat[0]
else:
new_serange = "%s:%s" % (newcat[0], newcat[1])
for cat in newcat[2:]:
new_serange = '%s,%s' % (new_serange, cat)
cmd = ["chcon", "-l", new_serange] + objects
try:
subprocess.check_call(cmd, stderr=subprocess.STDOUT, shell=False)
except subprocess.CalledProcessError:
errors += 1
return errors
def check_replace(cats):
plus_ind = 0
replace_ind = 0
for c in cats:
if len(c) > 0 and (c[0] == "+" or c[0] == "-"):
if replace_ind:
raise ValueError(_("Can not combine +/- with other types of categories"))
plus_ind = 1
else:
replace_ind = 1
if plus_ind:
raise ValueError(_("Can not combine +/- with other types of categories"))
return replace_ind
def isSensitivity(sensitivity):
if sensitivity[0] == "s" and sensitivity[1:].isdigit() and int(sensitivity[1:]) in range(0, 16):
return 1
else:
return 0
def expandCats(cats):
newcats = []
for c in cats:
for i in c.split(","):
if i.find(".") != -1:
j = i.split(".")
for k in range(int(j[0][1:]), int(j[1][1:]) + 1):
x = ("c%d" % k)
if x not in newcats:
newcats.append(x)
else:
if i not in newcats:
newcats.append(i)
if len(newcats) > 25:
return cats
return newcats
def translate(cats):
newcat = []
if len(cats) == 0:
newcat.append("s0")
return newcat
for c in cats:
(rc, raw) = selinux.selinux_trans_to_raw_context("a:b:c:%s" % c)
rlist = raw.split(":")[3:]
tlist = []
if isSensitivity(rlist[0]) == 0:
tlist.append("s0")
for i in expandCats(rlist):
tlist.append(i)
else:
tlist.append(rlist[0])
for i in expandCats(rlist[1:]):
tlist.append(i)
if len(newcat) == 0:
newcat.append(tlist[0])
else:
if newcat[0] != tlist[0]:
raise ValueError(_("Can not have multiple sensitivities"))
for i in tlist[1:]:
newcat.append(i)
return newcat
def usage():
print(_("Usage %s CATEGORY File ...") % sys.argv[0])
print(_("Usage %s -l CATEGORY user ...") % sys.argv[0])
print(_("Usage %s [[+|-]CATEGORY],...] File ...") % sys.argv[0])
print(_("Usage %s -l [[+|-]CATEGORY],...] user ...") % sys.argv[0])
print(_("Usage %s -d File ...") % sys.argv[0])
print(_("Usage %s -l -d user ...") % sys.argv[0])
print(_("Usage %s -L") % sys.argv[0])
print(_("Usage %s -L -l user") % sys.argv[0])
print(_("Use -- to end option list. For example"))
print(_("chcat -- -CompanyConfidential /docs/businessplan.odt"))
print(_("chcat -l +CompanyConfidential juser"))
sys.exit(1)
def listcats():
fd = open(selinux.selinux_translations_path())
for l in fd.read().split("\n"):
if l.startswith("#"):
continue
if l.find("=") != -1:
rec = l.split("=")
print("%-30s %s" % tuple(rec))
fd.close()
return 0
def listusercats(users):
if len(users) == 0:
try:
users.append(os.getlogin())
except OSError:
users.append(pwd.getpwuid(os.getuid()).pw_name)
verify_users(users)
for u in users:
cats = seobject.translate(selinux.getseuserbyname(u)[2])
cats = cats.split("-")
if len(cats) > 1 and cats[1] != "s0":
print("%s: %s" % (u, cats[1]))
else:
print("%s: %s" % (u, cats[0]))
def error(msg):
print("%s: %s" % (sys.argv[0], msg))
sys.exit(1)
if __name__ == '__main__':
if selinux.is_selinux_mls_enabled() != 1:
error("Requires a mls enabled system")
if selinux.is_selinux_enabled() != 1:
error("Requires an SELinux enabled system")
delete_ind = 0
list_ind = 0
login_ind = 0
try:
gopts, cmds = getopt.getopt(sys.argv[1:],
'dhlL',
['list',
'login',
'help',
'delete'])
for o, a in gopts:
if o == "-h" or o == "--help":
usage()
if o == "-d" or o == "--delete":
delete_ind = 1
if o == "-L" or o == "--list":
list_ind = 1
if o == "-l" or o == "--login":
login_ind = 1
if list_ind == 0 and len(cmds) < 1:
usage()
except getopt.error as error:
errorExit(_("Options Error %s ") % error.msg)
except ValueError:
usage()
if delete_ind:
sys.exit(chcat_replace(["s0"], cmds, login_ind))
if list_ind:
if login_ind:
sys.exit(listusercats(cmds))
else:
if len(cmds) > 0:
usage()
sys.exit(listcats())
if len(cmds) < 2:
usage()
set_ind = 0
cats = cmds[0].split(",")
mod_ind = 0
errors = 0
objects = cmds[1:]
try:
if check_replace(cats):
errors = chcat_replace(translate(cats), objects, login_ind)
else:
for c in cats:
l = []
l.append(c[1:])
if len(c) > 0 and c[0] == "+":
errors += chcat_add(c[1:], translate(l), objects, login_ind)
continue
if len(c) > 0 and c[0] == "-":
errors += chcat_remove(c[1:], translate(l), objects, login_ind)
continue
except ValueError as e:
error(e)
except OSError as e:
error(e)
sys.exit(errors)