selinux/python/chcat/chcat
Nicolas Iooss 72dc5c6241
python: always use python3 in the shebang of programs using setools
setools 4.2.0 dropped support for Python 2. On systems where
/usr/bin/python is Python 2, several tools are now broken because of
this. Update the shebang of these tools to /usr/bin/python3.

For future reference, as semanage/seobject.py, sepolicy and sepolgen
import setools, every program that uses one of these modules need to be
run with Python 3. The following programs do not use any of these
modules so their shebangs have not been modified:

    dbus/selinux_server.py
    libsemanage/utils/semanage_migrate_store
    mcstrans/share/util/mlscolor-test
    mcstrans/share/util/mlstrans-test
    sandbox/start

Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
2019-02-17 22:34:50 +01:00

476 lines
14 KiB
Python
Executable File

#!/usr/bin/python3 -Es
# 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 = "policycoreutils"
try:
import gettext
kwargs = {}
if sys.version_info < (3,):
kwargs['unicode'] = True
gettext.install(PROGNAME,
localedir="/usr/share/locale",
codeset='utf-8',
**kwargs)
except ImportError:
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]
cmd = 'chcon -l %s' % sensitivity
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(_("%s is already in %s") % (f, 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(_("%s is not in %s") % (f, 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(_("%s is not in %s") % (f, 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)