diff options
Diffstat (limited to 'share/libmsec.py')
-rw-r--r-- | share/libmsec.py | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/share/libmsec.py b/share/libmsec.py new file mode 100644 index 0000000..330244a --- /dev/null +++ b/share/libmsec.py @@ -0,0 +1,488 @@ +#--------------------------------------------------------------- +# Project : Mandrake Linux +# Module : share +# File : libmsec.py +# Version : $Id$ +# Author : Frederic Lepied +# Created On : Mon Dec 10 22:52:17 2001 +#--------------------------------------------------------------- + +import ConfigFile +import Config +from Log import * +import os +import grp +import Perms +import gettext +import pwd +import re +import string +import commands + +try: + cat = gettext.Catalog('msec') + _ = cat.gettext +except IOError: + _ = str + +SUFFIX='.msec' +_interactive=0 + +# 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' +GDM = '/etc/pam.d/gdm' +GDMCONF = '/etc/X11/gdm/gdm.conf' +HALT = '/etc/security/console.apps/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' +LDSOPRELOAD = '/etc/ld.so.preload' +LILOCONF = '/etc/lilo.conf' +LOGINDEFS = '/etc/login.defs' +MENULST = '/boot/grub/menu.lst' +MSEC = '/etc/sysconfig/msec' +MSECBIN = '/usr/sbin/msec' +MSECCRON = '/etc/cron.hourly/msec' +MSEC_XINIT = '/etc/X11/xinit.d/msec' +PASSWD = '/etc/pam.d/passwd' +POWEROFF = '/etc/security/console.apps/poweroff' +REBOOT = '/etc/security/console.apps/reboot' +SECURETTY = '/etc/securetty' +SHADOW = '/etc/shadow' +SHUTDOWN = '/etc/security/console.apps/shutdown' +SHUTDOWNALLOW = '/etc/shutdown.allow' +SSHDCONFIG = '/etc/ssh/sshd_config' +SU = '/etc/pam.d/su' +SYSCTLCONF = '/etc/sysctl.conf' +SYSLOGCONF = '/etc/syslog.conf' +XDM = '/etc/pam.d/xdm' + +# config files => actions + +ConfigFile.add_config_assoc(INITTAB, '/sbin/telinit q') +ConfigFile.add_config_assoc('/etc(?:/rc.d)?/init.d/(.+)', '[ -f /var/lock/subsys/@1 ] && @0 reload') +ConfigFile.add_config_assoc(SYSCTLCONF, '/sbin/sysctl -e -p /etc/sysctl.conf; service network restart') +ConfigFile.add_config_assoc(SSHDCONFIG, '[ -f /var/lock/subsys/sshd ] && /etc/rc.d/init.d/sshd restart') +ConfigFile.add_config_assoc(LILOCONF, '[ `/usr/sbin/detectloader` = LILO ] && /sbin/lilo') +ConfigFile.add_config_assoc(SYSLOGCONF, '[ -f /var/lock/subsys/syslog ] && service syslog reload') +ConfigFile.add_config_assoc('^/etc/issue$', '/usr/bin/killall mingetty') + +# configuration rules + +def set_secure_level(level): + _interactive and log(_('Setting secure level to %s') % level) + msec = ConfigFile.get_config_file(MSEC) + msec.set_shell_variable('SECURE_LEVEL', level) + +def get_secure_level(): + msec = ConfigFile.get_config_file(MSEC) + return msec.get_shell_variable('SECURE_LEVEL') + +def set_root_umask(umask): + _interactive and log(_('Setting root umask to %s') % umask) + msec = ConfigFile.get_config_file(MSEC) + msec.set_shell_variable('UMASK_ROOT', umask) + +def set_user_umask(umask): + _interactive and log(_('Setting users umask to %s') % umask) + msec = ConfigFile.get_config_file(MSEC) + msec.set_shell_variable('UMASK_USER', umask) + +def allow_x_connections(): + _interactive and log(_('Allowing users to connect X server from everywhere')) + msec = ConfigFile.get_config_file(MSEC_XINIT) + msec.replace_line_matching('/usr/X11R6/bin/xhost', '/usr/X11R6/bin/xhost +', 1) + +def allow_local_x_connections(): + _interactive and log(_('Allowing users to connect X server from localhost')) + msec = ConfigFile.get_config_file(MSEC_XINIT) + msec.replace_line_matching('/usr/X11R6/bin/xhost', '/usr/X11R6/bin/xhost + localhost', 1) + +def restrict_x_connections(): + _interactive and log(_('Restricting X server connection to the console user')) + msec = ConfigFile.get_config_file(MSEC_XINIT) + msec.remove_line_matching('/usr/X11R6/bin/xhost', 1) + +def set_shell_timeout(val): + _interactive and log(_('Setting shell timeout to %s') % val) + msec = ConfigFile.get_config_file(MSEC) + msec.set_shell_variable('TMOUT', val) + +def set_shell_history_size(size): + if size >= 0: + _interactive and log(_('Setting shell history size to %s') % size) + msec = ConfigFile.get_config_file(MSEC) + msec.set_shell_variable('HISTFILESIZE', size) + else: + _interactive and log(_('Removing limit on shell history size')) + msec = ConfigFile.get_config_file(MSEC) + msec. remove_line_matching('^HISTFILESIZE=') + +def allow_reboot(): + _interactive and log(_('Allowing reboot to the console user')) + shutdownallow = ConfigFile.get_config_file(SHUTDOWNALLOW) + shutdownallow.exists() and shutdownallow.move(SUFFIX) + for f in [SHUTDOWN, POWEROFF, REBOOT, HALT]: + ConfigFile.get_config_file(f).touch() + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('kernel.sysrq', 1) + kdmrc = ConfigFile.get_config_file(KDMRC) + kdmrc.exists() and kdmrc.set_shell_variable('AllowShutdown', 'All', 'X-:\*-Greeter', '^\s*$') + gdmconf = ConfigFile.get_config_file(GDMCONF) + gdmconf.exists() and gdmconf.set_shell_variable('SystemMenu', 'true', '\[greeter\]', '^\s*$') + +def forbid_reboot(): + _interactive and log(_('Forbidding reboot to the console user')) + ConfigFile.get_config_file(SHUTDOWNALLOW, SUFFIX).touch() + for f in [SHUTDOWN, POWEROFF, REBOOT, HALT]: + ConfigFile.get_config_file(f).unlink() + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('kernel.sysrq', 0) + kdmrc = ConfigFile.get_config_file(KDMRC) + kdmrc.exists() and kdmrc.set_shell_variable('AllowShutdown', 'None', 'X-:\*-Greeter', '^\s*$') + gdmconf = ConfigFile.get_config_file(GDMCONF) + gdmconf.exists() and gdmconf.set_shell_variable('SystemMenu', 'false', '\[greeter\]', '^\s*$') + +def allow_user_list(): + _interactive and log(_('Allowing the listing of users in display managers')) + kdmrc = ConfigFile.get_config_file(KDMRC) + kdmrc.exists() and kdmrc.set_shell_variable('ShowUsers', 'All') + gdmconf = ConfigFile.get_config_file(GDMCONF) + gdmconf.exists() and gdmconf.set_shell_variable('Browser', '1') + +def forbid_user_list(): + _interactive and log(_('Disabling the listing of users in display managers')) + kdmrc = ConfigFile.get_config_file(KDMRC) + kdmrc.exists() and kdmrc.set_shell_variable('ShowUsers', 'None') + gdmconf = ConfigFile.get_config_file(GDMCONF) + gdmconf.exists() and gdmconf.set_shell_variable('Browser', '0') + +def allow_root_login(): + _interactive and log(_('Allowing root login')) + sshd_config = ConfigFile.get_config_file(SSHDCONFIG) + sshd_config.exists() and sshd_config.replace_line_matching('^\s*PermitRootLogin\s+no', + 'PermitRootLogin yes') + + kde = ConfigFile.get_config_file(KDE) + gdm = ConfigFile.get_config_file(GDM) + xdm = ConfigFile.get_config_file(XDM) + + for cnf in (kde, gdm, xdm): + cnf.exists() and cnf.remove_line_matching('^auth\s*required\s*/lib/security/pam_listfile.so.*bastille-no-login', 1) + + securetty = ConfigFile.get_config_file(SECURETTY) + for n in range(1, 7): + s = 'tty' + str(n) + securetty.replace_line_matching(s, s, 1) + s = 'vc/' + str(n) + securetty.replace_line_matching(s, s, 1) + +def forbid_root_login(): + _interactive and log(_('Forbidding root login')) + sshd_config = ConfigFile.get_config_file(SSHDCONFIG) + sshd_config.exists() and sshd_config.replace_line_matching('^\s*PermitRootLogin\s+yes', + 'PermitRootLogin no') + + bastillenologin = ConfigFile.get_config_file(BASTILLENOLOGIN) + bastillenologin.replace_line_matching('^\s*root', 'root', 1) + + kde = ConfigFile.get_config_file(KDE) + gdm = ConfigFile.get_config_file(GDM) + xdm = ConfigFile.get_config_file(XDM) + + for cnf in (kde, gdm, xdm): + cnf.exists() and (cnf.replace_line_matching('^auth\s*required\s*/lib/security/pam_listfile.so.*bastille-no-login', 'auth required /lib/security/pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login') or \ + cnf.insert_at(0, 'auth required /lib/security/pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login')) + + # TODO xdm support + securetty = ConfigFile.get_config_file(SECURETTY) + securetty.remove_line_matching('.+', 1) + +def enable_pam_wheel_for_su(): + _interactive and log(_('Allowing su only from wheel group members')) + try: + ent = grp.getgrnam('wheel') + except KeyError: + error(_('no wheel group')) + return + members = ent[3] + if members == [] or members == ['root']: + error(_('wheel group is empty')) + return + su = ConfigFile.get_config_file(SU) + su.exists() and (su.replace_line_matching('^auth\s+required\s+/lib/security/pam_wheel.so\s+use_uid\s*$', + 'auth required /lib/security/pam_wheel.so use_uid') or \ + su.insert_after('^auth\s+required', + 'auth required /lib/security/pam_wheel.so use_uid')) + +def disable_pam_wheel_for_su(): + _interactive and log(_('Allowing su for all')) + su = ConfigFile.get_config_file(SU) + su.exists() and su.remove_line_matching('^auth\s+required\s+/lib/security/pam_wheel.so\s+use_uid\s*$') + +def forbid_issues(allow_local=0): + if not allow_local: + _interactive and log(_('Disabling pre-login message')) + issue = ConfigFile.get_config_file(ISSUE) + issue.exists() and issue.move(SUFFIX) and issue.modified() + else: + _interactive and log(_('Allowing pre-login message')) + issue = ConfigFile.get_config_file(ISSUE, SUFFIX) + issue.exists() and issue.get_lines() + _interactive and log(_('Disabling network pre-login message')) + issuenet = ConfigFile.get_config_file(ISSUENET) + issuenet.exists() and issuenet.move(SUFFIX) + +def allow_issues(): + _interactive and log(_('Allowing RemoteRoot pre-login messages')) + issue = ConfigFile.get_config_file(ISSUE, SUFFIX) + issue.exists() and issue.get_lines() + issuenet = ConfigFile.get_config_file(ISSUENET, SUFFIX) + issuenet.exists() and issuenet.get_lines() + +def allow_autologin(): + _interactive and log(_('Allowing autologin')) + autologin = ConfigFile.get_config_file(AUTOLOGIN) + autologin.exists() and autologin.set_shell_variable('AUTOLOGIN', 'yes') + +def forbid_autologin(): + _interactive and log(_('Forbidding autologin')) + autologin = ConfigFile.get_config_file(AUTOLOGIN) + autologin.exists() and autologin.set_shell_variable('AUTOLOGIN', 'no') + +def password_loader(value): + _interactive and log(_('Activating password in boot loader')) + liloconf = ConfigFile.get_config_file(LILOCONF) + liloconf.exists() and (liloconf.replace_line_matching('^password=', 'password="' + value + '"', 0, 1) or \ + liloconf.insert_after('^boot=', 'password="' + value + '"')) and \ + Perms.chmod(liloconf.path, 0600) + # TODO encrypt password in grub + menulst = ConfigFile.get_config_file(MENULST) + menulst.exists() and (menulst.replace_line_matching('^password\s', 'password "' + value + '"') or \ + menulst.insert_at(0, 'password "' + value + '"')) and \ + Perms.chmod(menulst.path, 0600) + # TODO add yaboot support + +def nopassword_loader(): + _interactive and log(_('Removing password in boot loader')) + liloconf = ConfigFile.get_config_file(LILOCONF) + liloconf.exists() and liloconf.remove_line_matching('^password=', 1) + menulst = ConfigFile.get_config_file(MENULST) + menulst.exists() and menulst.remove_line_matching('^password\s') + +def enable_console_log(): + _interactive and log(_('Enabling log on console 12')) + syslogconf = ConfigFile.get_config_file(SYSLOGCONF) + syslogconf.exists() and syslogconf.replace_line_matching('\s*[^#]+/dev/tty12', '*.* /dev/tty12', 1) + +def disable_console_log(): + _interactive and log(_('Disabling log on console 12')) + syslogconf = ConfigFile.get_config_file(SYSLOGCONF) + syslogconf.exists() and syslogconf.remove_line_matching('\*\.\*\s*/dev/tty12') + +def enable_promisc_check(): + _interactive and log(_('Activating periodic promiscuity check')) + cron = ConfigFile.get_config_file(CRON) + cron.replace_line_matching('[^#]+/usr/share/msec/promisc_check.sh', '*/1 * * * * root /usr/share/msec/promisc_check.sh', 1) + +def disable_promisc_check(): + _interactive and log(_('Disabling periodic promiscuity check')) + cron = ConfigFile.get_config_file(CRON) + cron.remove_line_matching('[^#]+/usr/share/msec/promisc_check.sh') + +def enable_security_check(): + _interactive and log(_('Activating daily security check')) + cron = ConfigFile.get_config_file(CRON) + cron.replace_line_matching('[^#]+/usr/share/msec/security.sh', '0 4 * * * root /usr/share/msec/security.sh', 1) + +def disable_security_check(): + _interactive and log(_('Disabling daily security check')) + cron = ConfigFile.get_config_file(CRON) + cron.remove_line_matching('[^#]+/usr/share/msec/security.sh') + +def deny_all_services(): + _interactive and log(_('Disabling all services')) + hostsdeny = ConfigFile.get_config_file(HOSTSDENY) + hostsdeny.remove_line_matching('^ALL:ALL EXCEPT localhost:DENY', 1) + hostsdeny.replace_line_matching('^ALL:ALL:DENY$', 'ALL:ALL:DENY', 1) + +def deny_non_local_services(): + _interactive and log(_('Disabling non local services')) + hostsdeny = ConfigFile.get_config_file(HOSTSDENY) + hostsdeny.remove_line_matching('^ALL:ALL:DENY', 1) + hostsdeny.replace_line_matching('^ALL:ALL EXCEPT localhost:DENY$', 'ALL:ALL EXCEPT localhost:DENY', 1) + +def authorize_all_services(): + _interactive and log(_('Authorizing all services')) + hostsdeny = ConfigFile.get_config_file(HOSTSDENY) + hostsdeny.remove_line_matching('^ALL:ALL:DENY', 1) + hostsdeny.remove_line_matching('^ALL:ALL EXCEPT localhost:DENY', 1) + +def enable_ip_spoofing_protection(alert): + _interactive and log(_('Enabling ip spoofing protection')) + hostconf = ConfigFile.get_config_file(HOSTCONF) + hostconf.replace_line_matching('nospoof', 'nospoof on', 1) + hostconf.replace_line_matching('spoofalert', 'spoofalert on', (alert != 0)) + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('net.ipv4.conf.all.rp_filter', 1) + +def disable_ip_spoofing_protection(): + _interactive and log(_('Disabling ip spoofing protection')) + hostconf = ConfigFile.get_config_file(HOSTCONF) + hostconf.remove_line_matching('nospoof') + hostconf.remove_line_matching('spoofalert') + +def ignore_icmp_echo(): + _interactive and log(_('Ignoring icmp echo')) + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('net.ipv4.icmp_echo_ignore_all', 1) + sysctlconf.set_shell_variable('net.ipv4.icmp_echo_ignore_broadcasts', 1) + +def accept_icmp_echo(): + _interactive and log(_('Accepting icmp echo')) + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('net.ipv4.icmp_echo_ignore_all', 0) + sysctlconf.set_shell_variable('net.ipv4.icmp_echo_ignore_broadcasts', 0) + +def ignore_bogus_error_responses(): + _interactive and log(_('Ignoring bogus icmp error responses')) + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('net.ipv4.icmp_ignore_bogus_error_responses', 1) + +def accept_bogus_error_responses(): + _interactive and log(_('Accepting bogus icmp error responses')) + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('net.ipv4.icmp_ignore_bogus_error_responses', 0) + +def enable_log_strange_packets(): + _interactive and log(_('Enabling logging of strange packets')) + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('net.ipv4.conf.all.log_martians', 1) + +def disable_log_strange_packets(): + _interactive and log(_('Disabling logging of strange packets')) + sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) + sysctlconf.set_shell_variable('net.ipv4.conf.all.log_martians', 0) + +def enable_libsafe(): + if os.path.exists(Config.get_config('root', '') + '/lib/libsafe.so.2'): + _interactive and log(_('Enabling libsafe')) + ldsopreload = ConfigFile.get_config_file(LDSOPRELOAD) + ldsopreload.replace_line_matching('[^#]*libsafe', '/lib/libsafe.so.2', 1) + +def disable_libsafe(): + _interactive and log(_('Disabling libsafe')) + ldsopreload = ConfigFile.get_config_file(LDSOPRELOAD) + ldsopreload.remove_line_matching('[^#]*libsafe') + +def password_length(length, ndigits=0, nupper=0): + _interactive and log(_('Setting minimum password length %d') % length) + passwd = ConfigFile.get_config_file(PASSWD) + passwd.exists() and (passwd.replace_line_matching('^(password\s+required\s+/lib/security/pam_cracklib.so.*?)(\sminlen=[0-9]+\s)(.*)', + '@1 minlen=%s @3' % length) or \ + passwd.replace_line_matching('^password\s+required\s+/lib/security/pam_cracklib.so.*', + '@0 minlen=%s ' % length)) + + passwd.exists() and (passwd.replace_line_matching('^(password\s+required\s+/lib/security/pam_cracklib.so.*?)(\sdcredit=[0-9]+\s)(.*)', + '@1 dcredit=%s @3' % ndigits) or \ + passwd.replace_line_matching('^password\s+required\s+/lib/security/pam_cracklib.so.*', + '@0 dcredit=%s ' % ndigits)) + + passwd.exists() and (passwd.replace_line_matching('^(password\s+required\s+/lib/security/pam_cracklib.so.*?)(\sucredit=[0-9]+\s)(.*)', + '@1 ucredit=%s @3' % nupper) or \ + passwd.replace_line_matching('^password\s+required\s+/lib/security/pam_cracklib.so.*', + '@0 ucredit=%s ' % nupper)) + +def enable_sulogin(): + _interactive and log(_('Enabling sulogin in single user runlevel')) + inittab = ConfigFile.get_config_file(INITTAB) + inittab.replace_line_matching('[^#]+:S:', '~~:S:wait:/sbin/sulogin', 1) + +def disable_sulogin(): + _interactive and log(_('Disabling sulogin in single user runlevel')) + inittab = ConfigFile.get_config_file(INITTAB) + inittab.remove_line_matching('~~:S:wait:/sbin/sulogin') + +def enable_msec_cron(): + _interactive and log(_('Enabling msec periodic runs')) + mseccron = ConfigFile.get_config_file(MSECCRON) + mseccron.symlink(MSECBIN) + +def disable_msec_cron(): + _interactive and log(_('Disabling msec periodic runs')) + mseccron = ConfigFile.get_config_file(MSECCRON) + mseccron.unlink() + +def disable_at_crontab(): + _interactive and log(_('Disabling crontab and at')) + cronallow = ConfigFile.get_config_file(CRONALLOW, SUFFIX) + cronallow.replace_line_matching('root', 'root', 1) + atallow = ConfigFile.get_config_file(ATALLOW, SUFFIX) + atallow.replace_line_matching('root', 'root', 1) + +def enable_at_crontab(): + _interactive and log(_('Enabling crontab and at')) + cronallow = ConfigFile.get_config_file(CRONALLOW) + cronallow.exists() and cronallow.move(SUFFIX) + atallow = ConfigFile.get_config_file(ATALLOW) + atallow.exists() and atallow.move(SUFFIX) + +maximum_regex = re.compile('^Maximum:\s*([0-9]+)', re.MULTILINE) + +# TODO FL Sat Dec 29 20:18:20 2001 +# replace chage calls and /etc/shadow parsing by a python API to the shadow functions. +def password_aging(max): + uid_min = 500 + _interactive and log(_('Setting password maximum aging for new user to %d') % max) + logindefs = ConfigFile.get_config_file(LOGINDEFS) + if logindefs.exists(): + logindefs.replace_line_matching('^\s*PASS_MAX_DAYS', 'PASS_MAX_DAYS ' + str(max)) + uid_min = logindefs.get_match('^\s*UID_MIN\s+([0-9]+)', '@1') + if uid_min: + uid_min = int(uid_min) + shadow = ConfigFile.get_config_file(SHADOW) + if shadow.exists(): + _interactive and log(_('Setting password maximum aging for users with id greater than %d to %d') % (uid_min, max)) + for line in shadow.get_lines(): + field = string.split(line, ':') + if len(field) < 2: + continue + name = field[0] + password = field[1] + entry = pwd.getpwnam(name) + if (len(password) > 0 and password[0] != '!') and password != '*' and password != 'x' and entry[2] >= uid_min: + cmd = '/usr/bin/chage -l %s' % entry[0] + ret = commands.getstatusoutput(cmd) + _interactive and log(_('got current maximum password aging for user %s with command \'%s\'') % (entry[0], cmd)) + if ret[0] == 0: + res = maximum_regex.search(ret[1]) + if res: + current_max = int(res.group(1)) + if max != current_max: + cmd = '/usr/bin/chage -M %d %s' % (max, entry[0]) + ret = commands.getstatusoutput(cmd) + log(_('changed maximum password aging for user %s with command %s') % (entry[0], cmd)) + if ret[0] != 0: + error(ret[1]) + else: + error(_('unable to parse chage output')) + else: + error(_('unable to run chage: %s') % ret[1]) + +# various + +def set_interactive(v): + global _interactive + + _interactive = v + +# libmsec.py ends here |