• 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#
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