Home | History | Annotate | Download | only in chcat
      1 #! /usr/bin/python -Es
      2 # Copyright (C) 2005 Red Hat
      3 # see file 'COPYING' for use and warranty information
      4 #
      5 #    chcat is a script that allows you modify the Security label on a file
      6 #
      7 #`   Author: Daniel Walsh <dwalsh (at] redhat.com>
      8 #
      9 #    This program is free software; you can redistribute it and/or
     10 #    modify it under the terms of the GNU General Public License as
     11 #    published by the Free Software Foundation; either version 2 of
     12 #    the License, or (at your option) any later version.
     13 #
     14 #    This program is distributed in the hope that it will be useful,
     15 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 #    GNU General Public License for more details.
     18 #
     19 #    You should have received a copy of the GNU General Public License
     20 #    along with this program; if not, write to the Free Software
     21 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
     22 #                                        02111-1307  USA
     23 #
     24 #
     25 try:
     26     from subprocess import getstatusoutput
     27 except ImportError:
     28     from commands import getstatusoutput
     29 import sys
     30 import os
     31 import pwd
     32 import string
     33 import getopt
     34 import selinux
     35 import seobject
     36 
     37 PROGNAME = "policycoreutils"
     38 try:
     39     import gettext
     40     kwargs = {}
     41     if sys.version_info < (3,):
     42         kwargs['unicode'] = True
     43     gettext.install(PROGNAME,
     44                     localedir="/usr/share/locale",
     45                     codeset='utf-8',
     46                     **kwargs)
     47 except:
     48     try:
     49         import builtins
     50         builtins.__dict__['_'] = str
     51     except ImportError:
     52         import __builtin__
     53         __builtin__.__dict__['_'] = unicode
     54 
     55 
     56 def errorExit(error):
     57     sys.stderr.write("%s: " % sys.argv[0])
     58     sys.stderr.write("%s\n" % error)
     59     sys.stderr.flush()
     60     sys.exit(1)
     61 
     62 
     63 def verify_users(users):
     64     for u in users:
     65         try:
     66             pwd.getpwnam(u)
     67         except KeyError:
     68             error("User %s does not exist" % u)
     69 
     70 
     71 def chcat_user_add(newcat, users):
     72     errors = 0
     73     logins = seobject.loginRecords()
     74     seusers = logins.get_all()
     75     add_ind = 0
     76     verify_users(users)
     77     for u in users:
     78         if u in seusers.keys():
     79             user = seusers[u]
     80         else:
     81             add_ind = 1
     82             user = seusers["__default__"]
     83         serange = user[1].split("-")
     84         cats = []
     85         top = ["s0"]
     86         if len(serange) > 1:
     87             top = serange[1].split(":")
     88             if len(top) > 1:
     89                 cats.append(top[1])
     90                 cats = expandCats(cats)
     91 
     92         for i in newcat[1:]:
     93             if i not in cats:
     94                 cats.append(i)
     95 
     96         if len(cats) > 0:
     97             new_serange = "%s-%s:%s" % (serange[0], top[0], ",".join(cats))
     98         else:
     99             new_serange = "%s-%s" % (serange[0], top[0])
    100 
    101         if add_ind:
    102             cmd = "semanage login -a -r %s -s %s %s" % (new_serange, user[0], u)
    103         else:
    104             cmd = "semanage login -m -r %s -s %s %s" % (new_serange, user[0], u)
    105         rc = getstatusoutput(cmd)
    106         if rc[0] != 0:
    107             print(rc[1])
    108             errors += 1
    109 
    110     return errors
    111 
    112 
    113 def chcat_add(orig, newcat, objects, login_ind):
    114     if len(newcat) == 1:
    115         raise ValueError(_("Requires at least one category"))
    116 
    117     if login_ind == 1:
    118         return chcat_user_add(newcat, objects)
    119 
    120     errors = 0
    121     sensitivity = newcat[0]
    122     cat = newcat[1]
    123     cmd = 'chcon -l %s' % sensitivity
    124     for f in objects:
    125         (rc, c) = selinux.getfilecon(f)
    126         con = c.split(":")[3:]
    127         clist = translate(con)
    128         if sensitivity != clist[0]:
    129             print(_("Can not modify sensitivity levels using '+' on %s") % f)
    130 
    131         if len(clist) > 1:
    132             if cat in clist[1:]:
    133                 print(_("%s is already in %s") % (f, orig))
    134                 continue
    135             clist.append(cat)
    136             cats = clist[1:]
    137             cats.sort()
    138             cat_string = cats[0]
    139             for c in cats[1:]:
    140                 cat_string = "%s,%s" % (cat_string, c)
    141         else:
    142             cat_string = cat
    143         cmd = 'chcon -l %s:%s %s' % (sensitivity, cat_string, f)
    144         rc = getstatusoutput(cmd)
    145         if rc[0] != 0:
    146             print(rc[1])
    147             errors += 1
    148     return errors
    149 
    150 
    151 def chcat_user_remove(newcat, users):
    152     errors = 0
    153     logins = seobject.loginRecords()
    154     seusers = logins.get_all()
    155     add_ind = 0
    156     verify_users(users)
    157     for u in users:
    158         if u in seusers.keys():
    159             user = seusers[u]
    160         else:
    161             add_ind = 1
    162             user = seusers["__default__"]
    163         serange = user[1].split("-")
    164         cats = []
    165         top = ["s0"]
    166         if len(serange) > 1:
    167             top = serange[1].split(":")
    168             if len(top) > 1:
    169                 cats.append(top[1])
    170                 cats = expandCats(cats)
    171 
    172         for i in newcat[1:]:
    173             if i in cats:
    174                 cats.remove(i)
    175 
    176         if len(cats) > 0:
    177             new_serange = "%s-%s:%s" % (serange[0], top[0], ",".join(cats))
    178         else:
    179             new_serange = "%s-%s" % (serange[0], top[0])
    180 
    181         if add_ind:
    182             cmd = "semanage login -a -r %s -s %s %s" % (new_serange, user[0], u)
    183         else:
    184             cmd = "semanage login -m -r %s -s %s %s" % (new_serange, user[0], u)
    185         rc = getstatusoutput(cmd)
    186         if rc[0] != 0:
    187             print(rc[1])
    188             errors += 1
    189     return errors
    190 
    191 
    192 def chcat_remove(orig, newcat, objects, login_ind):
    193     if len(newcat) == 1:
    194         raise ValueError(_("Requires at least one category"))
    195 
    196     if login_ind == 1:
    197         return chcat_user_remove(newcat, objects)
    198 
    199     errors = 0
    200     sensitivity = newcat[0]
    201     cat = newcat[1]
    202 
    203     for f in objects:
    204         (rc, c) = selinux.getfilecon(f)
    205         con = c.split(":")[3:]
    206         clist = translate(con)
    207         if sensitivity != clist[0]:
    208             print(_("Can not modify sensitivity levels using '+' on %s") % f)
    209             continue
    210 
    211         if len(clist) > 1:
    212             if cat not in clist[1:]:
    213                 print(_("%s is not in %s") % (f, orig))
    214                 continue
    215             clist.remove(cat)
    216             if len(clist) > 1:
    217                 cat = clist[1]
    218                 for c in clist[2:]:
    219                     cat = "%s,%s" % (cat, c)
    220             else:
    221                 cat = ""
    222         else:
    223             print(_("%s is not in %s") % (f, orig))
    224             continue
    225 
    226         if len(cat) == 0:
    227             cmd = 'chcon -l %s %s' % (sensitivity, f)
    228         else:
    229             cmd = 'chcon -l %s:%s %s' % (sensitivity, cat, f)
    230         rc = getstatusoutput(cmd)
    231         if rc[0] != 0:
    232             print(rc[1])
    233             errors += 1
    234     return errors
    235 
    236 
    237 def chcat_user_replace(newcat, users):
    238     errors = 0
    239     logins = seobject.loginRecords()
    240     seusers = logins.get_all()
    241     add_ind = 0
    242     verify_users(users)
    243     for u in users:
    244         if u in seusers.keys():
    245             user = seusers[u]
    246         else:
    247             add_ind = 1
    248             user = seusers["__default__"]
    249         serange = user[1].split("-")
    250         new_serange = "%s-%s:%s" % (serange[0], newcat[0], string.join(newcat[1:], ","))
    251         if new_serange[-1:] == ":":
    252             new_serange = new_serange[:-1]
    253 
    254         if add_ind:
    255             cmd = "semanage login -a -r %s -s %s %s" % (new_serange, user[0], u)
    256         else:
    257             cmd = "semanage login -m -r %s -s %s %s" % (new_serange, user[0], u)
    258         rc = getstatusoutput(cmd)
    259         if rc[0] != 0:
    260             print(rc[1])
    261             errors += 1
    262     return errors
    263 
    264 
    265 def chcat_replace(newcat, objects, login_ind):
    266     if login_ind == 1:
    267         return chcat_user_replace(newcat, objects)
    268     errors = 0
    269     if len(newcat) == 1:
    270         sensitivity = newcat[0]
    271         cmd = 'chcon -l %s ' % newcat[0]
    272     else:
    273         sensitivity = newcat[0]
    274         cmd = 'chcon -l %s:%s' % (sensitivity, newcat[1])
    275         for cat in newcat[2:]:
    276             cmd = '%s,%s' % (cmd, cat)
    277 
    278     for f in objects:
    279         cmd = "%s %s" % (cmd, f)
    280 
    281     rc = getstatusoutput(cmd)
    282     if rc[0] != 0:
    283         print(rc[1])
    284         errors += 1
    285 
    286     return errors
    287 
    288 
    289 def check_replace(cats):
    290     plus_ind = 0
    291     replace_ind = 0
    292     for c in cats:
    293         if len(c) > 0 and (c[0] == "+" or c[0] == "-"):
    294             if replace_ind:
    295                 raise ValueError(_("Can not combine +/- with other types of categories"))
    296             plus_ind = 1
    297         else:
    298             replace_ind = 1
    299             if plus_ind:
    300                 raise ValueError(_("Can not combine +/- with other types of categories"))
    301     return replace_ind
    302 
    303 
    304 def isSensitivity(sensitivity):
    305     if sensitivity[0] == "s" and sensitivity[1:].isdigit() and int(sensitivity[1:]) in range(0, 16):
    306         return 1
    307     else:
    308         return 0
    309 
    310 
    311 def expandCats(cats):
    312     newcats = []
    313     for c in cats:
    314         for i in c.split(","):
    315             if i.find(".") != -1:
    316                 j = i.split(".")
    317                 for k in range(int(j[0][1:]), int(j[1][1:]) + 1):
    318                     x = ("c%d" % k)
    319                     if x not in newcats:
    320                         newcats.append(x)
    321             else:
    322                 if i not in newcats:
    323                     newcats.append(i)
    324     if len(newcats) > 25:
    325         return cats
    326     return newcats
    327 
    328 
    329 def translate(cats):
    330     newcat = []
    331     if len(cats) == 0:
    332         newcat.append("s0")
    333         return newcat
    334     for c in cats:
    335         (rc, raw) = selinux.selinux_trans_to_raw_context("a:b:c:%s" % c)
    336         rlist = raw.split(":")[3:]
    337         tlist = []
    338         if isSensitivity(rlist[0]) == 0:
    339             tlist.append("s0")
    340             for i in expandCats(rlist):
    341                 tlist.append(i)
    342         else:
    343             tlist.append(rlist[0])
    344             for i in expandCats(rlist[1:]):
    345                 tlist.append(i)
    346         if len(newcat) == 0:
    347             newcat.append(tlist[0])
    348         else:
    349             if newcat[0] != tlist[0]:
    350                 raise ValueError(_("Can not have multiple sensitivities"))
    351         for i in tlist[1:]:
    352             newcat.append(i)
    353     return newcat
    354 
    355 
    356 def usage():
    357     print(_("Usage %s CATEGORY File ...") % sys.argv[0])
    358     print(_("Usage %s -l CATEGORY user ...") % sys.argv[0])
    359     print(_("Usage %s [[+|-]CATEGORY],...] File ...") % sys.argv[0])
    360     print(_("Usage %s -l [[+|-]CATEGORY],...] user ...") % sys.argv[0])
    361     print(_("Usage %s -d File ...") % sys.argv[0])
    362     print(_("Usage %s -l -d user ...") % sys.argv[0])
    363     print(_("Usage %s -L") % sys.argv[0])
    364     print(_("Usage %s -L -l user") % sys.argv[0])
    365     print(_("Use -- to end option list.  For example"))
    366     print(_("chcat -- -CompanyConfidential /docs/businessplan.odt"))
    367     print(_("chcat -l +CompanyConfidential juser"))
    368     sys.exit(1)
    369 
    370 
    371 def listcats():
    372     fd = open(selinux.selinux_translations_path())
    373     for l in fd.read().split("\n"):
    374         if l.startswith("#"):
    375             continue
    376         if l.find("=") != -1:
    377             rec = l.split("=")
    378             print("%-30s %s" % tuple(rec))
    379     fd.close()
    380     return 0
    381 
    382 
    383 def listusercats(users):
    384     if len(users) == 0:
    385         try:
    386             users.append(os.getlogin())
    387         except:
    388             users.append(pwd.getpwuid(os.getuid()).pw_name)
    389 
    390     verify_users(users)
    391     for u in users:
    392         cats = seobject.translate(selinux.getseuserbyname(u)[2])
    393         cats = cats.split("-")
    394         if len(cats) > 1 and cats[1] != "s0":
    395             print("%s: %s" % (u, cats[1]))
    396         else:
    397             print("%s: %s" % (u, cats[0]))
    398 
    399 
    400 def error(msg):
    401     print("%s: %s" % (sys.argv[0], msg))
    402     sys.exit(1)
    403 
    404 if __name__ == '__main__':
    405     if selinux.is_selinux_mls_enabled() != 1:
    406         error("Requires a mls enabled system")
    407 
    408     if selinux.is_selinux_enabled() != 1:
    409         error("Requires an SELinux enabled system")
    410 
    411     delete_ind = 0
    412     list_ind = 0
    413     login_ind = 0
    414     try:
    415         gopts, cmds = getopt.getopt(sys.argv[1:],
    416                                     'dhlL',
    417                                     ['list',
    418                                      'login',
    419                                      'help',
    420                                      'delete'])
    421 
    422         for o, a in gopts:
    423             if o == "-h" or o == "--help":
    424                 usage()
    425             if o == "-d" or o == "--delete":
    426                 delete_ind = 1
    427             if o == "-L" or o == "--list":
    428                 list_ind = 1
    429             if o == "-l" or o == "--login":
    430                 login_ind = 1
    431 
    432         if list_ind == 0 and len(cmds) < 1:
    433             usage()
    434 
    435     except getopt.error as error:
    436         errorExit(_("Options Error %s ") % error.msg)
    437 
    438     except ValueError as e:
    439         usage()
    440 
    441     if delete_ind:
    442         sys.exit(chcat_replace(["s0"], cmds, login_ind))
    443 
    444     if list_ind:
    445         if login_ind:
    446             sys.exit(listusercats(cmds))
    447         else:
    448             if len(cmds) > 0:
    449                 usage()
    450             sys.exit(listcats())
    451 
    452     if len(cmds) < 2:
    453         usage()
    454 
    455     set_ind = 0
    456     cats = cmds[0].split(",")
    457     mod_ind = 0
    458     errors = 0
    459     objects = cmds[1:]
    460     try:
    461         if check_replace(cats):
    462             errors = chcat_replace(translate(cats), objects, login_ind)
    463         else:
    464             for c in cats:
    465                 l = []
    466                 l.append(c[1:])
    467                 if len(c) > 0 and c[0] == "+":
    468                     errors += chcat_add(c[1:], translate(l), objects, login_ind)
    469                     continue
    470                 if len(c) > 0 and c[0] == "-":
    471                     errors += chcat_remove(c[1:], translate(l), objects, login_ind)
    472                     continue
    473     except ValueError as e:
    474         error(e)
    475     except OSError as e:
    476         error(e)
    477 
    478     sys.exit(errors)
    479