1
# Copyright 2016 Canonical Limited.
3
# This file is part of charm-helpers.
5
# charm-helpers is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU Lesser General Public License version 3 as
7
# published by the Free Software Foundation.
9
# charm-helpers is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU Lesser General Public License for more details.
14
# You should have received a copy of the GNU Lesser General Public License
15
# along with charm-helpers. If not, see <http://www.gnu.org/licenses/>.
19
from charmhelpers.core.hookenv import (
23
from charmhelpers.contrib.hardening.audits.file import NoSUIDSGIDAudit
24
from charmhelpers.contrib.hardening import utils
27
BLACKLIST = ['/usr/bin/rcp', '/usr/bin/rlogin', '/usr/bin/rsh',
28
'/usr/libexec/openssh/ssh-keysign',
29
'/usr/lib/openssh/ssh-keysign',
31
'/usr/sbin/usernetctl',
32
'/usr/sbin/userisdnctl',
36
'/usr/bin/mail-unlock',
37
'/usr/bin/mail-touchlock',
38
'/usr/bin/dotlockfile',
42
'/usr/lib/evolution/camel-lock-helper-1.2',
44
'/usr/lib/eject/dmcrypt-get-device',
45
'/usr/lib/mc/cons.saver']
47
WHITELIST = ['/bin/mount', '/bin/ping', '/bin/su', '/bin/umount',
48
'/sbin/pam_timestamp_check', '/sbin/unix_chkpwd', '/usr/bin/at',
49
'/usr/bin/gpasswd', '/usr/bin/locate', '/usr/bin/newgrp',
50
'/usr/bin/passwd', '/usr/bin/ssh-agent',
51
'/usr/libexec/utempter/utempter', '/usr/sbin/lockdev',
52
'/usr/sbin/sendmail.sendmail', '/usr/bin/expiry',
53
'/bin/ping6', '/usr/bin/traceroute6.iputils',
54
'/sbin/mount.nfs', '/sbin/umount.nfs',
55
'/sbin/mount.nfs4', '/sbin/umount.nfs4',
57
'/usr/bin/wall', '/usr/bin/write',
60
'/usr/bin/chage', '/usr/bin/chfn', '/usr/bin/chsh',
63
'/usr/bin/sudo', '/usr/bin/sudoedit',
64
'/usr/sbin/postdrop', '/usr/sbin/postqueue',
66
'/usr/lib/squid/ncsa_auth', '/usr/lib/squid/pam_auth',
67
'/usr/kerberos/bin/ksu',
68
'/usr/sbin/ccreds_validate',
71
'/usr/lib/dbus-1.0/dbus-daemon-launch-helper',
72
'/usr/lib/vte/gnome-pty-helper',
73
'/usr/lib/libvte9/gnome-pty-helper',
74
'/usr/lib/libvte-2.90-9/gnome-pty-helper']
78
"""Get OS hardening suid/sgid audits.
80
:returns: dictionary of audits
83
settings = utils.get_settings('os')
84
if not settings['security']['suid_sgid_enforce']:
85
log("Skipping suid/sgid hardening", level=INFO)
88
# Build the blacklist and whitelist of files for suid/sgid checks.
89
# There are a total of 4 lists:
90
# 1. the system blacklist
91
# 2. the system whitelist
92
# 3. the user blacklist
93
# 4. the user whitelist
95
# The blacklist is the set of paths which should NOT have the suid/sgid bit
96
# set and the whitelist is the set of paths which MAY have the suid/sgid
97
# bit setl. The user whitelist/blacklist effectively override the system
98
# whitelist/blacklist.
99
u_b = settings['security']['suid_sgid_blacklist']
100
u_w = settings['security']['suid_sgid_whitelist']
102
blacklist = set(BLACKLIST) - set(u_w + u_b)
103
whitelist = set(WHITELIST) - set(u_b + u_w)
105
checks.append(NoSUIDSGIDAudit(blacklist))
107
dry_run = settings['security']['suid_sgid_dry_run_on_unknown']
109
if settings['security']['suid_sgid_remove_from_unknown'] or dry_run:
110
# If the policy is a dry_run (e.g. complain only) or remove unknown
111
# suid/sgid bits then find all of the paths which have the suid/sgid
112
# bit set and then remove the whitelisted paths.
113
root_path = settings['environment']['root_path']
114
unknown_paths = find_paths_with_suid_sgid(root_path) - set(whitelist)
115
checks.append(NoSUIDSGIDAudit(unknown_paths, unless=dry_run))
120
def find_paths_with_suid_sgid(root_path):
121
"""Finds all paths/files which have an suid/sgid bit enabled.
123
Starting with the root_path, this will recursively find all paths which
124
have an suid or sgid bit set.
126
cmd = ['find', root_path, '-perm', '-4000', '-o', '-perm', '-2000',
127
'-type', 'f', '!', '-path', '/proc/*', '-print']
129
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
130
out, _ = p.communicate()
131
return set(out.split('\n'))