diff options
author | Frederic Lepied <flepied@mandriva.com> | 2002-01-17 19:56:19 +0000 |
---|---|---|
committer | Frederic Lepied <flepied@mandriva.com> | 2002-01-17 19:56:19 +0000 |
commit | 68ebfc5fc46591d84ed1b0887c1c0b19ba7aa47d (patch) | |
tree | 4e2d7e6f16f8bb65f61c2913dfe35fd767e62040 /share | |
parent | 1ff71ce4eb92eeeb629ce2669a8db801f182249c (diff) | |
download | msec-68ebfc5fc46591d84ed1b0887c1c0b19ba7aa47d.tar msec-68ebfc5fc46591d84ed1b0887c1c0b19ba7aa47d.tar.gz msec-68ebfc5fc46591d84ed1b0887c1c0b19ba7aa47d.tar.bz2 msec-68ebfc5fc46591d84ed1b0887c1c0b19ba7aa47d.tar.xz msec-68ebfc5fc46591d84ed1b0887c1c0b19ba7aa47d.zip |
0.17
Diffstat (limited to 'share')
-rw-r--r-- | share/.cvsignore | 2 | ||||
-rw-r--r-- | share/CHANGES | 40 | ||||
-rw-r--r-- | share/Config.py | 46 | ||||
-rw-r--r-- | share/ConfigFile.py | 376 | ||||
-rw-r--r-- | share/Log.py | 53 | ||||
-rwxr-xr-x | share/Perms.py | 173 | ||||
-rw-r--r-- | share/README | 66 | ||||
-rwxr-xr-x | share/compile.py | 17 | ||||
-rw-r--r-- | share/libmsec.py | 488 | ||||
-rwxr-xr-x | share/msec | 48 | ||||
-rwxr-xr-x | share/msec.py | 195 |
11 files changed, 1504 insertions, 0 deletions
diff --git a/share/.cvsignore b/share/.cvsignore new file mode 100644 index 0000000..34fcef1 --- /dev/null +++ b/share/.cvsignore @@ -0,0 +1,2 @@ +*.pyo +*.pyc diff --git a/share/CHANGES b/share/CHANGES new file mode 100644 index 0000000..0e66020 --- /dev/null +++ b/share/CHANGES @@ -0,0 +1,40 @@ +changes between version 0.16 and 0.17 +===================================== + +msec utility changes: + + * handle shell timeout (level 4 and 5) + * limit shell history (level 4 and 5) + * su only for wheel group (level 5) + * sulogin for single user mode (level 4 and 5) + * various sysctl.conf settings for icmp and network parameters + * password aging (level 4 and 5) + * suppress /etc/issue.net (level 4 and 5) and /etc/issue (level 5) + * removed manipulation of the groups of users + * removed removal of services + * logging in syslog according to the guideline for explanations in tools + * rewritten in python + +msec can be used to change level and it's also run hourly by cron to +maintain the security level on the system. Only the minimum of changes +on the filesystem are applied and the minimum of programs started. + +Periodic security checks changes: + + * added an rpm database check (rpm -va and rpm -qa) + * report when a user other than root is at uid 0 + +Permissions settings changes: + + * / + * removed audio group handling because it has always conflicted with pam_console + * handle /var/log sub-directories in a generic manner + * /etc/rc.d/init.d/* + * corrected ssh related paths + * /etc/sysconfig + * /proc + * corrected gcc files + * rpm related files to avoid exposing what is installed + * /var/lock/subsys + * added a local.perm to allow modifications without modifying level perms + * rewritten in python diff --git a/share/Config.py b/share/Config.py new file mode 100644 index 0000000..ee2c10e --- /dev/null +++ b/share/Config.py @@ -0,0 +1,46 @@ +#--------------------------------------------------------------- +# Project : Linux-Mandrake +# Module : msec2 +# File : Config.py +# Version : $Id$ +# Author : Frederic Lepied +# Created On : Thu Dec 6 19:54:35 2001 +# Purpose : configuration settings +#--------------------------------------------------------------- + +import sys + +CONFIG='/etc/security/msec2.conf' + +_config={ 'root' : '', + 'run_commands': 1, + 'log': 'syslog', + } +try: + execfile(CONFIG, _config) +except IOError: + #sys.stderr.write("no config file in %s. Using default values.\n" % CONFIG) + pass + +def get_config(name, default=None): + try: + return _config[name] + except KeyError: + return default + +def set_config(name, value): + _config[name] = value + +def converthexa(array): + result="" + for c in array: + o=ord(c) + d=o/16 + u=o-(d*16) + result=result + "%x%x" % (d, u) + return result + +def hashstring(str): + return converthexa(md5.new(str).digest()) + +# Config.py ends here diff --git a/share/ConfigFile.py b/share/ConfigFile.py new file mode 100644 index 0000000..3e24b9b --- /dev/null +++ b/share/ConfigFile.py @@ -0,0 +1,376 @@ +#--------------------------------------------------------------- +# Project : Mandrake Linux +# Module : msec2 +# File : ConfigFile.py +# Version : $Id$ +# Author : Frederic Lepied +# Created On : Wed Dec 5 21:42:49 2001 +# Purpose : class abstraction to handle configuration +# files. +#--------------------------------------------------------------- + +import re +import string +import os +import stat +import Config +import commands +from Log import * +import gettext + +try: + cat = gettext.Catalog('msec') + _ = cat.gettext +except IOError: + _ = str + +BEFORE=0 +INSIDE=1 +AFTER=2 + +space = re.compile('\s') + +class ConfigFiles: + def __init__(self): + self.files = {} + self.modified_files = [] + self.action_assoc = [] + + def add(self, file, path): + self.files[path] = file + + def modified(self, path): + if not path in self.modified_files: + self.modified_files.append(path) + + def get_config_file(self, path, suffix): + try: + return self.files[path] + except KeyError: + return ConfigFile(path, suffix, self) + + def add_config_assoc(self, regex, action): + self.action_assoc.append((re.compile(regex), action)) + +all_files=ConfigFiles() + +def move(old, new): + try: + os.unlink(new) + except OSError: + pass + os.rename(old, new) + +class ConfigFile: + def __init__(self, path, suffix=None, meta=all_files): + self.meta=meta + self.path = Config.get_config('root', '') + path + self.is_modified = 0 + self.is_touched = 0 + self.is_deleted = 0 + self.is_moved = 0 + self.suffix = suffix + self.lines = None + self.sym_link = None + self.meta.add(self, path) + + def get_lines(self): + if self.lines == None: + file=None + try: + file = open(self.path, 'r') + except IOError: + if self.suffix: + try: + moved = self.path + self.suffix + file = open(moved, 'r') + move(moved, self.path) + self.meta.modified(self.path) + except IOError: + self.lines = [] + else: + self.lines = [] + if file: + self.lines = string.split(file.read(), "\n") + file.close() + return self.lines + + def append(self, value): + lines = self.lines + l = len(lines) + if l > 0 and lines[l - 1] == '': + lines.insert(l - 1, value) + else: + lines.append(value) + lines.append('') + + def modified(self): + self.is_modified = 1 + return self + + def touch(self): + self.is_touched = 1 + return self + + def symlink(self, link): + self.sym_link = link + return self + + def exists(self): + return os.path.exists(self.path) or (self.suffix and os.path.exists(self.path + self.suffix)) + + def move(self, suffix): + self.suffix = suffix + self.is_moved = 1 + + def unlink(self): + self.is_deleted = 1 + self.lines=[] + return self + + def write(self): + if self.is_deleted: + if self.exists(): + os.unlink(self.path) + log(_('deleted %s') % (self.path,)) + elif self.is_modified: + content = string.join(self.lines, "\n") + file = open(self.path, 'w') + file.write(content) + file.close() + self.meta.modified(self.path) + elif self.is_touched: + if os.path.exists(self.path): + os.utime(self.path, None) + elif self.suffix and os.path.exists(self.path + self.suffix): + move(self.path + self.suffix, self.path) + os.utime(self.path, None) + else: + self.lines = [] + self.is_modified = 1 + file = open(self.path, 'w') + file.close() + log(_('touched file %s') % (self.path,)) + elif self.sym_link: + done = 0 + if self.exists(): + full = os.lstat(self.path) + if stat.S_ISLNK(full[stat.ST_MODE]): + link = os.readlink(self.path) + # to be fixed: resolv relative symlink + done = (link == self.sym_link) + if not done: + os.unlink(self.path) + log(_('deleted %s') % (self.path,)) + if not done: + os.symlink(self.sym_link, self.path) + log(_('made symbolic link from %s to %s') % (self.sym_link, self.path)) + + if self.is_moved: + move(self.path, self.path + self.suffix) + log(_('moved file %s to %s') % (self.path, self.path + self.suffix)) + self.meta.modified(self.path) + self.is_touched = 0 + self.is_modified = 0 + self.is_deleted = 0 + self.is_moved = 0 + + def set_shell_variable(self, var, value, start=None, end=None): + regex = re.compile('^' + var + '="?([^#"]+)"?(.*)') + lines = self.get_lines() + idx=0 + value=str(value) + + if start: + status = BEFORE + start = re.compile(start) + else: + status = INSIDE + + if end: + end = re.compile(end) + + idx = None + for idx in range(0, len(lines)): + line = lines[idx] + if status == BEFORE: + if start.search(line): + status = INSIDE + else: + continue + elif end and end.search(line): + break + res = regex.search(line) + if res: + if res.group(1) != value: + if space.search(value): + lines[idx] = var + '="' + value + '"' + res.group(2) + else: + lines[idx] = var + '=' + value + res.group(2) + self.modified() + log(_('set variable %s to %s in %s') % (var, value, self.path,)) + return self + if space.search(value): + s = var + '="' + value + '"' + else: + s = var + '=' + value + if idx == None or idx == len(lines): + self.append(s) + else: + lines.insert(idx, s) + + self.modified() + log(_('set variable %s to %s in %s') % (var, value, self.path,)) + return self + + def get_shell_variable(self, var): + regex = re.compile('^' + var + '="?([^#"]+)"?(.*)') + lines = self.get_lines() + for idx in range(0, len(lines)): + res = regex.search(lines[idx]) + if res: + return res.group(1) + return None + + def get_match(self, regex, replace=None): + r=re.compile(regex) + lines = self.get_lines() + matches = 0 + for idx in range(0, len(lines)): + res = r.search(lines[idx]) + if res: + s = substitute_re_result(res, replace) + return s + return None + + def replace_line_matching(self, regex, value, at_end_if_not_found=0, all=0, start=None, end=None): + r=re.compile(regex) + lines = self.get_lines() + matches = 0 + + if start: + status = BEFORE + start = re.compile(start) + else: + status = INSIDE + + if end: + end = re.compile(end) + + idx = None + for idx in range(0, len(lines)): + line = lines[idx] + if status == BEFORE: + if start.search(line): + status = INSIDE + else: + continue + elif end and end.search(line): + break + res = r.search(line) + if res: + s = substitute_re_result(res, value) + matches = matches + 1 + if s != line: + log(_("replaced in %s the line %d:\n%s\nwith the line:\n%s") % (self.path, idx, line, s)) + lines[idx] = s + self.modified() + if not all: + return matches + if matches == 0 and at_end_if_not_found: + log(_("appended in %s the line:\n%s") % (self.path, value)) + if idx == None or idx == len(lines): + self.append(value) + else: + lines.insert(idx, value) + self.modified() + matches = matches + 1 + return matches + + def insert_after(self, regex, value, at_end_if_not_found=0, all=0): + matches = 0 + r=re.compile(regex) + lines = self.get_lines() + for idx in range(0, len(lines)): + res = r.search(lines[idx]) + if res: + s = substitute_re_result(res, value) + log(_("inserted in %s after the line %d:\n%s\nthe line:\n%s") % (self.path, idx, lines[idx], s)) + lines.insert(idx+1, s) + self.modified() + matches = matches + 1 + if not all: + return matches + if matches == 0 and at_end_if_not_found: + log(_("appended in %s the line:\n%s") % (self.path, value)) + self.append(value) + self.modified() + matches = matches + 1 + return matches + + def insert_at(self, idx, value): + lines = self.get_lines() + try: + lines.insert(idx, value) + log(_("inserted in %s at the line %d:\n%s") % (self.path, idx, value)) + self.modified() + return 1 + except KeyError: + return 0 + + def remove_line_matching(self, regex, all=0): + matches = 0 + r=re.compile(regex) + lines = self.get_lines() + for idx in range(len(lines) - 1, -1, -1): + res = r.search(lines[idx]) + if res: + log(_("removing in %s the line %d:\n%s") % (self.path, idx, lines[idx])) + lines.pop(idx) + self.modified() + matches = matches + 1 + if not all: + return matches + return matches + +# utility funtions + +def substitute_re_result(res, s): + for idx in range(0, (res.lastindex or 0) + 1): + subst = res.group(idx) or '' + s = string.replace(s, '@' + str(idx), subst) + return s + +def write_files(): + global all_files + + run_commands = Config.get_config('run_commands', 0) + for f in all_files.files.values(): + f.write() + + for f in all_files.modified_files: + for a in all_files.action_assoc: + res = a[0].search(f) + if res: + s = substitute_re_result(res, a[1]) + if run_commands != '0': + log(_('%s modified so launched command: %s') % (f, s)) + cmd = commands.getstatusoutput(s) + if cmd[0] == 0: + log(cmd[1]) + else: + error(cmd[1]) + else: + log(_('%s modified so should have run command: %s') % (f, s)) + +def get_config_file(path, suffix=None): + global all_files + + return all_files.get_config_file(path, suffix) + +def add_config_assoc(regex, action): + global all_files + + return all_files.add_config_assoc(regex, action) + +# ConfigFile.py ends here diff --git a/share/Log.py b/share/Log.py new file mode 100644 index 0000000..bcda819 --- /dev/null +++ b/share/Log.py @@ -0,0 +1,53 @@ +#--------------------------------------------------------------- +# Project : Mandrake Linux +# Module : msec2 +# File : Log.py +# Version : $Id$ +# Author : Frederic Lepied +# Created On : Wed Dec 5 23:50:29 2001 +# Purpose : write log through syslog conforming to +# the Mandrake Linux guideline for the explanations +# in tools. Errors are reported to stderr. +#--------------------------------------------------------------- + +import syslog +import sys +import string +import Config + +_name = '' +_use_syslog = 1 + +def initlog(name, facility = syslog.LOG_AUTH): + global _name + global _use_syslog + + _use_syslog = (Config.get_config('log', 'syslog') == 'syslog') + + if _use_syslog: + syslog.openlog(name, 0, facility) + + _name = name + +def log(s, level = syslog.LOG_INFO): + global _use_syslog + + if _use_syslog: + for l in string.split(s, '\n'): + syslog.syslog(level, l) + else: + sys.stderr.write(s + '\n') + return 1 + +def closelog(): + global _use_syslog + + if _use_syslog: + syslog.closelog() + +def error(s): + global _name + + sys.stderr.write(_name + ': ' + s + '\n') + +# Log.py ends here diff --git a/share/Perms.py b/share/Perms.py new file mode 100755 index 0000000..8a8c983 --- /dev/null +++ b/share/Perms.py @@ -0,0 +1,173 @@ +#!/usr/bin/python -O +#--------------------------------------------------------------- +# Project : Mandrake Linux +# Module : msec +# File : Perms.py +# Version : $Id$ +# Author : Frederic Lepied +# Created On : Fri Dec 7 23:33:49 2001 +# Purpose : fix permissions and owner/group of files +# and directories. +#--------------------------------------------------------------- + +import glob +import re +import string +import os +import stat +import pwd +import grp +import Config +from Log import * +import gettext + +try: + cat = gettext.Catalog('msec') + _ = cat.gettext +except IOError: + _ = str + +comment_regex = re.compile('^\s*#|^\s*$') + +user = {} +group = {} +userid = {} +groupid = {} + +def get_user_id(name): + try: + return user[name] + except KeyError: + try: + user[name] = pwd.getpwnam(name)[2] + except KeyError: + error(_('user name %s not found') % name) + user[name] = -1 + return user[name] + +def get_user_name(id): + try: + return userid[id] + except KeyError: + try: + userid[id] = pwd.getpwuid(id)[0] + except KeyError: + error(_('user name not found for id %d') % id) + userid[id] = str(id) + return userid[id] + +def get_group_id(name): + try: + return group[name] + except KeyError: + try: + group[name] = grp.getgrnam(name)[2] + except KeyError: + error(_('group name %s not found') % name) + group[name] = -1 + return group[name] + +def get_group_name(id): + try: + return groupid[id] + except KeyError: + try: + groupid[id] = grp.getgrgid(id)[0] + except KeyError: + error(_('group name not found for id %d') % id) + groupid[id] = str(id) + return groupid[id] + +assoc = {} + +def fix_perms(path): + try: + file = open(path, 'r') + except IOError: + return + root = Config.get_config('root', '') + lineno = 0 + for line in file.readlines(): + lineno = lineno + 1 + if comment_regex.search(line): + continue + fields = re.split('\s*', line) + newmode = int(fields[2], 8) + if fields[1] == 'current': + user = group = -1 + user_str = group_str = '' + else: + (user_str, group_str) = string.split(fields[1], '.') + user = get_user_id(user_str) + group = get_group_id(group_str) + if len(fields) == 4: + for f in glob.glob(fields[0]): + try: + full = os.stat(f) + except OSError: + continue + mode = stat.S_IMODE(full[stat.ST_MODE]) + newperm = newmode + if stat.S_ISDIR(full[stat.ST_MODE]): + if newperm & 0400: + newperm = newperm | 0100 + if newperm & 0040: + newperm = newperm | 0010 + if newperm & 0004: + newperm = newperm | 0001 + uid = full[stat.ST_UID] + gid = full[stat.ST_GID] + if f != '/' and f[-1] == '/': + f = f[:-1] + if f[-2:] == '/.': + f = f[:-2] + assoc[f] = (mode, uid, gid, newperm, user, group, user_str, group_str) + else: + error(_('invalid syntax in %s line %d') % (path, lineno)) + file.close() + +def act(): + for f in assoc.keys(): + (mode, uid, gid, newperm, user, group, user_str, group_str) = assoc[f] + #print f, (mode, uid, gid, newperm, user, group) + if mode != newperm: + log(_('changed mode of %s from %o to %o') % (f, mode, newperm)) + os.chmod(f, newperm) + if user != -1: + if user != uid: + log(_('changed owner of %s from %s to %s') % (f, get_user_name(uid), user_str)) + os.chown(f, user, -1) + if group != -1: + if group != gid: + log(_('changed group of %s from %s to %s') % (f, get_group_name(gid), group_str)) + os.chown(f, -1, group) + +def chmod(f, newperm): + try: + full = os.stat(f) + except OSError: + return 0 + mode = stat.S_IMODE(full[stat.ST_MODE]) + if stat.S_ISDIR(full[stat.ST_MODE]): + if newperm & 0400: + newperm = newperm | 0100 + if newperm & 0040: + newperm = newperm | 0010 + if newperm & 0004: + newperm = newperm | 0001 + if mode != newperm: + log(_('changed mode of %s from %o to %o') % (f, mode, newperm)) + os.chmod(f, newperm) + return 1 + +if __name__ == '__main__': + import sys + + initlog('msec') + + for p in sys.argv[1:]: + fix_perms(p) + + act() + +# Perms.py ends here diff --git a/share/README b/share/README new file mode 100644 index 0000000..76b6a8f --- /dev/null +++ b/share/README @@ -0,0 +1,66 @@ +****************** +Configurations files in /etc/security/msec/ +Shell scripts in /usr/share/msec. +****************** + +Suggest & Comment : +yoann@mandrakesoft.com + +****************** +Doc of the rewritting in python: + + 0 1 2 3 4 5 +root umask 022 022 022 022 022 077 +shell timeout 0 0 0 0 3600 900 +deny services none none none none local all +su only for wheel grp no no no no no yes +user umask 022 022 022 022 077 077 +shell history size default default default default 0 0 +direct root login yes yes yes yes no no +sulogin for single user no no no no yes yes +user list in [kg]dm yes yes yes yes no no +promisc check no no no no yes yes +ignore icmp echo no no no no yes yes +ignore bogus error responses no no no no yes yes +enable libasfe no no no no yes yes +allow reboot by user yes yes yes yes no no +allow crontab/at yes yes yes yes no no +password aging no no no no 60 30 +allow autologin yes yes yes no no no +console log no no no yes yes yes +issues yes yes yes local local no +ip spoofing protection no no no yes yes yes +log stange ip packets no no no yes yes yes +periodic security check no yes yes yes yes yes +allow X connections yes local local no no no +run msec by cron yes yes yes yes yes yes + +Periodic security checks by level: + + 0 1 2 3 4 5 +CHECK_SECURITY no yes yes yes yes yes +CHECK_PERMS no no no yes yes yes +CHECK_SUID_ROOT no no yes yes yes yes +CHECK_SUID_MD5 no no yes yes yes yes +CHECK_SUID_GROUP no no yes yes yes yes +CHECK_WRITEABLE no no yes yes yes yes +CHECK_UNOWNED no no no no yes yes +CHECK_PROMISC no no no no yes yes +CHECK_OPEN_PORT no no no yes yes yes +CHECK_PASSWD no no no yes yes yes +CHECK_SHADOW no no no yes yes yes +TTY_WARN no no no no yes yes +MAIL_WARN no no no yes yes yes +SYSLOG_WARN no no yes yes yes yes +RPM_CHECK no no no yes yes yes + +These variables are configured by the user: + +MAIL_USER the user to send the dayly reports. If not set, the email is +sent to root. + +PERM_LEVEL is used to determine which file to use to fix +permissions/owners/groups (in /etc/security/msec/perm.$PERM_LEVEL). If +not set, the SECURE_LEVEL is used instead. If the file +/etc/security/msec/perm.local exists, it's used too. + diff --git a/share/compile.py b/share/compile.py new file mode 100755 index 0000000..a325016 --- /dev/null +++ b/share/compile.py @@ -0,0 +1,17 @@ +#!/usr/bin/python -O +############################################################################# +# File : compile.py +# Package : rpmlint +# Author : Frederic Lepied +# Created on : Sat Oct 23 23:40:21 1999 +# Version : $Id$ +# Purpose : byte compile all python files given in arguments. +############################################################################# + +import py_compile +import sys + +for f in sys.argv[2:]: + py_compile.compile(f, f+"o", sys.argv[1] + f) + +# compile.py ends here 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 diff --git a/share/msec b/share/msec new file mode 100755 index 0000000..423766c --- /dev/null +++ b/share/msec @@ -0,0 +1,48 @@ +#!/bin/sh +#--------------------------------------------------------------- +# Project : Mandrake Linux +# Module : share +# File : msec +# Version : $Id$ +# Author : Frederic Lepied +# Created On : Thu Dec 13 11:36:50 2001 +#--------------------------------------------------------------- + +MSEC=/usr/share/msec/msec.py + +for a in "$@"; do + last="$a" +done + +if [ -n "$last" ]; then + case $last in + [0-5]) ;; + *) [ -x /usr/share/msec/$last.py ] && MSEC=/usr/share/msec/$last.py;; + esac +else + # no args so try to guess if a custom msec is needed + . /etc/sysconfig/msec + + case "$SECURE_LEVEL" in + [0-5]) ;; + *) MSEC=/usr/share/msec/$SECURE_LEVEL.py;; + esac +fi + +if [ ! -x $MSEC ]; then + echo "/usr/share/msec/$last.py not found or not executable. Aborting" 1>&2 + exit 1 +fi + +if $MSEC "$@"; then + . /etc/sysconfig/msec + + [ -z "$PERM_LEVEL" ] && PERM_LEVEL=$SECURE_LEVEL + + LOCAL= + [ -f /etc/security/msec/perm.local ] && LOCAL=/etc/security/msec/perm.local + + /usr/share/msec/Perms.py /etc/security/msec/perm.$PERM_LEVEL $LOCAL +fi + +# msec.sh ends here diff --git a/share/msec.py b/share/msec.py new file mode 100755 index 0000000..8ab0a3f --- /dev/null +++ b/share/msec.py @@ -0,0 +1,195 @@ +#!/usr/bin/python -O +#--------------------------------------------------------------- +# Project : Mandrake Linux +# Module : msec/share +# File : msec.py +# Version : $Id$ +# Author : Frederic Lepied +# Created On : Wed Dec 5 20:20:21 2001 +#--------------------------------------------------------------- + +from libmsec import * +from Log import * +from Log import _name +import Config +import sys +import os +import string +import getopt +import gettext + +try: + cat = gettext.Catalog('msec') + _ = cat.gettext +except IOError: + _ = str + +# program +_name = 'msec' + +sys.argv[0] = os.path.basename(sys.argv[0]) + +try: + (opt, args) = getopt.getopt(sys.argv[1:], 'o:', + ['option']) +except getopt.error: + error(_('Invalid option. Use %s (-o var=<val>...) ([0-5])') % sys.argv[0]) + sys.exit(1) + + +for o in opt: + if o[0] == '-o' or o[0] == '--option': + pair = string.split(o[1], '=') + if len(pair) != 2: + error(_('Invalid option format %s %s: use -o var=<val>') % (o[0], o[1])) + sys.exit(1) + else: + Config.set_config(pair[0], pair[1]) + +interactive = sys.stdin.isatty() +set_interactive(interactive) + +# initlog must be done after processing the option because we can change +# the way to report log with options... +if interactive: + import syslog + + initlog('msec', syslog.LOG_LOCAL1) +else: + initlog('msec') + +if len(args) == 0: + level = get_secure_level() + if level == None: + error(_('Secure level not set. Use %s <secure level> to set it.') % sys.argv[0]) + sys.exit(1) +else: + level = args[0] + +try: + level = int(level) +except ValueError: + error(_('Invalid secure level %s. Use %s [0-5] to set it.') % (level, sys.argv[0])) + sys.exit(1) + +if level < 0 or level > 5: + error(_('Invalid secure level %s. Use %s [0-5] to set it.') % (level, sys.argv[0])) + sys.exit(1) + +set_secure_level(level) + +server=(level in range(3, 6)) + +# for all levels: min length = 2 * (level - 1) and for level 4,5 makes mandatory +# to have at least one upper case character and one digit. +if level > 1: + password_length = (level - 1) * 2 +else: + password_length = 0 + +password_length(password_length, level / 4, level / 4) + +enable_ip_spoofing_protection(server) + +# differences between level 5 and others +if level == 5: + set_root_umask('077') + set_shell_timeout(900) + deny_all_services() + enable_pam_wheel_for_su() +else: + set_root_umask('022') + if level == 4: + set_shell_timeout(3600) + deny_non_local_services() + else: + set_shell_timeout(0) + authorize_all_services() + disable_pam_wheel_for_su() + +# differences between level 4,5 and others +if level >= 4: + set_user_umask('077') + set_shell_history_size(10) + forbid_root_login() + enable_sulogin() + forbid_user_list() + enable_promisc_check() + ignore_icmp_echo() + ignore_bogus_error_responses() + enable_libsafe() + forbid_reboot() + disable_at_crontab() + if level == 4: + password_aging(60) + else: + password_aging(30) +else: + set_user_umask('022') + set_shell_history_size(-1) + allow_root_login() + disable_sulogin() + allow_user_list() + disable_promisc_check() + accept_icmp_echo() + accept_bogus_error_responses() + disable_libsafe() + allow_reboot() + enable_at_crontab() + password_aging(99999) + +# differences between level 3,4,5 and others +if server: + forbid_autologin() + enable_console_log() + forbid_issues((level != 5)) + enable_log_strange_packets() +else: + allow_autologin() + disable_console_log() + allow_issues() + disable_log_strange_packets() + +# differences between level 0 and others +if level != 0: + enable_security_check() + if level < 3: + allow_local_x_connections() + else: + restrict_x_connections() +else: + disable_security_check() + allow_x_connections() + +# msec cron +enable_msec_cron() + +# 0 1 2 3 4 5 +FILE_CHECKS = {'CHECK_SECURITY' : ('no', 'yes', 'yes', 'yes', 'yes', 'yes', ), + 'CHECK_PERMS' : ('no', 'no', 'no', 'yes', 'yes', 'yes', ), + 'CHECK_SUID_ROOT' : ('no', 'no', 'yes', 'yes', 'yes', 'yes', ), + 'CHECK_SUID_MD5' : ('no', 'no', 'yes', 'yes', 'yes', 'yes', ), + 'CHECK_SUID_GROUP' : ('no', 'no', 'yes', 'yes', 'yes', 'yes', ), + 'CHECK_WRITEABLE' : ('no', 'no', 'yes', 'yes', 'yes', 'yes', ), + 'CHECK_UNOWNED' : ('no', 'no', 'no', 'no', 'yes', 'yes', ), + 'CHECK_PROMISC' : ('no', 'no', 'no', 'no', 'yes', 'yes', ), + 'CHECK_OPEN_PORT' : ('no', 'no', 'no', 'yes', 'yes', 'yes', ), + 'CHECK_PASSWD' : ('no', 'no', 'no', 'yes', 'yes', 'yes', ), + 'CHECK_SHADOW' : ('no', 'no', 'no', 'yes', 'yes', 'yes', ), + 'TTY_WARN' : ('no', 'no', 'no', 'no', 'yes', 'yes', ), + 'MAIL_WARN' : ('no', 'no', 'no', 'yes', 'yes', 'yes', ), + 'SYSLOG_WARN' : ('no', 'no', 'yes', 'yes', 'yes', 'yes', ), + 'RPM_CHECK' : ('no', 'no', 'no', 'yes', 'yes', 'yes', ), + } + +interactive and log(_('Configuring periodic files checks')) +securityconf = ConfigFile.get_config_file('/etc/security/msec/security.conf') +for k in FILE_CHECKS.keys(): + securityconf.set_shell_variable(k, FILE_CHECKS[k][level]) + +interactive and log(_('Writing config files and then taking needed actions')) +ConfigFile.write_files() + +closelog() + +# msec.py ends here |