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@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# 25try: 26 from subprocess import getstatusoutput 27except ImportError: 28 from commands import getstatusoutput 29import sys 30import os 31import pwd 32import string 33import getopt 34import selinux 35import seobject 36 37PROGNAME = "policycoreutils" 38try: 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) 47except: 48 try: 49 import builtins 50 builtins.__dict__['_'] = str 51 except ImportError: 52 import __builtin__ 53 __builtin__.__dict__['_'] = unicode 54 55 56def 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 63def 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 71def 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 113def 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 151def 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 192def 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 237def 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 265def 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 289def 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 304def 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 311def 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 329def 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 356def 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 371def 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 383def 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 400def error(msg): 401 print("%s: %s" % (sys.argv[0], msg)) 402 sys.exit(1) 403 404if __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