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