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