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