aboutsummaryrefslogtreecommitdiffstats
path: root/share
diff options
context:
space:
mode:
Diffstat (limited to 'share')
-rw-r--r--share/.cvsignore2
-rw-r--r--share/CHANGES40
-rw-r--r--share/Config.py46
-rw-r--r--share/ConfigFile.py376
-rw-r--r--share/Log.py53
-rwxr-xr-xshare/Perms.py173
-rw-r--r--share/README66
-rwxr-xr-xshare/compile.py17
-rw-r--r--share/libmsec.py488
-rwxr-xr-xshare/msec48
-rwxr-xr-xshare/msec.py195
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