aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/libmsec.py165
1 files changed, 148 insertions, 17 deletions
diff --git a/share/libmsec.py b/share/libmsec.py
index d964e74..7906b78 100644
--- a/share/libmsec.py
+++ b/share/libmsec.py
@@ -81,8 +81,13 @@ NONE=0
ALL=1
LOCAL=2
+no=0
+yes=1
+without_password=2
+
ALL_LOCAL_NONE_TRANS = {ALL : 'ALL', NONE: 'NONE', LOCAL : 'LOCAL'}
-YES_NO_TRANS = {1 : 'yes', 0 : 'no'}
+YES_NO_TRANS = {yes : 'yes', no : 'no'}
+ALLOW_ROOT_LOGIN_TRANS = {no : 'no', yes : 'yes', without_password : 'without-password'}
# config files => actions
@@ -96,6 +101,8 @@ ConfigFile.add_config_assoc('^/etc/issue$', '/usr/bin/killall mingetty')
# rules
+################################################################################
+
def changing_level():
'D'
global _same_level
@@ -103,6 +110,8 @@ def changing_level():
# configuration rules
+################################################################################
+
def set_secure_level(level):
msec = ConfigFile.get_config_file(MSEC)
@@ -112,16 +121,22 @@ def set_secure_level(level):
_interactive and log(_('Setting secure level to %s') % level)
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)
@@ -130,6 +145,8 @@ def get_server_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
@@ -147,6 +164,8 @@ during the installation of packages.'''
create_server_link.arg_trans = YES_NO_TRANS
+################################################################################
+
# helper function for set_root_umask and set_user_umask
def set_umask(variable, umask, msg):
'D'
@@ -175,6 +194,8 @@ def set_user_umask(umask):
''' Set the user umask.'''
set_umask('UMASK_USER', umask, 'users')
+################################################################################
+
# the listen_tcp argument is kept for backward compatibility
def allow_x_connections(arg, listen_tcp=None):
''' Allow/Forbid X connections. First arg specifies what is done
@@ -222,6 +243,8 @@ local connection) and NONE (no connection).'''
allow_x_connections.arg_trans=ALL_LOCAL_NONE_TRANS
allow_x_connections.one_arg = 1
+################################################################################
+
STARTX_REGEXP = '(\s*clientargs=".*) -nolisten tcp(.*")'
XSERVERS_REGEXP = '(\s*[^#]+/usr/X11R6/bin/X .*) -nolisten tcp(.*)'
GDMCONF_REGEXP = '(\s*command=.*/X.*?) -nolisten tcp(.*)$'
@@ -260,6 +283,8 @@ to the X server on the tcp port 6000 or not.'''
allow_xserver_to_listen.arg_trans = YES_NO_TRANS
+################################################################################
+
def set_shell_timeout(val):
''' Set the shell timeout. A value of zero means no timeout.'''
@@ -281,6 +306,8 @@ def set_shell_timeout(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)
@@ -306,6 +333,8 @@ def set_shell_history_size(size):
_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)
@@ -354,6 +383,8 @@ def allow_reboot(arg):
allow_reboot.arg_trans = YES_NO_TRANS
+################################################################################
+
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)
@@ -387,6 +418,8 @@ def allow_user_list(arg):
allow_user_list.arg_trans = YES_NO_TRANS
+################################################################################
+
def allow_root_login(arg):
''' Allow/Forbid direct root login.'''
securetty = ConfigFile.get_config_file(SECURETTY)
@@ -448,12 +481,16 @@ def allow_root_login(arg):
allow_root_login.arg_trans = YES_NO_TRANS
+PERMIT_ROOT_LOGIN_REGEXP = '^\s*PermitRootLogin\s+(no|yes|without-password|forced-commands-only)'
+
+################################################################################
+
def allow_remote_root_login(arg):
''' Allow/Forbid remote root login.'''
sshd_config = ConfigFile.get_config_file(SSHDCONFIG)
if sshd_config.exists():
- val = sshd_config.get_match('^\s*PermitRootLogin\s+(no|yes)', '@1')
+ val = sshd_config.get_match(PERMIT_ROOT_LOGIN_REGEXP, '@1')
else:
val = None
@@ -461,21 +498,35 @@ def allow_remote_root_login(arg):
if _same_level:
if val == 'no':
return
+ if val == 'forced-commands-only':
+ return
- val = (val == 'yes')
-
- if arg:
- if val != arg:
+ if val == 'yes':
+ val = yes
+ elif val == 'no':
+ val = no
+ elif val == 'without-password':
+ val = without_password
+ else:
+ val = yes
+
+ if val != arg:
+ if arg == yes:
_interactive and log(_('Allowing remote root login'))
- sshd_config.exists() and sshd_config.replace_line_matching('^\s*PermitRootLogin\s+(no|yes)',
+ sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
'PermitRootLogin yes', 1)
- else:
- if val != arg:
+ elif arg == no:
_interactive and log(_('Forbidding remote root login'))
- sshd_config.exists() and sshd_config.replace_line_matching('^\s*PermitRootLogin\s+(no|yes)',
+ sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
'PermitRootLogin no', 1)
+ elif arg == without_password:
+ _interactive and log(_('Allowing remote root login only by passphrase'))
+ sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
+ 'PermitRootLogin without-password', 1)
-allow_remote_root_login.arg_trans = YES_NO_TRANS
+allow_remote_root_login.arg_trans = ALLOW_ROOT_LOGIN_TRANS
+
+################################################################################
def enable_pam_wheel_for_su(arg):
''' Enabling su only from members of the wheel group or allow su from any user.'''
@@ -511,6 +562,8 @@ def enable_pam_wheel_for_su(arg):
enable_pam_wheel_for_su.arg_trans = YES_NO_TRANS
+################################################################################
+
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.'''
@@ -547,6 +600,8 @@ allowed else only /etc/issue is allowed.'''
allow_issues.arg_trans = YES_NO_TRANS
+################################################################################
+
def allow_autologin(arg):
''' Allow/Forbid autologin.'''
autologin = ConfigFile.get_config_file(AUTOLOGIN)
@@ -572,6 +627,8 @@ def allow_autologin(arg):
allow_autologin.arg_trans = YES_NO_TRANS
+################################################################################
+
def password_loader(value):
'D'
_interactive and log(_('Activating password in boot loader'))
@@ -586,6 +643,8 @@ def password_loader(value):
Perms.chmod(menulst.path, 0600)
# TODO add yaboot support
+################################################################################
+
def nopassword_loader():
'D'
_interactive and log(_('Removing password in boot loader'))
@@ -594,6 +653,8 @@ def nopassword_loader():
menulst = ConfigFile.get_config_file(MENULST)
menulst.exists() and menulst.remove_line_matching('^password\s')
+################################################################################
+
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) and
@@ -625,6 +686,8 @@ enable_console_log.arg_trans = YES_NO_TRANS
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)
@@ -647,6 +710,8 @@ def enable_promisc_check(arg):
enable_promisc_check.arg_trans = YES_NO_TRANS
+################################################################################
+
def enable_security_check(arg):
''' Activate/Disable daily security check.'''
cron = ConfigFile.get_config_file(CRON)
@@ -672,6 +737,8 @@ def enable_security_check(arg):
enable_security_check.arg_trans = YES_NO_TRANS
+################################################################################
+
ALL_REGEXP = '^ALL:ALL:DENY'
ALL_LOCAL_REGEXP = '^ALL:ALL EXCEPT 127\.0\.0\.1:DENY'
def authorize_services(arg):
@@ -715,6 +782,8 @@ if \\fIarg\\fP = LOCAL and none if \\fIarg\\fP = NONE. To authorize the services
authorize_services.arg_trans = ALL_LOCAL_NONE_TRANS
+################################################################################
+
# helper function for enable_ip_spoofing_protection, accept_icmp_echo, accept_broadcasted_icmp_echo,
# accept_bogus_error_responses and enable_log_strange_packets.
def set_zero_one_variable(file, variable, value, secure_value, one_msg, zero_msg):
@@ -742,6 +811,8 @@ def set_zero_one_variable(file, variable, value, secure_value, one_msg, zero_msg
_interactive and log(msg)
f.set_shell_variable(variable, value)
+################################################################################
+
# the alert argument is kept for backward compatibility
def enable_ip_spoofing_protection(arg, alert=1):
''' Enable/Disable IP spoofing protection.'''
@@ -750,6 +821,8 @@ def enable_ip_spoofing_protection(arg, alert=1):
enable_ip_spoofing_protection.arg_trans = YES_NO_TRANS
enable_ip_spoofing_protection.one_arg = 1
+################################################################################
+
def enable_dns_spoofing_protection(arg, alert=1):
''' Enable/Disable name resolution spoofing protection. If
\\fIalert\\fP is true, also reports to syslog.'''
@@ -775,30 +848,40 @@ def enable_dns_spoofing_protection(arg, alert=1):
enable_dns_spoofing_protection.arg_trans = YES_NO_TRANS
+################################################################################
+
def accept_icmp_echo(arg):
''' Accept/Refuse icmp echo.'''
set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_all', not arg, 1, 'Accepting icmp echo', 'Ignoring icmp echo')
accept_icmp_echo.arg_trans = YES_NO_TRANS
+################################################################################
+
def accept_broadcasted_icmp_echo(arg):
''' Accept/Refuse broadcasted icmp echo.'''
set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_broadcasts', not arg, 1, 'Accepting broadcasted icmp echo', 'Ignoring broadcasted icmp echo')
accept_broadcasted_icmp_echo.arg_trans = YES_NO_TRANS
+################################################################################
+
def accept_bogus_error_responses(arg):
''' Accept/Refuse bogus IPv4 error messages.'''
set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_ignore_bogus_error_responses', not arg, 1, 'Accepting bogus icmp error responses', 'Ignoring bogus icmp error responses')
accept_bogus_error_responses.arg_trans = YES_NO_TRANS
+################################################################################
+
def enable_log_strange_packets(arg):
''' Enable/Disable the logging of IPv4 strange packets.'''
set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.log_martians', arg, 1, 'Enabling logging of strange packets', 'Disabling logging of strange packets')
enable_log_strange_packets.arg_trans = YES_NO_TRANS
+################################################################################
+
def enable_libsafe(arg):
''' Enable/Disable libsafe if libsafe is found on the system.'''
@@ -823,20 +906,24 @@ def enable_libsafe(arg):
enable_libsafe.arg_trans = YES_NO_TRANS
-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(.*)'
+################################################################################
+
+LENGTH_REGEXP = re.compile('^(password\s+required\s+/lib/security/pam_cracklib.so.*?)\sminlen=([0-9]+)\s(.*)')
+NDIGITS_REGEXP = re.compile('^(password\s+required\s+/lib/security/pam_cracklib.so.*?)\sdcredit=([0-9]+)\s(.*)')
+UCREDIT_REGEXP = re.compile('^(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.'''
-
- passwd = ConfigFile.get_config_file(PASSWD)
+ passwd = ConfigFile.get_config_file(SYSTEM_AUTH)
+
+ val_length = val_ndigits = val_ucredit = 999999
+
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)
@@ -876,6 +963,8 @@ def password_length(length, ndigits=0, nupper=0):
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.'''
@@ -900,6 +989,40 @@ def enable_password(arg):
enable_password.arg_trans = YES_NO_TRANS
+################################################################################
+
+UNIX_REGEXP = re.compile('(^\s*password\s+sufficient\s+/lib/security/pam_unix.so.*)\sremember=([0-9]+)(.*)')
+
+def password_history(arg):
+ ''' Set the password history length to prevent password reuse.'''
+ system_auth = ConfigFile.get_config_file(SYSTEM_AUTH)
+
+ if system_auth.exists():
+ val = system_auth.get_match(UNIX_REGEXP, '@2')
+
+ if val and val != '':
+ val = int(val)
+ else:
+ val = 0
+ else:
+ val = 0
+
+ # don't lower security when not changing security level
+ if _same_level:
+ if val >= arg:
+ return
+
+ if arg != val:
+ if arg > 0:
+ _interactive and log(_('Setting password history to %d.') % arg)
+ system_auth.replace_line_matching(UNIX_REGEXP, '@1 remember=%d@3' % arg) or \
+ system_auth.replace_line_matching('(^\s*password\s+sufficient\s+/lib/security/pam_unix.so.*)', '@1 remember=%d' % arg)
+ else:
+ _interactive and log(_('Disabling password history'))
+ system_auth.replace_line_matching(UNIX_REGEXP, '@1@3')
+
+################################################################################
+
SULOGIN_REGEXP = '~~:S:wait:/sbin/sulogin'
def enable_sulogin(arg):
''' Enable/Disable sulogin(8) in single user level.'''
@@ -923,6 +1046,8 @@ def enable_sulogin(arg):
enable_sulogin.arg_trans = YES_NO_TRANS
+################################################################################
+
def enable_msec_cron(arg):
''' Enable/Disable msec hourly security check.'''
mseccron = ConfigFile.get_config_file(MSECCRON)
@@ -945,6 +1070,8 @@ def enable_msec_cron(arg):
enable_msec_cron.arg_trans = YES_NO_TRANS
+################################################################################
+
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)).'''
@@ -974,6 +1101,8 @@ def enable_at_crontab(arg):
enable_at_crontab.arg_trans = YES_NO_TRANS
+################################################################################
+
maximum_regex = re.compile('^Maximum:\s*([0-9]+|-1)', re.MULTILINE)
inactive_regex = re.compile('^Inactive:\s*(-?[0-9]+)', re.MULTILINE)
no_aging_list = []
@@ -1042,6 +1171,8 @@ def password_aging(max, inactive=-1):
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