• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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#
25import subprocess
26import sys
27import os
28import pwd
29import string
30import getopt
31import selinux
32import seobject
33import gettext
34
35try:
36    gettext.install('policycoreutils')
37except IOError:
38    try:
39        import builtins
40        builtins.__dict__['_'] = str
41    except ImportError:
42        import __builtin__
43        __builtin__.__dict__['_'] = unicode
44
45
46def 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
53def 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
61def 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
103def 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
141def 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
182def 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
227def 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
255def 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
279def 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
294def 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
301def 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
319def 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
346def 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
361def 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
373def 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
390def error(msg):
391    print("%s: %s" % (sys.argv[0], msg))
392    sys.exit(1)
393
394if __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