diff options
-rw-r--r-- | src/msec/plugins/msec.py | 95 | ||||
-rw-r--r-- | src/msec/plugins/network.py | 198 | ||||
-rw-r--r-- | src/msec/plugins/pam.py | 2 |
3 files changed, 201 insertions, 94 deletions
diff --git a/src/msec/plugins/msec.py b/src/msec/plugins/msec.py index fd7204d..fc47aea 100644 --- a/src/msec/plugins/msec.py +++ b/src/msec/plugins/msec.py @@ -1,5 +1,5 @@ #!/usr/bin/plugin -"""Msec plugin for enforcing security settings""" +"""Msec plugin for enforcing local security settings""" # main plugin class name PLUGIN = "msec" @@ -109,12 +109,8 @@ class msec: config.SETTINGS['ROOT_UMASK'] = ("msec.set_root_umask", ['*']) config.SETTINGS['ALLOW_CURDIR_IN_PATH'] = ("msec.allow_curdir_in_path", ['yes', 'no']) config.SETTINGS['WIN_PARTS_UMASK'] = ("msec.set_win_parts_umask", ['*']) - config.SETTINGS['ACCEPT_BOGUS_ERROR_RESPONSES'] = ("msec.accept_bogus_error_responses", ['yes', 'no']) - config.SETTINGS['ACCEPT_BROADCASTED_ICMP_ECHO'] = ("msec.accept_broadcasted_icmp_echo", ['yes', 'no']) - config.SETTINGS['ACCEPT_ICMP_ECHO'] = ("msec.accept_icmp_echo", ['yes', 'no']) config.SETTINGS['ALLOW_AUTOLOGIN'] = ("msec.allow_autologin", ['yes', 'no']) config.SETTINGS['ALLOW_REBOOT'] = ("msec.allow_reboot", ['yes', 'no']) - config.SETTINGS['ALLOW_REMOTE_ROOT_LOGIN'] = ("msec.allow_remote_root_login", ['yes', 'no', 'without-password']) config.SETTINGS['ALLOW_ROOT_LOGIN'] = ("msec.allow_root_login", ['yes', 'no']) config.SETTINGS['ALLOW_USER_LIST'] = ("msec.allow_user_list", ['yes', 'no']) config.SETTINGS['ALLOW_X_CONNECTIONS'] = ("msec.allow_x_connections", ['yes', 'no', 'local']) @@ -124,9 +120,6 @@ class msec: config.SETTINGS['CREATE_SERVER_LINK'] = ("msec.create_server_link", ['no', 'remote', 'local']) config.SETTINGS['ENABLE_AT_CRONTAB'] = ("msec.enable_at_crontab", ['yes', 'no']) config.SETTINGS['ENABLE_CONSOLE_LOG'] = ("msec.enable_console_log", ['yes', 'no']) - config.SETTINGS['ENABLE_DNS_SPOOFING_PROTECTION'] = ("msec.enable_dns_spoofing_protection", ['yes', 'no']) - config.SETTINGS['ENABLE_IP_SPOOFING_PROTECTION'] = ("msec.enable_ip_spoofing_protection", ['yes', 'no']) - config.SETTINGS['ENABLE_LOG_STRANGE_PACKETS'] = ("msec.enable_log_strange_packets", ['yes', 'no']) config.SETTINGS['ENABLE_MSEC_CRON'] = ("msec.enable_msec_cron", ['yes', 'no']) config.SETTINGS['ENABLE_SULOGIN'] = ("msec.enable_sulogin", ['yes', 'no']) config.SETTINGS['SECURE_TMP'] = ("msec.secure_tmp", ['yes', 'no']) @@ -137,7 +130,7 @@ class msec: # system settings for check in ["ENABLE_STARTUP_MSEC", "ENABLE_STARTUP_PERMS", "ENABLE_MSEC_CRON", - "ENABLE_SULOGIN", "ENABLE_AT_CRONTAB", + "ENABLE_SULOGIN", "ENABLE_AT_CRONTAB", "ALLOW_XSERVER_TO_LISTEN", "ALLOW_ROOT_LOGIN", "ALLOW_USER_LIST", "ALLOW_AUTOLOGIN", "ENABLE_CONSOLE_LOG", "CREATE_SERVER_LINK", "ALLOW_XAUTH_FROM_ROOT", "ALLOW_REBOOT", "SHELL_HISTORY_SIZE", "SHELL_TIMEOUT", "USER_UMASK", "ROOT_UMASK", @@ -145,13 +138,6 @@ class msec: ]: config.SETTINGS_SYSTEM.append(check) - # network settings - for check in ["ACCEPT_BOGUS_ERROR_RESPONSES", "ACCEPT_BROADCASTED_ICMP_ECHO", "ACCEPT_ICMP_ECHO", - "ALLOW_REMOTE_ROOT_LOGIN", "ALLOW_X_CONNECTIONS", "ALLOW_XSERVER_TO_LISTEN", - "AUTHORIZE_SERVICES", "ENABLE_DNS_SPOOFING_PROTECTION", "ENABLE_IP_SPOOFING_PROTECTION", - "ENABLE_LOG_STRANGE_PACKETS"]: - config.SETTINGS_NETWORK.append(check) - def create_server_link(self, param): ''' Creates the symlink /etc/security/msec/server to point to /etc/security/msec/server.SERVER_LEVEL. The /etc/security/msec/server is used by chkconfig --add to decide to add a service if it is present in the file during the installation of packages. By default, two presets are provided: local (which only enables local services) and remote (which also enables some remote services considered safe). Note that the allowed services must be placed manually into the server.SERVER_LEVEL files when necessary.''' server = self.configfiles.get_config_file(SERVER) @@ -406,29 +392,6 @@ class msec: self.log.info(_("Forbidding list of users in GDM")) gdmconf.set_shell_variable('Browser', 'false') - def allow_remote_root_login(self, arg): - ''' Allow remote root login via sshd. If yes, login is allowed. If without-password, only public-key authentication logins are allowed. See sshd_config(5) man page for more information.''' - sshd_config = self.configfiles.get_config_file(SSHDCONFIG) - - if not sshd_config.exists(): - return - - val = sshd_config.get_match(PERMIT_ROOT_LOGIN_REGEXP, '@1') - - if val != arg: - if arg == "yes": - self.log.info(_('Allowing remote root login')) - sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP, - 'PermitRootLogin yes', 1) - elif arg == "no": - self.log.info(_('Forbidding remote root login')) - sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP, - 'PermitRootLogin no', 1) - elif arg == "without-password": - self.log.info(_('Allowing remote root login only by passphrase')) - sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP, - 'PermitRootLogin without-password', 1) - def allow_autologin(self, arg): ''' Allow autologin.''' autologin = self.configfiles.get_config_file(AUTOLOGIN) @@ -507,60 +470,6 @@ class msec: hostsdeny.remove_line_matching(ALL_REGEXP, 1) hostsdeny.replace_line_matching(ALL_LOCAL_REGEXP, 'ALL:ALL EXCEPT 127.0.0.1:DENY', 1) - def set_zero_one_variable(self, file, variable, value, one_msg, zero_msg): - ''' Helper function for enable_ip_spoofing_protection, accept_icmp_echo, accept_broadcasted_icmp_echo, - # accept_bogus_error_responses and enable_log_strange_packets.''' - f = self.configfiles.get_config_file(file) - curvalue = f.get_shell_variable(variable) - if value == "yes": - value = "1" - else: - value = "0" - if value != curvalue: - if value == "1": - self.log.info(one_msg) - f.set_shell_variable(variable, 1) - else: - self.log.info(zero_msg) - f.set_shell_variable(variable, 0) - - def enable_ip_spoofing_protection(self, arg, alert=1): - ''' Enable IP spoofing protection.''' - self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.rp_filter', arg, 'Enabling ip spoofing protection', 'Disabling ip spoofing protection') - - def enable_dns_spoofing_protection(self, arg, alert=1): - ''' Enable name resolution spoofing protection.''' - hostconf = self.configfiles.get_config_file(HOSTCONF) - - val = hostconf.get_match('nospoof\s+on') - - if arg: - if not val: - self.log.info(_('Enabling name resolution spoofing protection')) - hostconf.replace_line_matching('nospoof', 'nospoof on', 1) - hostconf.replace_line_matching('spoofalert', 'spoofalert on', (alert != 0)) - else: - if val: - self.log.info(_('Disabling name resolution spoofing protection')) - hostconf.remove_line_matching('nospoof') - hostconf.remove_line_matching('spoofalert') - - def accept_icmp_echo(self, arg): - ''' Accept ICMP echo.''' - self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_all', invert(arg), 'Ignoring icmp echo', 'Accepting icmp echo') - - def accept_broadcasted_icmp_echo(self, arg): - ''' Accept broadcasted ICMP echo.''' - self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_broadcasts', invert(arg), 'Ignoring broadcasted icmp echo', 'Accepting broadcasted icmp echo') - - def accept_bogus_error_responses(self, arg): - ''' Accept bogus IPv4 error messages.''' - self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_ignore_bogus_error_responses', invert(arg), 'Ignoring bogus icmp error responses', 'Accepting bogus icmp error responses') - - def enable_log_strange_packets(self, arg): - ''' Enable logging of strange network packets.''' - self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.log_martians', arg, 'Enabling logging of strange packets', 'Disabling logging of strange packets') - def enable_sulogin(self, arg): ''' Ask for root password when going to single user level (man sulogin(8)).''' inittab = self.configfiles.get_config_file(INITTAB) diff --git a/src/msec/plugins/network.py b/src/msec/plugins/network.py new file mode 100644 index 0000000..8721027 --- /dev/null +++ b/src/msec/plugins/network.py @@ -0,0 +1,198 @@ +#!/usr/bin/plugin +"""Msec plugin for enforcing network security settings""" + +# main plugin class name +PLUGIN = "network" + +# configuration + +import os +import re +import string +import stat +import sys + +import config + +import gettext +# localization +try: + gettext.install('msec') +except IOError: + _ = str + +# list of config files + +ATALLOW = '/etc/at.allow' +AUTOLOGIN = '/etc/sysconfig/autologin' +BASTILLENOLOGIN = '/etc/bastille-no-login' +CRON = '/etc/cron.d/msec' +CRONALLOW = '/etc/cron.allow' +FSTAB = '/etc/fstab' +GDM = '/etc/pam.d/gdm' +GDMCONF = '/etc/X11/gdm/custom.conf' +HALT = '/usr/bin/halt' +HOSTCONF = '/etc/host.conf' +HOSTSDENY = '/etc/hosts.deny' +INITTAB = '/etc/inittab' +ISSUE = '/etc/issue' +ISSUENET = '/etc/issue.net' +KDE = '/etc/pam.d/kde' +KDMRC = '/usr/share/config/kdm/kdmrc' +LILOCONF = '/etc/lilo.conf' +LOGINDEFS = '/etc/login.defs' +MENULST = '/boot/grub/menu.lst' +SHELLCONF = '/etc/security/shell' +MSECBIN = '/usr/sbin/msec' +MSECCRON = '/etc/cron.hourly/msec' +MSEC_XINIT = '/etc/X11/xinit.d/msec' +OPASSWD = '/etc/security/opasswd' +PASSWD = '/etc/pam.d/passwd' +POWEROFF = '/usr/bin/poweroff' +REBOOT = '/usr/bin/reboot' +SECURITYSH = '/usr/share/msec/security.sh' +SERVER = '/etc/security/msec/server' +SHADOW = '/etc/shadow' +SHUTDOWN = '/usr/bin/shutdown' +SHUTDOWNALLOW = '/etc/shutdown.allow' +SSHDCONFIG = '/etc/ssh/sshd_config' +STARTX = '/usr/bin/startx' +SYSCTLCONF = '/etc/sysctl.conf' +SYSLOGCONF = '/etc/syslog.conf' +XDM = '/etc/pam.d/xdm' +XSERVERS = '/etc/X11/xdm/Xservers' +EXPORT = '/root/.xauth/export' + +# regexps +# X server +SECURETTY = '/etc/securetty' +STARTX_REGEXP = '(\s*serverargs=".*) -nolisten tcp(.*")' +XSERVERS_REGEXP = '(\s*[^#]+/usr/bin/X .*) -nolisten tcp(.*)' +GDMCONF_REGEXP = '(\s*command=.*/X.*?) -nolisten tcp(.*)$' +KDMRC_REGEXP = re.compile('(.*?)-nolisten tcp(.*)$') +# ctrl-alt-del +CTRALTDEL_REGEXP = '^ca::ctrlaltdel:/sbin/shutdown.*' +# consolehelper +CONSOLE_HELPER = 'consolehelper' +# ssh PermitRootLogin +PERMIT_ROOT_LOGIN_REGEXP = '^\s*PermitRootLogin\s+(no|yes|without-password|forced-commands-only)' +# tcp_wrappers +ALL_REGEXP = '^ALL:ALL:DENY' +ALL_LOCAL_REGEXP = '^ALL:ALL EXCEPT 127\.0\.0\.1:DENY' +# sulogin +SULOGIN_REGEXP = '~~:S:wait:/sbin/sulogin' + +def invert(param): + """Returns inverse value for param. E.g., yes becomes no, and no becomes yes.""" + if param == "yes": + return "no" + else: + return "yes" +class network: + def __init__(self, log=None, configfiles=None, root=None): + """This plugin is responsible for enforcing network security settings on the machine.""" + self.log = log + self.configfiles = configfiles + self.root = root + + # associate helper commands with files + self.configfiles.add_config_assoc(INITTAB, '/sbin/telinit q') + self.configfiles.add_config_assoc('/etc(?:/rc.d)?/init.d/(.+)', '[ -f /var/lock/subsys/@1 ] && @0 reload') + self.configfiles.add_config_assoc(SYSCTLCONF, '/sbin/sysctl -e -p /etc/sysctl.conf') + self.configfiles.add_config_assoc(SSHDCONFIG, '[ -f /var/lock/subsys/sshd ] && /etc/rc.d/init.d/sshd restart') + self.configfiles.add_config_assoc(LILOCONF, '[ `/usr/sbin/detectloader` = LILO ] && /sbin/lilo') + self.configfiles.add_config_assoc(SYSLOGCONF, '[ -f /var/lock/subsys/syslog ] && service syslog reload') + self.configfiles.add_config_assoc('^/etc/issue$', '/usr/bin/killall mingetty') + + # security options + config.SETTINGS['ACCEPT_BOGUS_ERROR_RESPONSES'] = ("network.accept_bogus_error_responses", ['yes', 'no']) + config.SETTINGS['ACCEPT_BROADCASTED_ICMP_ECHO'] = ("network.accept_broadcasted_icmp_echo", ['yes', 'no']) + config.SETTINGS['ACCEPT_ICMP_ECHO'] = ("network.accept_icmp_echo", ['yes', 'no']) + config.SETTINGS['ALLOW_REMOTE_ROOT_LOGIN'] = ("network.allow_remote_root_login", ['yes', 'no', 'without-password']) + config.SETTINGS['ENABLE_DNS_SPOOFING_PROTECTION'] = ("network.enable_dns_spoofing_protection", ['yes', 'no']) + config.SETTINGS['ENABLE_IP_SPOOFING_PROTECTION'] = ("network.enable_ip_spoofing_protection", ['yes', 'no']) + config.SETTINGS['ENABLE_LOG_STRANGE_PACKETS'] = ("network.enable_log_strange_packets", ['yes', 'no']) + + # network settings + for check in ["ACCEPT_BOGUS_ERROR_RESPONSES", "ACCEPT_BROADCASTED_ICMP_ECHO", "ACCEPT_ICMP_ECHO", + "ALLOW_REMOTE_ROOT_LOGIN", "ALLOW_X_CONNECTIONS", "ALLOW_XSERVER_TO_LISTEN", + "AUTHORIZE_SERVICES", "ENABLE_DNS_SPOOFING_PROTECTION", "ENABLE_IP_SPOOFING_PROTECTION", + "ENABLE_LOG_STRANGE_PACKETS"]: + config.SETTINGS_NETWORK.append(check) + + def allow_remote_root_login(self, arg): + ''' Allow remote root login via sshd. If yes, login is allowed. If without-password, only public-key authentication logins are allowed. See sshd_config(5) man page for more information.''' + sshd_config = self.configfiles.get_config_file(SSHDCONFIG) + + if not sshd_config.exists(): + return + + val = sshd_config.get_match(PERMIT_ROOT_LOGIN_REGEXP, '@1') + + if val != arg: + if arg == "yes": + self.log.info(_('Allowing remote root login')) + sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP, + 'PermitRootLogin yes', 1) + elif arg == "no": + self.log.info(_('Forbidding remote root login')) + sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP, + 'PermitRootLogin no', 1) + elif arg == "without-password": + self.log.info(_('Allowing remote root login only by passphrase')) + sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP, + 'PermitRootLogin without-password', 1) + + def set_zero_one_variable(self, file, variable, value, one_msg, zero_msg): + ''' Helper function for enable_ip_spoofing_protection, accept_icmp_echo, accept_broadcasted_icmp_echo, + # accept_bogus_error_responses and enable_log_strange_packets.''' + f = self.configfiles.get_config_file(file) + curvalue = f.get_shell_variable(variable) + if value == "yes": + value = "1" + else: + value = "0" + if value != curvalue: + if value == "1": + self.log.info(one_msg) + f.set_shell_variable(variable, 1) + else: + self.log.info(zero_msg) + f.set_shell_variable(variable, 0) + + def enable_ip_spoofing_protection(self, arg, alert=1): + ''' Enable IP spoofing protection.''' + self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.rp_filter', arg, 'Enabling ip spoofing protection', 'Disabling ip spoofing protection') + + def enable_dns_spoofing_protection(self, arg, alert=1): + ''' Enable name resolution spoofing protection.''' + hostconf = self.configfiles.get_config_file(HOSTCONF) + + val = hostconf.get_match('nospoof\s+on') + + if arg: + if not val: + self.log.info(_('Enabling name resolution spoofing protection')) + hostconf.replace_line_matching('nospoof', 'nospoof on', 1) + hostconf.replace_line_matching('spoofalert', 'spoofalert on', (alert != 0)) + else: + if val: + self.log.info(_('Disabling name resolution spoofing protection')) + hostconf.remove_line_matching('nospoof') + hostconf.remove_line_matching('spoofalert') + + def accept_icmp_echo(self, arg): + ''' Accept ICMP echo.''' + self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_all', invert(arg), 'Ignoring icmp echo', 'Accepting icmp echo') + + def accept_broadcasted_icmp_echo(self, arg): + ''' Accept broadcasted ICMP echo.''' + self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_broadcasts', invert(arg), 'Ignoring broadcasted icmp echo', 'Accepting broadcasted icmp echo') + + def accept_bogus_error_responses(self, arg): + ''' Accept bogus IPv4 error messages.''' + self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_ignore_bogus_error_responses', invert(arg), 'Ignoring bogus icmp error responses', 'Accepting bogus icmp error responses') + + def enable_log_strange_packets(self, arg): + ''' Enable logging of strange network packets.''' + self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.log_martians', arg, 'Enabling logging of strange packets', 'Disabling logging of strange packets') diff --git a/src/msec/plugins/pam.py b/src/msec/plugins/pam.py index bb04eff..6961a30 100644 --- a/src/msec/plugins/pam.py +++ b/src/msec/plugins/pam.py @@ -1,5 +1,5 @@ #!/usr/bin/python -"""PAM plugin for msec """ +"""Msec plugin for enforcing pam-related settings""" # main plugin class name PLUGIN = "pam" |