#--------------------------------------------------------------- # Project : Mandrake Linux # Module : share # File : libmsec.py # Version : $Id$ # Author : Frederic Lepied # Created On : Mon Dec 10 22:52:17 2001 # Purpose : all access points of the msec utility. #--------------------------------------------------------------- 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' SECURITYCONF = '/var/lib/msec/security.conf' SECURITYCRON = '/etc/cron.daily/msec' SECURITYSH = '/usr/share/msec/security.sh' SERVER = '/etc/security/msec/server' SHADOW = '/etc/shadow' SHUTDOWN = '/etc/security/console.apps/shutdown' SHUTDOWNALLOW = '/etc/shutdown.allow' SSHDCONFIG = '/etc/ssh/sshd_config' STARTX = '/usr/X11R6/bin/startx ' SU = '/etc/pam.d/su' SYSCTLCONF = '/etc/sysctl.conf' SYSLOGCONF = '/etc/syslog.conf' SYSTEM_AUTH = '/etc/pam.d/system-auth' XDM = '/etc/pam.d/xdm' XSERVERS = '/etc/X11/xdm/Xservers' # constants to keep in sync with shadow.py NONE=0 ALL=1 LOCAL=2 # 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(): "D" msec = ConfigFile.get_config_file(MSEC) return msec.get_shell_variable('SECURE_LEVEL') def set_server_level(level): _interactive and log(_('Setting server level to %s') % level) securityconf = ConfigFile.get_config_file(SECURITYCONF) securityconf.set_shell_variable('SERVER_LEVEL', level) def get_server_level(): "D" securityconf = ConfigFile.get_config_file(SECURITYCONF) level = securityconf.get_shell_variable('SERVER_LEVEL') if level: return level msec = ConfigFile.get_config_file(MSEC) return msec.get_shell_variable('SECURE_LEVEL') def create_server_link(): ''' If SERVER_LEVEL (or SECURE_LEVEL if absent) is greater than 3 in /etc/security/msec/security.conf, creates the symlink /etc/security/msec/server to point to /etc/security/msec/server.. 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.''' level = get_server_level() server = ConfigFile.get_config_file(SERVER) if level in ('0', '1', '2', '3'): _interactive and log(_('Allowing chkconfig --add from rpm')) server.exists() and server.unlink() else: _interactive and log(_('Restricting chkconfig --add from rpm')) server.symlink(SERVER + '.' + str(level)) def set_root_umask(umask): ''' Set the root 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): ''' Set the user 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(arg, listen_tcp): ''' Allow/Forbid X connections. First arg specifies what is done on the client side: ALL (all connections are allowed), LOCAL (only local connection) and NONE (no connection). The second argument specifies what is authorized on the server side: if clients are authorized to connect on the tcp port 6000 or not.''' msec = ConfigFile.get_config_file(MSEC_XINIT) startx = ConfigFile.get_config_file(STARTX) xservers = ConfigFile.get_config_file(XSERVERS) gdmconf = ConfigFile.get_config_file(GDMCONF) if arg == ALL: _interactive and log(_('Allowing users to connect X server from everywhere')) msec.exists() and msec.replace_line_matching('/usr/X11R6/bin/xhost', '/usr/X11R6/bin/xhost +', 1) elif arg == LOCAL: _interactive and log(_('Allowing users to connect X server from localhost')) msec.exists() and msec.replace_line_matching('/usr/X11R6/bin/xhost', '/usr/X11R6/bin/xhost + localhost', 1) elif arg == NONE: _interactive and log(_('Restricting X server connection to the console user')) msec.exists() and msec.remove_line_matching('/usr/X11R6/bin/xhost', 1) else: error(_('invalid allow_x_connections arg: %s') % arg) return if listen_tcp: _interactive and log(_('Allowing the X server to listen to tcp connections')) startx.exists() and startx.replace_line_matching('(\s*clientargs=".*) -nolisten tcp(.*")', '@1@2') xservers.exists() and xservers.replace_line_matching('(\s*[^#]+/usr/X11R6/bin/X .*) -nolisten tcp(.*)', '@1@2', 0, 1) gdmconf.exists() and gdmconf. replace_line_matching('(\s*command=.*/X.*?) -nolisten tcp(.*)$', '@1@2', 0, 1) else: _interactive and log(_('Forbidding the X server to listen to tcp connection')) startx.exists() and startx.replace_line_matching('clientargs="(.*?)( -nolisten tcp)?"', 'clientargs="@1 -nolisten tcp"') xservers.exists() and xservers.replace_line_matching('(\s*[^#]+/usr/X11R6/bin/X .*?)( -nolisten tcp)?$', '@1 -nolisten tcp', 0, 1) gdmconf.exists() and gdmconf. replace_line_matching('(\s*command=.*/X.*?)( -nolisten tcp)?$', '@1 -nolisten tcp', 0, 1) def set_shell_timeout(val): ''' Set the shell timeout. A value of zero means no timeout.''' _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): ''' Set shell commands history size. A value of -1 means unlimited.''' msec = ConfigFile.get_config_file(MSEC) if size >= 0: _interactive and log(_('Setting shell history size to %s') % size) msec.set_shell_variable('HISTFILESIZE', size) else: _interactive and log(_('Removing limit on shell history size')) msec. remove_line_matching('^HISTFILESIZE=') def allow_reboot(arg): ''' Allow/Forbid reboot by the console user.''' shutdownallow = ConfigFile.get_config_file(SHUTDOWNALLOW) sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) kdmrc = ConfigFile.get_config_file(KDMRC) gdmconf = ConfigFile.get_config_file(GDMCONF) if arg: _interactive and log(_('Allowing reboot to the console user')) shutdownallow.exists() and shutdownallow.move(SUFFIX) for f in [SHUTDOWN, POWEROFF, REBOOT, HALT]: cfg = ConfigFile.get_config_file(f) cfg.exists() or cfg.touch() sysctlconf.set_shell_variable('kernel.sysrq', 1) kdmrc.exists() and kdmrc.set_shell_variable('AllowShutdown', 'All', 'X-:\*-Greeter', '^\s*$') gdmconf.exists() and gdmconf.set_shell_variable('SystemMenu', 'true', '\[greeter\]', '^\s*$') else: _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.set_shell_variable('kernel.sysrq', 0) kdmrc.exists() and kdmrc.set_shell_variable('AllowShutdown', 'None', 'X-:\*-Greeter', '^\s*$') gdmconf.exists() and gdmconf.set_shell_variable('SystemMenu', 'false', '\[greeter\]', '^\s*$') def allow_user_list(arg): ''' Allow/Forbid the list of users on the system on display managers (kdm and gdm).''' kdmrc = ConfigFile.get_config_file(KDMRC) gdmconf = ConfigFile.get_config_file(GDMCONF) if arg: _interactive and log(_('Allowing the listing of users in display managers')) kdmrc.exists() and kdmrc.set_shell_variable('ShowUsers', 'All') gdmconf.exists() and gdmconf.set_shell_variable('Browser', 'true') else: _interactive and log(_('Disabling the listing of users in display managers')) kdmrc.exists() and kdmrc.set_shell_variable('ShowUsers', 'None') gdmconf.exists() and gdmconf.set_shell_variable('Browser', 'false') def allow_root_login(arg): ''' Allow/Forbid direct root login.''' sshd_config = ConfigFile.get_config_file(SSHDCONFIG) securetty = ConfigFile.get_config_file(SECURETTY) if arg: _interactive and log(_('Allowing direct root login')) sshd_config.exists() and sshd_config.replace_line_matching('^\s*PermitRootLogin\s+no', 'PermitRootLogin yes', 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.remove_line_matching('^auth\s*required\s*/lib/security/pam_listfile.so.*bastille-no-login', 1) 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) else: _interactive and log(_('Forbidding direct root login')) sshd_config.exists() and sshd_config.replace_line_matching('^\s*PermitRootLogin\s+yes', 'PermitRootLogin no', 1) 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')) securetty.remove_line_matching('.+', 1) def enable_pam_wheel_for_su(arg): ''' Enabling su only from members of the wheel group or allow su from any user.''' su = ConfigFile.get_config_file(SU) if arg: _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']: _interactive and error(_('wheel group is empty')) return 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')) else: _interactive and log(_('Allowing su for all')) su.exists() and su.remove_line_matching('^auth\s+required\s+/lib/security/pam_wheel.so\s+use_uid\s*$') def allow_issues(arg): ''' If \\fIarg\\fP = ALL allow /etc/issue and /etc/issue.net to exist. If \\fIarg\\fP = NONE no issues are allowed else only /etc/issue is allowed.''' issue = ConfigFile.get_config_file(ISSUE, SUFFIX) issuenet = ConfigFile.get_config_file(ISSUENET, SUFFIX) if arg == ALL: _interactive and log(_('Allowing RemoteRoot pre-login messages')) issue.exists() and issue.get_lines() issuenet.exists() and issuenet.get_lines() else: if arg == NONE: _interactive and log(_('Disabling pre-login message')) issue.exists(1) and issue.move(SUFFIX) and issue.modified() else: _interactive and log(_('Allowing pre-login message')) issue.exists() and issue.get_lines() _interactive and log(_('Disabling network pre-login message')) issuenet.exists(1) and issuenet.move(SUFFIX) def allow_autologin(arg): ''' Allow/Forbid autologin.''' autologin = ConfigFile.get_config_file(AUTOLOGIN) if arg: _interactive and log(_('Allowing autologin')) autologin.exists() and autologin.set_shell_variable('AUTOLOGIN', 'yes') else: _interactive and log(_('Forbidding autologin')) autologin.exists() and autologin.set_shell_variable('AUTOLOGIN', 'no') def password_loader(value): "D" _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(): "D" _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(arg, expr='*.*'): ''' Enable/Disable syslog reports to console 12. \\fIexpr\\fP is the expression describing what to log (see syslog.conf(5) for more details).''' syslogconf = ConfigFile.get_config_file(SYSLOGCONF) if arg: _interactive and log(_('Enabling log on console 12')) syslogconf.exists() and syslogconf.replace_line_matching('\s*[^#]+/dev/tty12', expr + ' /dev/tty12', 1) else: _interactive and log(_('Disabling log on console 12')) syslogconf.exists() and syslogconf.remove_line_matching('\s*[^#]+/dev/tty12') def enable_promisc_check(arg): ''' Activate/Disable ethernet cards promiscuity check.''' cron = ConfigFile.get_config_file(CRON) if arg: _interactive and log(_('Activating periodic promiscuity check')) cron.replace_line_matching('[^#]+/usr/share/msec/promisc_check.sh', '*/1 * * * * root /usr/share/msec/promisc_check.sh', 1) else: _interactive and log(_('Disabling periodic promiscuity check')) cron.remove_line_matching('[^#]+/usr/share/msec/promisc_check.sh') def enable_security_check(arg): ''' Activate/Disable daily security check.''' cron = ConfigFile.get_config_file(CRON) cron.remove_line_matching('[^#]+/usr/share/msec/security.sh') securitycron = ConfigFile.get_config_file(SECURITYCRON) if arg: _interactive and log(_('Activating daily security check')) securitycron.symlink(SECURITYSH) else: _interactive and log(_('Disabling daily security check')) securitycron.unlink() def authorize_services(arg): ''' Authorize all services controlled by tcp_wrappers (see hosts.deny(5)) if \\fIarg\\fP = ALL. Only local ones if \\fIarg\\fP = LOCAL and none if \\fIarg\\fP = NONE. To authorize the services you need, use /etc/hosts.allow (see hosts.allow(5)).''' hostsdeny = ConfigFile.get_config_file(HOSTSDENY) if arg == ALL: _interactive and log(_('Authorizing all services')) hostsdeny.remove_line_matching('^ALL:ALL:DENY', 1) hostsdeny.remove_line_matching('^ALL:ALL EXCEPT 127\.0\.0\.1:DENY', 1) elif arg == NONE: _interactive and log(_('Disabling all services')) hostsdeny.remove_line_matching('^ALL:ALL EXCEPT 127\.0\.0\.1:DENY', 1) hostsdeny.replace_line_matching('^ALL:ALL:DENY$', 'ALL:ALL:DENY', 1) elif arg == LOCAL: _interactive and log(_('Disabling non local services')) hostsdeny.remove_line_matching('^ALL:ALL:DENY', 1) hostsdeny.replace_line_matching('^ALL:ALL EXCEPT 127\.0\.0\.1:DENY$', 'ALL:ALL EXCEPT 127.0.0.1:DENY', 1) else: error(_('authorize_services invalid argument: %s') % arg) def enable_ip_spoofing_protection(arg, alert=1): ''' Enable/Disable IP spoofing protection. If \\fIalert\\fP is true, also reports to syslog.''' hostconf = ConfigFile.get_config_file(HOSTCONF) if arg: _interactive and log(_('Enabling ip spoofing protection')) 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) else: _interactive and log(_('Disabling ip spoofing protection')) hostconf.remove_line_matching('nospoof') hostconf.remove_line_matching('spoofalert') def accept_icmp_echo(arg): ''' Accept/Refuse icmp echo.''' sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) if arg: _interactive and log(_('Accepting icmp echo')) sysctlconf.set_shell_variable('net.ipv4.icmp_echo_ignore_all', 0) sysctlconf.set_shell_variable('net.ipv4.icmp_echo_ignore_broadcasts', 0) else: _interactive and log(_('Ignoring icmp echo')) sysctlconf.set_shell_variable('net.ipv4.icmp_echo_ignore_all', 1) sysctlconf.set_shell_variable('net.ipv4.icmp_echo_ignore_broadcasts', 1) def accept_bogus_error_responses(arg): ''' Accept/Refuse bogus IPv4 error messages.''' sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) if arg: _interactive and log(_('Accepting bogus icmp error responses')) sysctlconf.set_shell_variable('net.ipv4.icmp_ignore_bogus_error_responses', 0) else: _interactive and log(_('Ignoring bogus icmp error responses')) sysctlconf.set_shell_variable('net.ipv4.icmp_ignore_bogus_error_responses', 1) def enable_log_strange_packets(arg): ''' Enable/Disable the logging of IPv4 strange packets.''' sysctlconf = ConfigFile.get_config_file(SYSCTLCONF) if arg: _interactive and log(_('Enabling logging of strange packets')) sysctlconf.set_shell_variable('net.ipv4.conf.all.log_martians', 1) else: _interactive and log(_('Disabling logging of strange packets')) sysctlconf.set_shell_variable('net.ipv4.conf.all.log_martians', 0) def enable_libsafe(arg): ''' Enable/Disable libsafe if libsafe is found on the system.''' if arg: 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) else: _interactive and log(_('Disabling libsafe')) ldsopreload = ConfigFile.get_config_file(LDSOPRELOAD) ldsopreload.remove_line_matching('[^#]*libsafe') def password_length(length, ndigits=0, nupper=0): ''' Set the password minimum length and minimum number of digit and minimum number of capitalized letters.''' _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_password(arg): ''' Use password to authenticate users.''' system_auth = ConfigFile.get_config_file(SYSTEM_AUTH) if arg: _interactive and log(_('Using password to authenticate users')) system_auth.remove_line_matching('^\s*auth\s+sufficient\s+/lib/security/pam_permit.so') else: _interactive and log(_('Don\'t use password to authenticate users')) system_auth.replace_line_matching('^\s*auth\s+sufficient\s+/lib/security/pam_permit.so', 'auth sufficient /lib/security/pam_permit.so') or \ system_auth.insert_before('auth\s+sufficient', 'auth sufficient /lib/security/pam_permit.so') def enable_sulogin(arg): ''' Enable/Disable sulogin(8) in single user level.''' inittab = ConfigFile.get_config_file(INITTAB) if arg: _interactive and log(_('Enabling sulogin in single user runlevel')) inittab.replace_line_matching('[^#]+:S:', '~~:S:wait:/sbin/sulogin', 1) else: _interactive and log(_('Disabling sulogin in single user runlevel')) inittab.remove_line_matching('~~:S:wait:/sbin/sulogin') def enable_msec_cron(arg): ''' Enable/Disable msec hourly security check.''' mseccron = ConfigFile.get_config_file(MSECCRON) if arg: _interactive and log(_('Enabling msec periodic runs')) mseccron.symlink(MSECBIN) else: _interactive and log(_('Disabling msec periodic runs')) mseccron.unlink() def enable_at_crontab(arg): ''' Enable/Disable crontab and at for users. Put allowed users in /etc/cron.allow and /etc/at.allow (see man at(1) and crontab(1)).''' cronallow = ConfigFile.get_config_file(CRONALLOW) atallow = ConfigFile.get_config_file(ATALLOW) if arg: _interactive and log(_('Enabling crontab and at')) cronallow.exists() and cronallow.move(SUFFIX) atallow.exists() and atallow.move(SUFFIX) else: _interactive and log(_('Disabling crontab and at')) cronallow.replace_line_matching('root', 'root', 1) atallow.replace_line_matching('root', 'root', 1) maximum_regex = re.compile('^Maximum:\s*([0-9]+|-1)', re.MULTILINE) inactive_regex = re.compile('^Inactive:\s*(-?[0-9]+)', re.MULTILINE) no_aging_list = [] def no_password_aging_for(name): '''D Add the name as an exception to the handling of password aging by msec.''' no_aging_list.append(name) # 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, inactive=-1): ''' Set password aging to \\fImax\\fP days and delay to change to \\fIinactive\\fP.''' 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), 1) 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 root and users with id greater than %d to %d and delay to %d days') % (uid_min, max, inactive)) for line in shadow.get_lines(): field = string.split(line, ':') if len(field) < 2: continue name = field[0] password = field[1] if name in no_aging_list: _interactive and log(_('User %s in password aging exception list') % (name,)) continue try: entry = pwd.getpwnam(name) except KeyError: error(_('User %s in shadow but not in passwd file') % name) continue if (len(password) > 0 and password[0] != '!') and password != '*' and password != 'x' and (entry[2] >= uid_min or entry[2] == 0): 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]) res2 = inactive_regex.search(ret[1]) if res and res2: current_max = int(res.group(1)) current_inactive = int(res2.group(1)) if max != current_max or current_inactive != inactive: cmd = '/usr/bin/chage -M %d -I %d %s' % (max, inactive, 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]) def set_security_conf(var, value): '''1 Set the variable \\fIvar\\fP to the value \\fIvalue\\fP in /var/lib/msec/security.conf. The best way to override the default setting is to use create /etc/security/msec/security.conf with the value you want. The following variables are currentrly recognized by msec: CHECK_UNOWNED if set to yes, report unowned files. CHECK_SHADOW if set to yes, check empty password in /etc/shadow. CHECK_SUID_MD5 if set to yes, verify checksum of the suid/sgid files. CHECK_SECURITY if set to yes, run the daily security checks. CHECK_PASSWD if set to yes, check for empty password, or a password while it should be in /etc/shadow or other users with id 0. SYSLOG_WARN if set to yes, report check result to syslog. CHECK_SUID_ROOT if set to yes, check additions/removals of suid root files. CHECK_PERMS if set to yes, check permissions of files in the users' home. CHKROOTKIT_CHECK if set to yes, run chkrootkit checks. CHECK_PROMISC if set to yes, check if the network devices are in promiscuous mode. RPM_CHECK if set to yes, run some checks against the rpm database. TTY_WARN if set to yes, reports check result to tty. CHECK_WRITEABLE if set to yes, check files/directories writable by everybody. MAIL_WARN if set to yes, report check result by mail. MAIL_USER if set, send the mail report to this email address else send it to root. CHECK_OPEN_PORT if set to yes, check open ports. CHECK_SUID_GROUP if set to yes, check additions/removals of sgid files. ''' securityconf = ConfigFile.get_config_file(SECURITYCONF) securityconf.set_shell_variable(var, value) # various def set_interactive(v): "D" global _interactive _interactive = v # libmsec.py ends here