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