diff options
-rw-r--r-- | share/libmsec.py | 578 |
1 files changed, 424 insertions, 154 deletions
diff --git a/share/libmsec.py b/share/libmsec.py index 1b34dd1..b71386b 100644 --- a/share/libmsec.py +++ b/share/libmsec.py @@ -151,7 +151,7 @@ def set_umask(variable, umask, msg): # don't lower security when not changing security level if _same_level: if val: - octal = string.atoi(umask, 8) | string.atoi(val, 8) + octal = int(umask, 8) | int(val, 8) umask = '0%o' % octal if val != umask: @@ -173,58 +173,121 @@ on the client side: ALL (all connections are allowed), LOCAL (only local connection) and NONE (no connection).''' msec = ConfigFile.get_config_file(MSEC_XINIT) - + + val = msec.exists() and msec.get_match('/usr/X11R6/bin/xhost\s*\+\s*([^#]*)') + + if val: + if val == '': + val = ALL + elif val == 'localhost': + val = LOCAL + else: + val = NONE + else: + val = NONE + + # don't lower security when not changing security level + if _same_level: + if val == NONE or (val == LOCAL and arg == ALL): + return + 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) + if val != arg: + _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) + if val != arg: + _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) + if val != arg: + _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 +STARTX_REGEXP = '(\s*clientargs=".*) -nolisten tcp(.*")' +XSERVERS_REGEXP = '(\s*[^#]+/usr/X11R6/bin/X .*) -nolisten tcp(.*)' +GDMCONF_REGEXP = '(\s*command=.*/X.*?) -nolisten tcp(.*)$' def allow_xserver_to_listen(arg): ''' The argument specifies if clients are authorized to connect to the X server on the tcp port 6000 or not.''' - + startx = ConfigFile.get_config_file(STARTX) xservers = ConfigFile.get_config_file(XSERVERS) gdmconf = ConfigFile.get_config_file(GDMCONF) + + val_startx = startx.exists() and startx.get_match(STARTX_REGEXP) + val_xservers = xservers.exists() and xservers.get_match(XSERVERS_REGEXP) + val_gdmconf = gdmconf.exists() and gdmconf.get_match(GDMCONF_REGEXP) + # don't lower security when not changing security level + if _same_level: + if val_startx and val_xservers and val_gdmconf: + return + if arg: - _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) + if val_startx or val_xservers or val_gdmconf: + _interactive and log(_('Allowing the X server to listen to tcp connections')) + startx.exists() and startx.replace_line_matching(STARTX_REGEXP, '@1@2') + xservers.exists() and xservers.replace_line_matching(XSERVERS_REGEXP, '@1@2', 0, 1) + gdmconf.exists() and gdmconf. replace_line_matching(GDMCONF_REGEXP, '@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) + if not val_startx or not val_xservers or not val_gdmconf: + _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) + + if msec.exists(): + old = msec.get_shell_variable('TMOUT') + if old != None: + old = int(old) + else: + old = None + + # don't lower security when not changing security level + if _same_level: + if old != None and old > val: + return + + if old != val: + _interactive and log(_('Setting shell timeout to %s') % val) + 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 msec.exists(): + old = msec.get_shell_variable('HISTFILESIZE') + else: + old = None + + # don't lower security when not changing security level + if _same_level: + if old != None: + old = int(old) + if size == -1 or old < size: + return + if size >= 0: - _interactive and log(_('Setting shell history size to %s') % size) - msec.set_shell_variable('HISTFILESIZE', size) + if old != size: + _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=') + if old != None: + _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.''' @@ -255,50 +318,81 @@ 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) - + + oldval_kdmrc = kdmrc.exists() and kdmrc.get_shell_variable('ShowUsers') + oldval_gdmconf = gdmconf.exists() and gdmconf.get_shell_variable('Browser') + 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') + msg = 'Allowing the listing of users in display managers' + val_kdmrc = 'All' + val_gdmconf = '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') + msg = 'Disabling the listing of users in display managers' + val_kdmrc = 'None' + val_gdmconf = 'false' + + # don't lower security when not changing security level + if _same_level: + if oldval_kdmrc == 'None' and oldval_gdmconf == 'false': + return + if oldval_kdmrc == 'None': + val_kdmrc = 'None' + if oldval_gdmconf == 'false': + val_gdmconf = 'false' + + if (gdmconf.exists() and oldval_gdmconf != val_gdmconf) or (kdmrc.exists() and oldval_kdmrc != val_kdmrc): + _interactive and log(_(msg)) + oldval_kdmrc != val_gdmconf and kdmrc.exists() and kdmrc.set_shell_variable('ShowUsers', val_kdmrc) + oldval_gdmconf != val_gdmconf and gdmconf.exists() and gdmconf.set_shell_variable('Browser', val_gdmconf) def allow_root_login(arg): ''' Allow/Forbid direct root login.''' securetty = ConfigFile.get_config_file(SECURETTY) - + kde = ConfigFile.get_config_file(KDE) + gdm = ConfigFile.get_config_file(GDM) + xdm = ConfigFile.get_config_file(XDM) + + val_kde = kde.exists() and kde.get_match('auth required /lib/security/pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login') + val_gdm = gdm.exists() and gdm.get_match('auth required /lib/security/pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login') + val_xdm = xdm.exists() and xdm.get_match('auth required /lib/security/pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login') + num = 0 + for n in range(1, 7): + s = 'tty' + str(n) + if securetty.get_match(s): + num = num + 1 + s = 'vc/' + str(n) + if securetty.get_match(s): + num = num + 1 + + # don't lower security when not changing security level + if _same_level: + if (not kde.exists() or val_kde) and (not gdm.exists() or val_gdm) and (not xdm.exists() or val_xdm) and num == 12: + return + if arg: - _interactive and log(_('Allowing direct root login')) - - kde = ConfigFile.get_config_file(KDE) - gdm = ConfigFile.get_config_file(GDM) - xdm = ConfigFile.get_config_file(XDM) + if val_kde or val_gdm or val_xdm or num != 12: + _interactive and log(_('Allowing direct root login')) - 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 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) + 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')) + if (kde.exists() and not val_kde) or (gdm.exists() and not val_gdm) or (xdm.exists() and not val_xdm) or num > 0: + _interactive and log(_('Forbidding direct root login')) - bastillenologin = ConfigFile.get_config_file(BASTILLENOLOGIN) - bastillenologin.replace_line_matching('^\s*root', 'root', 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')) - 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) + securetty.remove_line_matching('.+', 1) def allow_remote_root_login(arg): ''' Allow/Forbid remote root login.''' @@ -331,29 +425,33 @@ 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) + val = su.exists() and su.get_match('^auth\s+required\s+/lib/security/pam_wheel.so\s+use_uid\s*$') + # don't lower security when not changing security level if _same_level: - if su.exists() and su.get_match('^auth\s+required\s+/lib/security/pam_wheel.so\s+use_uid\s*$'): + if val: return 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')) + if not val: + _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*$') + if val: + _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 @@ -361,19 +459,33 @@ allowed else only /etc/issue is allowed.''' issue = ConfigFile.get_config_file(ISSUE, SUFFIX) issuenet = ConfigFile.get_config_file(ISSUENET, SUFFIX) + val = issue.exists(1) + valnet = issuenet.exists(1) + + # don't lower security when not changing security level + if _same_level: + if not val and not valnet: + return + if arg == ALL and not valnet: + return + if arg == ALL: - _interactive and log(_('Allowing RemoteRoot pre-login messages')) - issue.exists() and issue.get_lines() - issuenet.exists() and issuenet.get_lines() + if not (val and valnet): + _interactive and log(_('Allowing network 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() + if val: + _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) + if not val: + _interactive and log(_('Allowing pre-login message')) + issue.exists() and issue.get_lines() + if valnet: + _interactive and log(_('Disabling network pre-login message')) + issuenet.exists(1) and issuenet.move(SUFFIX) def allow_autologin(arg): ''' Allow/Forbid autologin.''' @@ -420,28 +532,54 @@ def nopassword_loader(): menulst = ConfigFile.get_config_file(MENULST) menulst.exists() and menulst.remove_line_matching('^password\s') -def enable_console_log(arg, expr='*.*'): +def enable_console_log(arg, expr='*.*', dev='tty12'): ''' Enable/Disable syslog reports to console 12. \\fIexpr\\fP is the -expression describing what to log (see syslog.conf(5) for more details).''' +expression describing what to log (see syslog.conf(5) for more details) and +dev the device to report the log.''' + syslogconf = ConfigFile.get_config_file(SYSLOGCONF) + if syslogconf.exists(): + val = syslogconf.get_match('\s*[^#]+/dev/([^ ]+)', '@1') + else: + val = None + + # don't lower security when not changing security level + if _same_level: + if val: + return + if arg: - _interactive and log(_('Enabling log on console 12')) - syslogconf.exists() and syslogconf.replace_line_matching('\s*[^#]+/dev/tty12', expr + ' /dev/tty12', 1) + if dev != val: + _interactive and log(_('Enabling log on console')) + syslogconf.exists() and syslogconf.replace_line_matching('\s*[^#]+/dev/', expr + ' /dev/' + dev, 1) else: - _interactive and log(_('Disabling log on console 12')) - syslogconf.exists() and syslogconf.remove_line_matching('\s*[^#]+/dev/tty12') + if val != None: + _interactive and log(_('Disabling log on console')) + syslogconf.exists() and syslogconf.remove_line_matching('\s*[^#]+/dev/') + +CRON_ENTRY = '*/1 * * * * root /usr/share/msec/promisc_check.sh' +CRON_REGEX = '[^#]+/usr/share/msec/promisc_check.sh' def enable_promisc_check(arg): ''' Activate/Disable ethernet cards promiscuity check.''' cron = ConfigFile.get_config_file(CRON) - + + val = cron.exists() and cron.get_match(CRON_REGEX) + + # don't lower security when not changing security level + if _same_level: + if val == CRON_ENTRY: + return + 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) + if val != CRON_ENTRY: + _interactive and log(_('Activating periodic promiscuity check')) + cron.replace_line_matching(CRON_REGEX, CRON_ENTRY, 1) else: - _interactive and log(_('Disabling periodic promiscuity check')) - cron.remove_line_matching('[^#]+/usr/share/msec/promisc_check.sh') + if val: + _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.''' @@ -449,32 +587,61 @@ def enable_security_check(arg): cron.remove_line_matching('[^#]+/usr/share/msec/security.sh') securitycron = ConfigFile.get_config_file(SECURITYCRON) + + val = securitycron.exists() + # don't lower security when not changing security level + if _same_level: + if val: + return + if arg: - _interactive and log(_('Activating daily security check')) - securitycron.symlink(SECURITYSH) + if not val: + _interactive and log(_('Activating daily security check')) + securitycron.symlink(SECURITYSH) else: - _interactive and log(_('Disabling daily security check')) - securitycron.unlink() - + if val: + _interactive and log(_('Disabling daily security check')) + securitycron.unlink() + +ALL_REGEXP = '^ALL:ALL:DENY' +ALL_LOCAL_REGEXP = '^ALL:ALL EXCEPT 127\.0\.0\.1:DENY' 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 hostsdeny.exists(): + if hostsdeny.get_match(ALL_REGEXP): + val = NONE + elif hostsdeny.get_match(ALL_LOCAL_REGEXP): + val = LOCAL + else: + val = ALL + else: + val = ALL + + # don't lower security when not changing security level + if _same_level: + if val == NONE or (val == LOCAL and arg == ALL): + return + 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) + if arg != val: + _interactive and log(_('Authorizing all services')) + hostsdeny.remove_line_matching(ALL_REGEXP, 1) + hostsdeny.remove_line_matching(ALL_LOCAL_REGEXP, 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) + if arg != val: + _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) + if arg != val: + _interactive and log(_('Disabling non local services')) + hostsdeny.remove_line_matching(ALL_REGEXP, 1) + hostsdeny.replace_line_matching(ALL_LOCAL_REGEXP, 'ALL:ALL EXCEPT 127.0.0.1:DENY', 1) else: error(_('authorize_services invalid argument: %s') % arg) @@ -485,7 +652,7 @@ def set_zero_one_variable(file, variable, value, secure_value, one_msg, zero_msg f = ConfigFile.get_config_file(file) if f.exists(): - val = string.atoi(f.get_shell_variable(variable)) + val = int(f.get_shell_variable(variable)) else: val = None @@ -513,14 +680,23 @@ def enable_dns_spoofing_protection(arg, alert=1): \\fIalert\\fP is true, also reports to syslog.''' hostconf = ConfigFile.get_config_file(HOSTCONF) + val = hostconf.exists() and hostconf.get_match('nospoof\s+on') + + # don't lower security when not changing security level + if _same_level: + if val: + return + if arg: - _interactive and log(_('Enabling name resolution spoofing protection')) - hostconf.replace_line_matching('nospoof', 'nospoof on', 1) - hostconf.replace_line_matching('spoofalert', 'spoofalert on', (alert != 0)) + if not val: + _interactive and log(_('Enabling name resolution spoofing protection')) + hostconf.replace_line_matching('nospoof', 'nospoof on', 1) + hostconf.replace_line_matching('spoofalert', 'spoofalert on', (alert != 0)) else: - _interactive and log(_('Disabling name resolution spoofing protection')) - hostconf.remove_line_matching('nospoof') - hostconf.remove_line_matching('spoofalert') + if val: + _interactive and log(_('Disabling name resolution spoofing protection')) + hostconf.remove_line_matching('nospoof') + hostconf.remove_line_matching('spoofalert') def accept_icmp_echo(arg): ''' Accept/Refuse icmp echo.''' @@ -540,68 +716,141 @@ def enable_log_strange_packets(arg): def enable_libsafe(arg): ''' Enable/Disable libsafe if libsafe is found on the system.''' + + ldsopreload = ConfigFile.get_config_file(LDSOPRELOAD) + + val = ldsopreload.exists() and ldsopreload.get_match('/lib/libsafe.so.2') + + # don't lower security when not changing security level + if _same_level: + if val: + return + 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) + if not val: + if os.path.exists(Config.get_config('root', '') + '/lib/libsafe.so.2'): + _interactive and log(_('Enabling libsafe')) + 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') + if val: + _interactive and log(_('Disabling libsafe')) + ldsopreload.remove_line_matching('[^#]*libsafe') + +LENGTH_REGEXP = '^(password\s+required\s+/lib/security/pam_cracklib.so.*?)\sminlen=([0-9]+)\s(.*)' +NDIGITS_REGEXP = '^(password\s+required\s+/lib/security/pam_cracklib.so.*?)\sdcredit=([0-9]+)\s(.*)' +UCREDIT_REGEXP = '^(password\s+required\s+/lib/security/pam_cracklib.so.*?)\sucredit=([0-9]+)\s(.*)' 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)) + + if passwd.exists(): + val_length = passwd.get_match(LENGTH_REGEXP, '@2') + if val_length: + val_length = int(val_length) + + val_ndigits = passwd.get_match(NDIGITS_REGEXP, '@2') + if val_ndigits: + val_ndigits = int(val_ndigits) + + val_ucredit = passwd.get_match(UCREDIT_REGEXP, '@2') + if val_ucredit: + val_ucredit = int(val_ucredit) + + # don't lower security when not changing security level + if _same_level: + if val_length > length and val_ndigits > ndigits and val_ucredit > nupper: + return + + if val_length > length: + length = val_length + + if val_ndigits > ndigits: + ndigits = val_ndigits + + if val_ucredit > nupper: + nupper = val_ucredit + + if passwd.exists() and (val_length != length or val_ndigits != ndigits or val_ucredit != nupper): + _interactive and log(_('Setting minimum password length %d') % length) + (passwd.replace_line_matching(LENGTH_REGEXP, + '@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.replace_line_matching(NDIGITS_REGEXP, + '@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)) + (passwd.replace_line_matching(UCREDIT_REGEXP, + '@1 ucredit=%s @3' % nupper) or \ + passwd.replace_line_matching('^password\s+required\s+/lib/security/pam_cracklib.so.*', + '@0 ucredit=%s ' % nupper)) +PASSWORD_REGEXP = '^\s*auth\s+sufficient\s+/lib/security/pam_permit.so' def enable_password(arg): ''' Use password to authenticate users.''' system_auth = ConfigFile.get_config_file(SYSTEM_AUTH) + val = system_auth.exists() and system_auth.get_match(PASSWORD_REGEXP) + + # don't lower security when not changing security level + if _same_level: + if not val: + return + 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') + if val: + _interactive and log(_('Using password to authenticate users')) + system_auth.remove_line_matching(PASSWORD_REGEXP) 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') + if not val: + _interactive and log(_('Don\'t use password to authenticate users')) + system_auth.replace_line_matching(PASSWORD_REGEXP, 'auth sufficient /lib/security/pam_permit.so') or \ + system_auth.insert_before('auth\s+sufficient', 'auth sufficient /lib/security/pam_permit.so') +SULOGIN_REGEXP = '~~:S:wait:/sbin/sulogin' def enable_sulogin(arg): ''' Enable/Disable sulogin(8) in single user level.''' inittab = ConfigFile.get_config_file(INITTAB) + val = inittab.exists() and inittab.get_match(SULOGIN_REGEXP) + + # don't lower security when not changing security level + if _same_level: + if val: + return + if arg: - _interactive and log(_('Enabling sulogin in single user runlevel')) - inittab.replace_line_matching('[^#]+:S:', '~~:S:wait:/sbin/sulogin', 1) + if not val: + _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') + if val: + _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) + val = mseccron.exists() + + # don't lower security when not changing security level + if _same_level: + if val: + return + if arg: - _interactive and log(_('Enabling msec periodic runs')) - mseccron.symlink(MSECBIN) + if arg != val: + _interactive and log(_('Enabling msec periodic runs')) + mseccron.symlink(MSECBIN) else: - _interactive and log(_('Disabling msec periodic runs')) - mseccron.unlink() + if arg != val: + _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 @@ -609,14 +858,24 @@ def enable_at_crontab(arg): cronallow = ConfigFile.get_config_file(CRONALLOW) atallow = ConfigFile.get_config_file(ATALLOW) + val_cronallow = cronallow.exists() and cronallow.get_match('root') + val_atallow = atallow.exists() and atallow.get_match('root') + + # don't lower security when not changing security level + if _same_level: + if val_cronallow and val_atallow: + return + if arg: - _interactive and log(_('Enabling crontab and at')) - cronallow.exists() and cronallow.move(SUFFIX) - atallow.exists() and atallow.move(SUFFIX) + if val_cronallow or val_atallow: + _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) + if not val_cronallow or not val_atallow: + _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) @@ -665,8 +924,18 @@ def password_aging(max, inactive=-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]) + new_max = max + new_inactive = inactive + # don't lower security when not changing security level + if _same_level: + if current_max < max and current_inactive < inactive: + continue + if current_max < max: + new_max = current_max + if current_inactive < inactive: + new_inactive = current_inactive + if new_max != current_max or current_inactive != new_inactive: + cmd = '/usr/bin/chage -M %d -I %d %s' % (new_max, new_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: @@ -730,3 +999,4 @@ def set_interactive(v): _interactive = v # libmsec.py ends here + |