aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile29
-rw-r--r--conf/level.default54
-rw-r--r--conf/level.none54
-rw-r--r--conf/level.secure54
-rw-r--r--conf/perm.092
-rw-r--r--conf/perm.396
-rw-r--r--conf/perm.596
-rw-r--r--conf/perm.default (renamed from conf/perm.2)14
-rw-r--r--conf/perm.none (renamed from conf/perm.1)8
-rw-r--r--conf/perm.secure (renamed from conf/perm.4)49
-rw-r--r--conf/server.default (renamed from conf/server.4)0
-rw-r--r--conf/server.secure (renamed from conf/server.5)0
-rwxr-xr-xcron-sh/diff_check.sh10
-rwxr-xr-xcron-sh/promisc_check.sh10
-rwxr-xr-xcron-sh/security.sh7
-rwxr-xr-xcron-sh/security_check.sh61
-rw-r--r--man/C/msec.8635
-rw-r--r--man/C/mseclib.3228
-rw-r--r--share/Config.py44
-rw-r--r--share/ConfigFile.py453
-rw-r--r--share/Log.py54
-rw-r--r--share/Makefile45
-rwxr-xr-xshare/Perms.py305
-rwxr-xr-xshare/draksec_help.py65
-rw-r--r--share/libmsec.py1391
-rwxr-xr-xshare/man.py67
-rwxr-xr-xshare/msec85
-rwxr-xr-xshare/msec.py290
-rwxr-xr-xshare/shadow.py116
-rw-r--r--src/msec/.svnignore (renamed from share/.svnignore)0
-rw-r--r--src/msec/CHANGES (renamed from share/CHANGES)0
-rw-r--r--src/msec/Makefile34
-rw-r--r--src/msec/README (renamed from share/README)0
-rwxr-xr-xsrc/msec/compile.py (renamed from share/compile.py)12
-rw-r--r--src/msec/config.py273
-rwxr-xr-xsrc/msec/help_draksec.py74
-rwxr-xr-xsrc/msec/libmsec.py1763
-rwxr-xr-xsrc/msec/man.py197
-rwxr-xr-xsrc/msec/msec34
-rwxr-xr-xsrc/msec/msec.py141
-rwxr-xr-xsrc/msec/msecgui34
-rwxr-xr-xsrc/msec/msecgui.py725
-rwxr-xr-xsrc/msec/msecperms34
-rwxr-xr-xsrc/msec/msecperms.py152
-rw-r--r--src/msec/version.py1
-rw-r--r--src/msec_find/Makefile2
-rw-r--r--src/promisc_check/Makefile2
47 files changed, 4308 insertions, 3582 deletions
diff --git a/Makefile b/Makefile
index 8176d83..da16286 100644
--- a/Makefile
+++ b/Makefile
@@ -1,39 +1,48 @@
PACKAGE = msec
-VERSION = 0.50.11
+VERSION = 0.60.1
SVNPATH = svn+ssh://svn.mandriva.com/svn/soft/msec
-all: promisc_check msec_find python
+all: version promisc_check msec_find python
make -C cron-sh
+version:
+ echo "version='$(VERSION)'" > src/msec/version.py
+
clean:
-find . -name '*.o' -o -name '*.py[oc]' -o -name '*~' | xargs rm -f
rm -f src/promisc_check/promisc_check
rm -f src/msec_find/msec_find
rm -f *.bz2
- cd share; make clean
+ make -C src/msec clean
promisc_check:
- (cd src/promisc_check && make)
+ make -C src/promisc_check
msec_find:
- (cd src/msec_find && make)
+ make -C src/msec_find
python:
- -cd share; make
+ make -C src/msec
-install:
+install: all
mkdir -p $(RPM_BUILD_ROOT)/etc/security/msec
mkdir -p $(RPM_BUILD_ROOT)/usr/share/msec
mkdir -p $(RPM_BUILD_ROOT)/usr/sbin
cp init-sh/*.sh $(RPM_BUILD_ROOT)/usr/share/msec
cp cron-sh/*.sh $(RPM_BUILD_ROOT)/usr/share/msec
- cp init-sh/msec $(RPM_BUILD_ROOT)/usr/sbin
- cp conf/perm.* conf/server.* $(RPM_BUILD_ROOT)/etc/security/msec
+ # install main msec files
+ for i in libmsec.py config.py msec.py msecperms.py msecgui.py help.py version.py; do \
+ install -m755 src/msec/$$i $(RPM_BUILD_ROOT)/usr/share/msec ; \
+ done
+ for i in msec msecperms msecgui; do \
+ install -m755 src/msec/$$i $(RPM_BUILD_ROOT)/usr/sbin ; \
+ done
+ cp conf/perm.* conf/server.* conf/level.* $(RPM_BUILD_ROOT)/etc/security/msec
mkdir -p $(RPM_BUILD_ROOT)/var/log
mkdir -p $(RPM_BUILD_ROOT)/var/log/security
- touch $(RPM_BUILD_ROOT)/etc/security/msec/security.conf
touch $(RPM_BUILD_ROOT)/var/log/security.log
+ touch $(RPM_BUILD_ROOT)/var/log/msec.log
cd src/promisc_check && make install
cd src/msec_find && make install
mkdir -p $(RPM_BUILD_ROOT)/usr/share/man/man8/
diff --git a/conf/level.default b/conf/level.default
new file mode 100644
index 0000000..f9c0f7f
--- /dev/null
+++ b/conf/level.default
@@ -0,0 +1,54 @@
+ENABLE_APPARMOR=no
+ALLOW_X_CONNECTIONS=local
+CHECK_WRITABLE=yes
+ENABLE_IP_SPOOFING_PROTECTION=yes
+MAIL_EMPTY_CONTENT=no
+ACCEPT_BROADCASTED_ICMP_ECHO=yes
+CHECK_PERMS=yes
+CHECK_USER_FILES=yes
+ENABLE_SUDO=wheel
+ALLOW_XSERVER_TO_LISTEN=no
+CHECK_CHKROOTKIT=yes
+SHELL_HISTORY_SIZE=-1
+ALLOW_REBOOT=yes
+CHECK_SUID_ROOT=yes
+SYSLOG_WARN=yes
+ENABLE_AT_CRONTAB=yes
+ACCEPT_BOGUS_ERROR_RESPONSES=no
+CHECK_PASSWD=yes
+PASSWORD_HISTORY=0
+CHECK_SUID_MD5=yes
+CHECK_SHOSTS=yes
+MAIL_USER=root
+ALLOW_AUTOLOGIN=yes
+ENABLE_PAM_WHEEL_FOR_SU=no
+CHECK_SHADOW=yes
+ALLOW_ROOT_LOGIN=yes
+CHECK_UNOWNED=no
+ENABLE_CONSOLE_LOG=yes
+ALLOW_USER_LIST=yes
+ENABLE_DNS_SPOOFING_PROTECTION=yes
+CREATE_SERVER_LINK=default
+ENABLE_PASSWORD=yes
+NOTIFY_WARN=yes
+WIN_PARTS_UMASK=no
+CHECK_OPEN_PORT=yes
+SHELL_TIMEOUT=0
+ALLOW_REMOTE_ROOT_LOGIN=without_password
+ENABLE_LOG_STRANGE_PACKETS=yes
+USER_UMASK=022
+CHECK_RPM=yes
+ENABLE_SULOGIN=no
+ENABLE_PAM_ROOT_FROM_WHEEL=no
+MAIL_WARN=yes
+ALLOW_XAUTH_FROM_ROOT=yes
+CHECK_SECURITY=yes
+ACCEPT_ICMP_ECHO=yes
+PASSWORD_LENGTH=4,0,0
+AUTHORIZE_SERVICES=yes
+ROOT_UMASK=022
+ENABLE_MSEC_CRON=yes
+TTY_WARN=no
+ENABLE_POLICYKIT=yes
+CHECK_SGID=yes
+CHECK_PROMISC=no
diff --git a/conf/level.none b/conf/level.none
new file mode 100644
index 0000000..1e0f2c8
--- /dev/null
+++ b/conf/level.none
@@ -0,0 +1,54 @@
+ENABLE_APPARMOR=no
+ALLOW_X_CONNECTIONS=yes
+CHECK_WRITABLE=no
+ENABLE_IP_SPOOFING_PROTECTION=yes
+MAIL_EMPTY_CONTENT=no
+ACCEPT_BROADCASTED_ICMP_ECHO=yes
+CHECK_PERMS=no
+CHECK_USER_FILES=no
+ENABLE_SUDO=yes
+ALLOW_XSERVER_TO_LISTEN=yes
+CHECK_CHKROOTKIT=no
+SHELL_HISTORY_SIZE=-1
+ALLOW_REBOOT=yes
+CHECK_SUID_ROOT=no
+SYSLOG_WARN=no
+ENABLE_AT_CRONTAB=yes
+ACCEPT_BOGUS_ERROR_RESPONSES=yes
+CHECK_PASSWD=no
+PASSWORD_HISTORY=0
+CHECK_SUID_MD5=no
+CHECK_SHOSTS=no
+MAIL_USER=root
+ALLOW_AUTOLOGIN=yes
+ENABLE_PAM_WHEEL_FOR_SU=no
+CHECK_SHADOW=no
+ALLOW_ROOT_LOGIN=yes
+CHECK_UNOWNED=no
+ENABLE_CONSOLE_LOG=yes
+ALLOW_USER_LIST=yes
+ENABLE_DNS_SPOOFING_PROTECTION=yes
+CREATE_SERVER_LINK=no
+ENABLE_PASSWORD=yes
+NOTIFY_WARN=yes
+WIN_PARTS_UMASK=no
+CHECK_OPEN_PORT=no
+SHELL_TIMEOUT=0
+ALLOW_REMOTE_ROOT_LOGIN=yes
+ENABLE_LOG_STRANGE_PACKETS=no
+USER_UMASK=022
+CHECK_RPM=no
+ENABLE_SULOGIN=no
+ENABLE_PAM_ROOT_FROM_WHEEL=no
+MAIL_WARN=no
+ALLOW_XAUTH_FROM_ROOT=yes
+CHECK_SECURITY=no
+ACCEPT_ICMP_ECHO=yes
+PASSWORD_LENGTH=0,0,0
+AUTHORIZE_SERVICES=yes
+ROOT_UMASK=022
+ENABLE_MSEC_CRON=no
+TTY_WARN=no
+ENABLE_POLICYKIT=yes
+CHECK_SGID=no
+CHECK_PROMISC=no
diff --git a/conf/level.secure b/conf/level.secure
new file mode 100644
index 0000000..4d12b1d
--- /dev/null
+++ b/conf/level.secure
@@ -0,0 +1,54 @@
+ENABLE_APPARMOR=yes
+ALLOW_X_CONNECTIONS=no
+CHECK_WRITABLE=yes
+ENABLE_IP_SPOOFING_PROTECTION=yes
+MAIL_EMPTY_CONTENT=yes
+ACCEPT_BROADCASTED_ICMP_ECHO=no
+CHECK_PERMS=yes
+CHECK_USER_FILES=yes
+ENABLE_SUDO=no
+ALLOW_XSERVER_TO_LISTEN=no
+CHECK_CHKROOTKIT=yes
+SHELL_HISTORY_SIZE=100
+ALLOW_REBOOT=no
+CHECK_SUID_ROOT=yes
+SYSLOG_WARN=yes
+ENABLE_AT_CRONTAB=no
+ACCEPT_BOGUS_ERROR_RESPONSES=no
+CHECK_PASSWD=yes
+PASSWORD_HISTORY=2
+CHECK_SUID_MD5=yes
+CHECK_SHOSTS=yes
+MAIL_USER=root
+ALLOW_AUTOLOGIN=no
+ENABLE_PAM_WHEEL_FOR_SU=yes
+CHECK_SHADOW=yes
+ALLOW_ROOT_LOGIN=no
+CHECK_UNOWNED=yes
+ENABLE_CONSOLE_LOG=no
+ALLOW_USER_LIST=no
+ENABLE_DNS_SPOOFING_PROTECTION=yes
+CREATE_SERVER_LINK=secure
+ENABLE_PASSWORD=yes
+NOTIFY_WARN=no
+WIN_PARTS_UMASK=0
+CHECK_OPEN_PORT=yes
+SHELL_TIMEOUT=600
+ALLOW_REMOTE_ROOT_LOGIN=no
+ENABLE_LOG_STRANGE_PACKETS=yes
+USER_UMASK=077
+CHECK_RPM=yes
+ENABLE_SULOGIN=yes
+ENABLE_PAM_ROOT_FROM_WHEEL=no
+MAIL_WARN=yes
+ALLOW_XAUTH_FROM_ROOT=no
+CHECK_SECURITY=yes
+ACCEPT_ICMP_ECHO=yes
+PASSWORD_LENGTH=6,1,1
+AUTHORIZE_SERVICES=local
+ROOT_UMASK=077
+ENABLE_MSEC_CRON=yes
+TTY_WARN=yes
+ENABLE_POLICYKIT=no
+CHECK_SGID=yes
+CHECK_PROMISC=yes
diff --git a/conf/perm.0 b/conf/perm.0
deleted file mode 100644
index e786706..0000000
--- a/conf/perm.0
+++ /dev/null
@@ -1,92 +0,0 @@
-# Welcome in Level 0
-###
-/ root.root 755
-/bin/ root.root 755
-/bin/ping root.root 4755
-/bin/rpm rpm.rpm 755
-/boot/ root.root 755
-/dev/ root.root 755
-/etc/ root.root 755
-/etc/conf.modules root.root 644
-/etc/cron.daily/ root.root 755
-/etc/cron.hourly/ root.root 755
-/etc/cron.monthly/ root.root 755
-/etc/cron.weekly/ root.root 755
-/etc/crontab root.root 644
-/etc/dhcpcd/ root.root 755
-/etc/dhcpcd/* root.root 644
-/etc/ftpaccess root.root 644
-/etc/ftpconversions root.root 644
-/etc/ftpgroups root.root 644
-/etc/ftphosts root.root 644
-/etc/ftpusers root.root 644
-/etc/gettydefs root.root 644
-/etc/hosts.allow root.root 644
-/etc/hosts.deny root.root 644
-/etc/hosts.equiv root.root 644
-/etc/httpd/modules.d/*.conf root.root 644
-/etc/httpd/conf/*.conf root.root 644
-/etc/httpd/conf/addon-modules/* root.root 644
-/etc/httpd/conf/vhosts.d/* root.root 644
-/etc/httpd/conf/webapps.d/* root.root 644
-/etc/inetd.conf root.root 644
-/etc/inittab root.root 644
-/etc/ld.so.conf root.root 644
-/etc/mandrake-release root.root 644
-/etc/modules.conf root.root 644
-/etc/motd root.root 644
-/etc/printcap root.root 644
-/etc/profile.d/* root.root 755
-/etc/rc.d/ root.root 755
-/etc/rc.d/init.d/ root.root 755
-/etc/rc.d/init.d/* root.root 755
-/etc/securetty root.root 644
-/etc/mail/sendmail.cf root.mail 644
-/etc/shutdown.allow root.root 644
-/etc/ssh/ssh_config root.root 644
-/etc/ssh/ssh_host_*key root.root 600
-/etc/ssh/ssh_host_*key.pub root.root 644
-/etc/ssh/sshd_config root.root 644
-/etc/sysconfig root.root 755
-/etc/syslog.conf root.root 644
-/etc/updatedb.conf root.root 644
-/home/ root.root 755
-/home/* current 755
-/lib/ root.root 755
-/mnt/ root.root 755
-/proc root.root 555
-/root/ root.root 755
-/sbin/ root.root 755
-/tmp/ root.root 777
-/usr/ root.root 755
-/usr/* root.root 755
-/usr/bin/ root.root 755
-/usr/bin/cc root.root 755
-/usr/bin/finger root.root 755
-/usr/bin/g++* root.root 755
-/usr/bin/gcc* root.root 755
-/usr/bin/ssh root.root 755
-/usr/bin/telnet root.root 755
-/usr/bin/w root.root 755
-/usr/bin/who root.root 755
-/usr/lib/rpm/rpm? rpm.rpm 755
-/usr/sbin/ root.root 755
-/usr/sbin/sendmail.postfix root.root 755
-/usr/sbin/sendmail.sendmail root.mail 2755
-/usr/sbin/traceroute root.bin 4755
-/usr/share/doc root.root 755
-/usr/share/man root.root 755
-/usr/tmp root.root 777
-/var/ root.root 755
-/var/lib/rpm/Packages rpm.rpm 644
-/var/lock/subsys root.root 755
-/var/log/ root.root 755
-/var/log/* root.adm 644
-/var/log/lp-errs lp.lp 600
-/var/log/*/* current 644
-/var/log/*/*/* current 644
-/var/log/*/. current 755
-/var/log/mailman/ root.mail 2775
-/var/log/mailman/* root.mail 664
-/var/spool/mail/ root.mail 2775
-/var/tmp root.root 777
diff --git a/conf/perm.3 b/conf/perm.3
deleted file mode 100644
index 23f273b..0000000
--- a/conf/perm.3
+++ /dev/null
@@ -1,96 +0,0 @@
-# Welcome in Level 3
-###
-/ root.adm 755
-/bin/ root.root 755
-/bin/ping root.root 4755
-/bin/rpm rpm.rpm 755
-/boot/ root.root 755
-/dev/ root.root 755
-/etc/ root.root 755
-/etc/conf.modules root.root 644
-/etc/cron.daily/ root.root 755
-/etc/cron.hourly/ root.root 755
-/etc/cron.monthly/ root.root 755
-/etc/cron.weekly/ root.root 755
-/etc/crontab root.root 644
-/etc/dhcpcd/ root.root 755
-/etc/dhcpcd/* root.root 644
-/etc/ftpaccess root.root 644
-/etc/ftpconversions root.root 644
-/etc/ftpgroups root.root 644
-/etc/ftphosts root.root 644
-/etc/ftpusers root.root 644
-/etc/gettydefs root.root 644
-/etc/hosts.allow root.root 644
-/etc/hosts.deny root.root 644
-/etc/hosts.equiv root.root 644
-/etc/httpd/modules.d/*.conf root.root 644
-/etc/httpd/conf/*.conf root.root 644
-/etc/httpd/conf/addon-modules/* root.root 644
-/etc/httpd/conf/vhosts.d/* root.root 644
-/etc/httpd/conf/webapps.d/* root.root 644
-/etc/inetd.conf root.root 644
-/etc/inittab root.root 644
-/etc/ld.so.conf root.root 644
-/etc/mandrake-release root.root 644
-/etc/modules.conf root.root 644
-/etc/motd root.root 644
-/etc/printcap root.root 644
-/etc/profile.d/* root.root 755
-/etc/rc.d/ root.root 755
-/etc/rc.d/init.d/ root.root 755
-/etc/rc.d/init.d/* root.root 700
-/etc/rc.d/init.d/functions root.root 644
-/etc/rc.d/init.d/mandrake_consmap root.root 644
-/etc/rc.d/init.d/xprint root.root 755
-/etc/securetty root.root 644
-/etc/sendmail.cf root.mail 644
-/etc/shutdown.allow root.root 644
-/etc/ssh/ssh_config root.root 644
-/etc/ssh/ssh_host_*key root.root 600
-/etc/ssh/ssh_host_*key.pub root.root 644
-/etc/ssh/sshd_config root.root 644
-/etc/sysconfig root.root 755
-/etc/syslog.conf root.adm 640
-/etc/updatedb.conf root.root 644
-/home/ root.root 755
-/home/* current 711
-/lib/ root.root 755
-/mnt/ root.root 755
-/proc root.root 555
-/root/ root.root 700
-/sbin/ root.root 755
-/tmp/ root.root 1777
-/usr/ root.root 755
-/usr/* root.root 755
-/usr/bin/ root.root 755
-/usr/bin/cc root.root 755
-/usr/bin/finger root.root 755
-/usr/bin/g++* root.root 755
-/usr/bin/gcc* root.root 755
-/usr/bin/ssh root.root 755
-/usr/bin/telnet root.root 755
-/usr/bin/w root.root 755
-/usr/bin/who root.root 755
-/usr/lib/rpm/rpm? rpm.rpm 755
-/usr/sbin/ root.root 755
-/usr/sbin/sendmail.postfix root.root 755
-/usr/sbin/sendmail.sendmail root.mail 2755
-/usr/sbin/traceroute root.bin 4755
-/usr/share/doc root.root 755
-/usr/share/man root.root 755
-/usr/tmp root.root 1777
-/var/ root.root 755
-/var/lib/rpm/Packages rpm.rpm 644
-/var/lock/subsys root.root 755
-/var/log/ root.root 755
-/var/log/* root.root 640
-/var/log/Xorg.0.log current current
-/var/log/lp-errs lp.lp 600
-/var/log/*/* current 640
-/var/log/*/*/* current 640
-/var/log/*/. current 755
-/var/log/mailman/ root.mail 2775
-/var/log/mailman/* root.mail 660
-/var/spool/mail/ root.mail 2775
-/var/tmp root.root 1777
diff --git a/conf/perm.5 b/conf/perm.5
deleted file mode 100644
index 8aedfc8..0000000
--- a/conf/perm.5
+++ /dev/null
@@ -1,96 +0,0 @@
-# Welcome in Level 5, aka paranoid.
-###
-/ root.root 711
-/bin/ root.root 711
-/bin/ping root.ntools 4750
-/bin/rpm rpm.rpm 750
-/boot/ root.ctools 710
-/dev/ root.root 711
-/etc/ root.root 711
-/etc/conf.modules root.root 600
-/etc/cron.daily/ root.root 700
-/etc/cron.hourly/ root.root 700
-/etc/cron.monthly/ root.root 700
-/etc/cron.weekly/ root.root 700
-/etc/crontab root.root 600
-/etc/dhcpcd/ root.root 700
-/etc/dhcpcd/* root.root 600
-/etc/ftpaccess root.root 600
-/etc/ftpconversions root.root 600
-/etc/ftpgroups root.root 600
-/etc/ftphosts root.root 600
-/etc/ftpusers root.root 600
-/etc/gettydefs root.root 600
-/etc/hosts.allow root.daemon 644
-/etc/hosts.deny root.daemon 644
-/etc/hosts.equiv root.daemon 640
-/etc/httpd/modules.d/*.conf root.root 600
-/etc/httpd/conf/*.conf root.root 600
-/etc/httpd/conf/addon-modules/* root.root 600
-/etc/httpd/conf/vhosts.d/* root.root 600
-/etc/httpd/conf/webapps.d/* root.root 600
-/etc/inetd.conf root.root 600
-/etc/inittab root.root 600
-/etc/ld.so.conf root.root 600
-/etc/mandrake-release root.root 600
-/etc/modules.conf root.root 600
-/etc/motd root.root 644
-/etc/printcap root.lp 640
-/etc/profile.d/* root.root 755
-/etc/rc.d/ root.root 700
-/etc/rc.d/init.d/ root.root 700
-/etc/rc.d/init.d/* root.root 700
-/etc/rc.d/init.d/functions root.root 644
-/etc/rc.d/init.d/mandrake_consmap root.adm 644
-/etc/rc.d/init.d/xprint root.root 755
-/etc/securetty root.root 600
-/etc/sendmail.cf root.mail 640
-/etc/shutdown.allow root.root 600
-/etc/ssh/ssh_config root.root 644
-/etc/ssh/ssh_host_*key root.root 600
-/etc/ssh/ssh_host_*key.pub root.root 644
-/etc/ssh/sshd_config root.root 600
-/etc/sysconfig root.root 711
-/etc/syslog.conf root.root 600
-/etc/updatedb.conf root.root 600
-/home/ root.root 711
-/home/* current 700
-/lib/ root.root 711
-/mnt/ root.root 710
-/proc root.adm 550
-/root/ root.root 700
-/sbin/ root.root 711
-/tmp/ root.root 1733
-/usr/ root.root 711
-/usr/* root.root 711
-/usr/bin/ root.root 711
-/usr/bin/cc root.ctools 750
-/usr/bin/finger root.ntools 750
-/usr/bin/g++* root.ctools 750
-/usr/bin/gcc* root.ctools 750
-/usr/bin/ssh root.ntools 750
-/usr/bin/telnet root.ntools 750
-/usr/bin/w root.ntools 750
-/usr/bin/who root.ntools 750
-/usr/lib/rpm/rpm? rpm.rpm 750
-/usr/sbin/ root.root 711
-/usr/sbin/sendmail.postfix root.root 711
-/usr/sbin/sendmail.sendmail root.mail 2711
-/usr/sbin/traceroute root.ntools 4750
-/usr/share/doc rpm.rpm 710
-/usr/share/man rpm.rpm 710
-/usr/tmp root.root 1733
-/var/ root.root 755
-/var/lib/rpm/Packages rpm.rpm 640
-/var/lock/subsys root.root 700
-/var/log/ root.root 711
-/var/log/* root.root 600
-/var/log/lp-errs lp.lp 600
-/var/log/*/* current 600
-/var/log/*/*/* current 600
-/var/log/*/. current 700
-/var/log/intraline/. current 750
-/var/log/mailman/ root.mail 2770
-/var/log/mailman/* root.mail 660
-/var/spool/mail/ root.mail 771
-/var/tmp root.root 1733
diff --git a/conf/perm.2 b/conf/perm.default
index 7fa4ae8..744e4a8 100644
--- a/conf/perm.2
+++ b/conf/perm.default
@@ -1,6 +1,6 @@
# Welcome in Level 2
###
-/ root.root 755
+/ root.adm 755
/bin/ root.root 755
/bin/ping root.root 4755
/bin/rpm rpm.rpm 755
@@ -54,7 +54,7 @@
/etc/syslog.conf root.root 644
/etc/updatedb.conf root.root 644
/home/ root.root 755
-/home/* current 755
+/home/* current.current 755
/lib/ root.root 755
/mnt/ root.root 755
/proc root.root 555
@@ -85,12 +85,12 @@
/var/lock/subsys root.root 755
/var/log/ root.root 755
/var/log/* root.adm 640
-/var/log/Xorg.0.log current current
+/var/log/Xorg.0.log current.current current
/var/log/lp-errs lp.lp 600
-/var/log/*/* current 640
-/var/log/*/*/* current 640
-/var/log/*/. current 755
+/var/log/*/* current.current 640
+/var/log/*/*/* current.current 640
+/var/log/*/. current.current 755
/var/log/mailman/ root.mail 2775
-/var/log/mailman/* root.mail 664
+/var/log/mailman/* root.mail 660
/var/spool/mail/ root.mail 2775
/var/tmp root.root 1777
diff --git a/conf/perm.1 b/conf/perm.none
index ddeaa3c..9df8c21 100644
--- a/conf/perm.1
+++ b/conf/perm.none
@@ -53,7 +53,7 @@
/etc/syslog.conf root.root 644
/etc/updatedb.conf root.root 644
/home/ root.root 755
-/home/* current 755
+/home/* current.current 755
/lib/ root.root 755
/mnt/ root.root 755
/proc root.root 555
@@ -85,9 +85,9 @@
/var/log/ root.root 755
/var/log/* root.adm 644
/var/log/lp-errs lp.lp 600
-/var/log/*/* current 644
-/var/log/*/*/* current 644
-/var/log/*/. current 755
+/var/log/*/* current.current 644
+/var/log/*/*/* current.current 644
+/var/log/*/. current.current 755
/var/log/mailman/ root.mail 2775
/var/log/mailman/* root.mail 664
/var/spool/mail/ root.mail 2775
diff --git a/conf/perm.4 b/conf/perm.secure
index b8848bf..159a933 100644
--- a/conf/perm.4
+++ b/conf/perm.secure
@@ -1,12 +1,12 @@
-# Welcome in Level 4, aka secure & usable.
+# Welcome in Level 2
###
-/ root.adm 751
-/bin/ root.adm 751
+/ root.adm 755
+/bin/ root.adm 755
/bin/ping root.ntools 4750
/bin/rpm rpm.rpm 750
/boot/ root.ctools 710
-/dev/ root.root 711
-/etc/ root.adm 711
+/dev/ root.root 755
+/etc/ root.adm 755
/etc/conf.modules root.adm 640
/etc/cron.daily/ root.adm 750
/etc/cron.hourly/ root.adm 750
@@ -32,14 +32,14 @@
/etc/inetd.conf root.adm 640
/etc/inittab root.adm 640
/etc/ld.so.conf root.ctools 640
-/etc/mandrake-release root.adm 640
+/etc/mandrake-release root.adm 644
/etc/modules.conf root.adm 640
/etc/motd root.adm 644
/etc/printcap root.lp 640
/etc/profile.d/* root.root 755
-/etc/rc.d/ root.adm 750
-/etc/rc.d/init.d/ root.adm 750
-/etc/rc.d/init.d/* root.adm 740
+/etc/rc.d/ root.adm 755
+/etc/rc.d/init.d/ root.adm 755
+/etc/rc.d/init.d/* root.adm 744
/etc/rc.d/init.d/functions root.adm 644
/etc/rc.d/init.d/mandrake_consmap root.adm 644
/etc/rc.d/init.d/xprint root.root 755
@@ -50,20 +50,20 @@
/etc/ssh/ssh_host_*key root.adm 600
/etc/ssh/ssh_host_*key.pub root.adm 644
/etc/ssh/sshd_config root.adm 640
-/etc/sysconfig root.adm 751
+/etc/sysconfig root.adm 755
/etc/syslog.conf root.adm 640
-/etc/updatedb.conf root.adm 640
+/etc/updatedb.conf root.adm 644
/home/ root.adm 751
-/home/* current 700
-/lib/ root.adm 751
+/home/* current.current 751
+/lib/ root.adm 755
/mnt/ root.adm 750
-/proc root.adm 550
+/proc root.adm 555
/root/ root.root 700
/sbin/ root.adm 751
/tmp/ root.adm 1773
-/usr/ root.adm 751
-/usr/* root.adm 751
-/usr/bin/ root.adm 751
+/usr/ root.adm 755
+/usr/* root.adm 755
+/usr/bin/ root.adm 755
/usr/bin/cc root.ctools 750
/usr/bin/finger root.ntools 750
/usr/bin/g++* root.ctools 750
@@ -77,19 +77,20 @@
/usr/sbin/sendmail.postfix root.root 711
/usr/sbin/sendmail.sendmail root.mail 2711
/usr/sbin/traceroute root.ntools 4750
-/usr/share/doc rpm.rpm 750
-/usr/share/man rpm.rpm 750
+/usr/share/doc root.root 755
+/usr/share/man root.root 755
/usr/tmp root.adm 1773
/var/ root.root 755
/var/lib/rpm/Packages rpm.rpm 640
/var/lock/subsys root.adm 750
/var/log/ root.adm 751
-/var/log/* root.root 600
+/var/log/* root.root 640
+/var/log/Xorg.0.log current.current current
/var/log/lp-errs lp.lp 600
-/var/log/*/* current 600
-/var/log/*/*/* current 600
-/var/log/*/. current 700
-/var/log/intraline/. current 750
+/var/log/*/* current.current 600
+/var/log/*/*/* current.current 600
+/var/log/*/. current.current 700
+/var/log/intraline/. current.current 750
/var/log/mailman/ root.mail 2770
/var/log/mailman/* root.mail 660
/var/spool/mail/ root.mail 771
diff --git a/conf/server.4 b/conf/server.default
index d518731..d518731 100644
--- a/conf/server.4
+++ b/conf/server.default
diff --git a/conf/server.5 b/conf/server.secure
index 2141239..2141239 100644
--- a/conf/server.5
+++ b/conf/server.secure
diff --git a/cron-sh/diff_check.sh b/cron-sh/diff_check.sh
index 3c0fc27..b8991e1 100755
--- a/cron-sh/diff_check.sh
+++ b/cron-sh/diff_check.sh
@@ -3,15 +3,11 @@
# Written by Vandoorselaere Yoann, <yoann@mandrakesoft.com>
#
-if [[ -f /var/lib/msec/security.conf ]]; then
- . /var/lib/msec/security.conf
-else
- echo "/var/lib/msec/security.conf don't exist."
- exit 1
-fi
-
if [[ -f /etc/security/msec/security.conf ]]; then
. /etc/security/msec/security.conf
+else
+ echo "/etc/security/msec/security.conf don't exist."
+ exit 1
fi
if [[ ${CHECK_SECURITY} == no ]]; then
diff --git a/cron-sh/promisc_check.sh b/cron-sh/promisc_check.sh
index e573526..468a44a 100755
--- a/cron-sh/promisc_check.sh
+++ b/cron-sh/promisc_check.sh
@@ -28,15 +28,11 @@ LogPromisc() {
}
-if [[ -f /var/lib/msec/security.conf ]]; then
- . /var/lib/msec/security.conf
-else
- echo "/var/lib/msec/security.conf doesn't exist."
- exit 1
-fi
-
if [[ -f /etc/security/msec/security.conf ]]; then
. /etc/security/msec/security.conf
+else
+ echo "/etc/security/msec/security.conf don't exist."
+ exit 1
fi
if tail /var/log/security.log | grep -q "promiscuous"; then
diff --git a/cron-sh/security.sh b/cron-sh/security.sh
index c7ec008..4026c5f 100755
--- a/cron-sh/security.sh
+++ b/cron-sh/security.sh
@@ -24,10 +24,11 @@ if [[ ! -f /var/lib/msec/security.conf ]]; then
exit 1
fi
-. /var/lib/msec/security.conf
-
-if [ -r /etc/security/msec/security.conf ]; then
+if [[ -f /etc/security/msec/security.conf ]]; then
. /etc/security/msec/security.conf
+else
+ echo "/etc/security/msec/security.conf don't exist."
+ exit 1
fi
if [ -r /etc/sysconfig/msec ]; then
diff --git a/cron-sh/security_check.sh b/cron-sh/security_check.sh
index e9121fe..2062902 100755
--- a/cron-sh/security_check.sh
+++ b/cron-sh/security_check.sh
@@ -4,15 +4,11 @@
# Written by Vandoorselaere Yoann, <yoann@mandrakesoft.com>
#
-if [[ -f /var/lib/msec/security.conf ]]; then
- . /var/lib/msec/security.conf
-else
- echo "/var/lib/msec/security.conf don't exist."
- exit 1
-fi
-
if [[ -f /etc/security/msec/security.conf ]]; then
. /etc/security/msec/security.conf
+else
+ echo "/etc/security/msec/security.conf don't exist."
+ exit 1
fi
if [[ ${CHECK_SECURITY} != yes ]]; then
@@ -59,6 +55,10 @@ if [[ ${CHECK_UNOWNED} == yes ]]; then
fi
if [[ ${CHECK_PERMS} == yes ]]; then
+ # running msec_perms
+fi
+
+if [[ ${CHECK_USER_FILES} == yes ]]; then
# Files that should not be owned by someone else or readable.
list=".netrc .rhosts .shosts .Xauthority .gnupg/secring.gpg \
.pgp/secring.pgp .ssh/identity .ssh/id_dsa .ssh/id_rsa .ssh/random_seed"
@@ -139,7 +139,7 @@ if [[ -s $TMP ]] ; then
printf "\nSecurity Warning: these home directory should not be owned by someone else or writable :\n" >> ${SECURITY}
cat ${TMP} >> ${SECURITY}
fi
-fi # End of check perms
+fi # End of CHECK_USER_FILES
### Passwd file check
if [[ ${CHECK_PASSWD} == yes ]]; then
@@ -213,28 +213,31 @@ for file in $list ; do
fi
done > ${TMP}
-# TODO: do not check on remote shares (#41709)
-getent passwd | awk -F: '{print $1" "$6}' |
- while read username homedir; do
- if ! expr "$homedir" : "$FILTER" > /dev/null; then
- for file in .rhosts .shosts; do
- if [[ -s ${homedir}/${file} ]] ; then
- awk '{
- if ($0 ~ /^\+@.*$/)
- next;
- if ($0 ~ /^\+.*$/)
- printf("\t\t- %s: %s\n", FILENAME, $0);
- }' ${homedir}/${file}
+### Passwd file check
+if [[ ${CHECK_SHOSTS} == yes ]]; then
+ # TODO: do not check on remote shares (#41709)
+ getent passwd | awk -F: '{print $1" "$6}' |
+ while read username homedir; do
+ if ! expr "$homedir" : "$FILTER" > /dev/null; then
+ for file in .rhosts .shosts; do
+ if [[ -s ${homedir}/${file} ]] ; then
+ awk '{
+ if ($0 ~ /^\+@.*$/)
+ next;
+ if ($0 ~ /^\+.*$/)
+ printf("\t\t- %s: %s\n", FILENAME, $0);
+ }' ${homedir}/${file}
+ fi
+ done >> ${TMP}
+ fi
+ done
+
+ if [[ -s ${TMP} ]]; then
+ printf "\nSecurity Warning: '+' character found in hosts trusting files,\n" >> ${SECURITY}
+ printf "\tthis probably mean that you trust certains users/domain\n" >> ${SECURITY}
+ printf "\tto connect on this host without proper authentication :\n" >> ${SECURITY}
+ cat ${TMP} >> ${SECURITY}
fi
- done >> ${TMP}
- fi
- done
-
-if [[ -s ${TMP} ]]; then
- printf "\nSecurity Warning: '+' character found in hosts trusting files,\n" >> ${SECURITY}
- printf "\tthis probably mean that you trust certains users/domain\n" >> ${SECURITY}
- printf "\tto connect on this host without proper authentication :\n" >> ${SECURITY}
- cat ${TMP} >> ${SECURITY}
fi
### executables should not be in the aliases file.
diff --git a/man/C/msec.8 b/man/C/msec.8
index 16768ad..8a0c098 100644
--- a/man/C/msec.8
+++ b/man/C/msec.8
@@ -1,69 +1,592 @@
-.TH msec 8 "29 Sep 2001" "Mandriva" "Mandriva Linux"
-.IX msec
+.ds q \N'34'
+.TH msec 0.60.1 msec "Mandriva Linux"
.SH NAME
msec \- Mandriva Linux security tools
.SH SYNOPSIS
-.B msec
-([-o <option>=<value>...]) ([0-5])
+.nf
+.B msec [options]
+.B msecperms [options]
+.B msecgui [options]
+.fi
.SH DESCRIPTION
-\fPmsec\fP is the main script of the msec package. It enables the
-system administrator to change the security level for that system.
-msec is provided with six preconfigured security levels. These levels
-range from poor security and ease of use, to paranoid config, suitable
-for very sensitive server applications.
-.PP
-You must be root to run \fPmsec\fP.
-.br
-Launch "msec x" to set you security level to x (x=[0-5]). It'll modify
-your system according to security level x features. Called without
-argument, it will enforce the current security level without lowering
-security.
-.br
-All the changes are logged to syslog(8) at the AUTH facility when called
-non interactivelly (by cron for example) or at the LOCAL1 facility
-when called interactivelly (on the command line or from Mandriva Linux
-Control Center for example).
-.br
-For a fine description of each security level, consult the
-documentation under /usr/share/doc/msec-*/security.txt.
-.PP
-If you want to make changes to the current level, use
-/etc/security/msec/perm.local to override the
-permissions/owners/groups (use the same syntax as /usr/share/msec/perm.*
-or use the drakperm graphical utility) and /etc/security/msec/level.local to
-override the rules (see mseclib(3) for details or use the draksec graphical utility).
-.PP
-Available options:
+.B msec
+is responsible to maintain system security in Mandriva. It supports different security
+configurations, which can be organized into several security levels. Currently, three
+preconfigured security levels are provided:
+
.TP
-\fB\-o all-local-files=<value>\fR
-if <value> is 1, consider that all the files are local.
+\fBnone\fR
+this level aims to provide the most basic security. It should be used when you want to
+manage all aspects of system security on your own.
+
.TP
-\fB\-o log=<value>\fR
-if <value> is different of syslog do not log to syslog but to the standard error output.
+\fBdefault\fR
+this is the default security level, which configures a reasonably safe set of security
+features. It activates several periodic system checks, and sends the results of their
+execution by email (by default, the local 'root' account is used).
+
.TP
-\fB\-o nolocal=<path>\fR
-do not load the /etc/security/msec/level.local rules.
+\fBsecure\fR
+this level is configured to provide maximum system security, even at the cost of limiting
+the remote access to the system, and local user permissions. It also runs a wider set of
+periodic checks, enforces the local password settings, and periodically checks if the
+system security settings, configured by msec, were modified directly or by some other
+application.
+
+.PP
+
+The security settings are stored in \fB/etc/security/msec/security.conf\fR
+file, and default settings for each predefined level are stored in
+\fB/etc/security/msec/level.LEVEL\fR. Permissions for files and directories
+that should be enforced or checked for changes are stored in
+\fB/etc/security/msec/perms.conf\fR, and default permissions for each
+predefined level are stored in \fB/etc/security/msec/perm.LEVEL\fR. Note
+that user-modified parameters take precedence over default level settings. For
+example, when default level configuration forbids direct root logins, this
+setting can be overridden by the user.
+
+.PP
+
+The following options are supported by msec applications:
+
.TP
-\fB\-o non-local-fstypes=<value>\fR
-<value> is a list of non local file system types separated by spaces.
+\fBmsec\fR:
+.PP
+
+This is the console version of msec. It is responsible for system security configuration
+and checking and transitions between security levels.
+
+When executed without parameters, msec will read the system configuration file
+(/etc/security/msec/security.conf), and enforce the specified security
+settings. The operations are logged to \fB/var/log/msec.log\fP file, and also
+to syslog, using \fBLOG_AUTHPRIV\fR facility. Please note that msec should
+by run as root.
+
+\fB\-h, --help\fR
+ This option will display the list of supported command line options.
+
+\fB\-l, --level <level>\fR
+ List the default configuration for given security level.
+
+\fB\-f, --force <level>\fR
+ Apply the specified security level to the system, overwritting all
+local changes. This is necessary to initialize a security level, either on first
+install, on when a change to a different level is required.
+
+\fB\-d\fR
+ Enable debugging messages.
+
+\fB\-p, --pretend\fR
+ Verify the actions that will be performed by msec, without actually
+doing anything to the system. In this mode of operation, msec performs all the
+required tasks, except effectively writting data back to disk.
+
.TP
-\fB\-o print=<value>\fR
-if <value> is equal to 1, output the default values of the rules.
+\fBmsecperms\fR:
+.PP
+
+This application is responsible for system permission checking and enforcements.
+
+When executed without parameters, msecperms will read the permissions
+configuration file (/etc/security/msec/perms.conf), and enforce the specified
+security settings. The operations are logged to \fB/var/log/msec.log\fP file,
+and also to syslog, using \fBLOG_AUTHPRIV\fR facility. Please note that msecperms
+should by run as root.
+
+\fB\-h, --help\fR
+ This option will display the list of supported command line options.
+
+\fB\-l, --level <level>\fR
+ List the default configuration for given security level.
+
+\fB\-f, --force <level>\fR
+ Apply the specified security level to the system, overwritting all
+local changes. This is necessary to initialize a security level, either on first
+install, on when a change to a different level is required.
+
+\fB\-e, --enforce\fR
+ Enforce the default permissions on all files.
+
+\fB\-d\fR
+ Enable debugging messages.
+
+\fB\-p, --pretend\fR
+ Verify the actions that will be performed by msec, without actually
+doing anything to the system. In this mode of operation, msec performs all the
+required tasks, except effectively writting data back to disk.
+
.TP
-\fB\-o root=<path>\fR
-use <path> as the root of the file system.
-.SH FILES
-/usr/sbin/msec
-.br
-The \fPmsec\fP executable (sh script)
+\fBmsecgui\fR:
.PP
-/var/lib/msec/security.conf
-.br
-Contains the configuration of the current active security level. These
-settings can be overridden in /etc/security/msec/security.conf.
-.SH "SEE ALSO"
-mseclib(3), draksec, drakperm
+This is the GTK version of msec. It acts as frontend to all msec functionalities.
+
+\fB\-h, --help\fR
+ This option will display the list of supported command line options.
+
+\fB\-d\fR
+ Enable debugging messages.
+
+.SH "SECURITY OPTIONS"
+
+The following security options are supported by msec:
+
+
+
+.TP 4
+.B \fIenable_dns_spoofing_protection\fP
+Enable/Disable name resolution spoofing protection. If \fIalert\fP is true, also reports to syslog.
+
+MSEC parameter: \fIENABLE_IP_SPOOFING_PROTECTION\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fImail_empty_content\fP
+Enables sending of empty mail reports.
+
+MSEC parameter: \fIMAIL_EMPTY_CONTENT\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIaccept_broadcasted_icmp_echo\fP
+Accept/Refuse broadcasted icmp echo.
+
+MSEC parameter: \fIACCEPT_BROADCASTED_ICMP_ECHO\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIallow_xserver_to_listen\fP
+The argument specifies if clients are authorized to connect to the X server on the tcp port 6000 or not.
+
+MSEC parameter: \fIALLOW_XSERVER_TO_LISTEN\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_chkrootkit\fP
+Enables checking for known rootkits using chkrootkit.
+
+MSEC parameter: \fICHECK_CHKROOTKIT\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_suid_root\fP
+Enables checking for additions/removals of suid root files.
+
+MSEC parameter: \fICHECK_SUID_ROOT\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIenable_at_crontab\fP
+Enable/Disable crontab and at for users. Put allowed users in /etc/cron.allow and /etc/at.allow (see man at(1) and crontab(1)).
+
+MSEC parameter: \fIENABLE_AT_CRONTAB\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIaccept_bogus_error_responses\fP
+Accept/Refuse bogus IPv4 error messages.
+
+MSEC parameter: \fIACCEPT_BOGUS_ERROR_RESPONSES\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_suid_md5\fP
+Enables checksum verification for suid files.
+
+MSEC parameter: \fICHECK_SUID_MD5\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fImail_user\fP
+Defines email to receive security notifications.
+
+MSEC parameter: \fIMAIL_USER\fP
+
+Accepted values: \fI*\fP
+
+
+.TP 4
+.B \fIallow_autologin\fP
+Allow/Forbid autologin.
+
+MSEC parameter: \fIALLOW_AUTOLOGIN\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIenable_pam_wheel_for_su\fP
+Enabling su only from members of the wheel group or allow su from any user.
+
+MSEC parameter: \fIENABLE_PAM_WHEEL_FOR_SU\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcreate_server_link\fP
+Creates the symlink /etc/security/msec/server to point to /etc/security/msec/server.<SERVER_LEVEL>. The /etc/security/msec/server is used by chkconfig --add to decide to add a service if it is present in the file during the installation of packages.
+
+MSEC parameter: \fICREATE_SERVER_LINK\fP
+
+Accepted values: \fIno, default, secure\fP
+
+
+.TP 4
+.B \fIset_shell_timeout\fP
+Set the shell timeout. A value of zero means no timeout.
+
+MSEC parameter: \fISHELL_TIMEOUT\fP
+
+Accepted values: \fI*\fP
+
+
+.TP 4
+.B \fIcheck_user_files\fP
+Enables permission checking on users' files that should not be owned by someone else, or writable.
+
+MSEC parameter: \fICHECK_USER_FILES\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_shadow\fP
+Enables checking for empty passwords.
+
+MSEC parameter: \fICHECK_SHADOW\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIenable_password\fP
+Use password to authenticate users. Take EXTREMELY care when disabling passwords, as it will leave the machine COMPLETELY vulnerable.
+
+MSEC parameter: \fIENABLE_PASSWORD\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIset_win_parts_umask\fP
+Set umask option for mounting vfat and ntfs partitions. A value of None means default umask.
+
+MSEC parameter: \fIWIN_PARTS_UMASK\fP
+
+Accepted values: \fIno, *\fP
+
+
+.TP 4
+.B \fIcheck_open_port\fP
+Enables checking for open network ports.
+
+MSEC parameter: \fICHECK_OPEN_PORT\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIenable_log_strange_packets\fP
+Enable/Disable the logging of IPv4 strange packets.
+
+MSEC parameter: \fIENABLE_LOG_STRANGE_PACKETS\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_rpm\fP
+Enables verification of installed packages.
+
+MSEC parameter: \fICHECK_RPM\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIenable_pam_root_from_wheel\fP
+Allow root access without password for the members of the wheel group.
+
+MSEC parameter: \fIENABLE_PAM_ROOT_FROM_WHEEL\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fImail_warn\fP
+Enables security results submission by email.
+
+MSEC parameter: \fIMAIL_WARN\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIpassword_length\fP
+Set the password minimum length and minimum number of digit and minimum number of capitalized letters.
+
+MSEC parameter: \fIPASSWORD_LENGTH\fP
+
+Accepted values: \fI*\fP
+
+
+.TP 4
+.B \fIset_root_umask\fP
+Set the root umask.
+
+MSEC parameter: \fIROOT_UMASK\fP
+
+Accepted values: \fI*\fP
+
+
+.TP 4
+.B \fIcheck_sgid\fP
+Enables checking for additions/removals of sgid files.
+
+MSEC parameter: \fICHECK_SGID\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_promisc\fP
+Activate/Disable ethernet cards promiscuity check.
+
+MSEC parameter: \fICHECK_PROMISC\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIallow_x_connections\fP
+Allow/Forbid X connections. Accepted arguments: yes (all connections are allowed), local (only local connection), no (no connection).
+
+MSEC parameter: \fIALLOW_X_CONNECTIONS\fP
+
+Accepted values: \fIyes, no, local\fP
+
+
+.TP 4
+.B \fIcheck_writable\fP
+Enables checking for files/directories writable by everybody.
+
+MSEC parameter: \fICHECK_WRITABLE\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIenable_console_log\fP
+Enable/Disable syslog reports to console 12. \fIexpr\fP is the expression describing what to log (see syslog.conf(5) for more details) and dev the device to report the log.
+
+MSEC parameter: \fIENABLE_CONSOLE_LOG\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIenable_ip_spoofing_protection\fP
+Enable/Disable IP spoofing protection.
+
+MSEC parameter: \fIENABLE_DNS_SPOOFING_PROTECTION\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_perms\fP
+Enables periodic permission checking for system files.
+
+MSEC parameter: \fICHECK_PERMS\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIset_shell_history_size\fP
+Set shell commands history size. A value of -1 means unlimited.
+
+MSEC parameter: \fISHELL_HISTORY_SIZE\fP
+
+Accepted values: \fI*\fP
+
+
+.TP 4
+.B \fIallow_reboot\fP
+Allow/Forbid system reboot and shutdown to local users.
+
+MSEC parameter: \fIALLOW_REBOOT\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIsyslog_warn\fP
+Enables logging to system log.
+
+MSEC parameter: \fISYSLOG_WARN\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_shosts\fP
+Enables checking for dangerous options in users' .rhosts/.shosts files.
+
+MSEC parameter: \fICHECK_SHOSTS\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_passwd\fP
+Enables password-related checks, such as empty passwords and strange super-user accounts.
+
+MSEC parameter: \fICHECK_PASSWD\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIpassword_history\fP
+Set the password history length to prevent password reuse. This is not supported by pam_tcb.
+
+MSEC parameter: \fIPASSWORD_HISTORY\fP
+
+Accepted values: \fI*\fP
+
+
+.TP 4
+.B \fIcheck_security\fP
+Enables daily security checks.
+
+MSEC parameter: \fICHECK_SECURITY\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIallow_root_login\fP
+Allow/Forbid direct root login.
+
+MSEC parameter: \fIALLOW_ROOT_LOGIN\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIcheck_unowned\fP
+Enables checking for unowned files.
+
+MSEC parameter: \fICHECK_UNOWNED\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIallow_user_list\fP
+Allow/Forbid the list of users on the system on display managers (kdm and gdm).
+
+MSEC parameter: \fIALLOW_USER_LIST\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIallow_remote_root_login\fP
+Allow/Forbid remote root login via sshd. You can specify yes, no and without-password. See sshd_config(5) man page for more information.
+
+MSEC parameter: \fIALLOW_REMOTE_ROOT_LOGIN\fP
+
+Accepted values: \fIyes, no, without_password\fP
+
+
+.TP 4
+.B \fIenable_msec_cron\fP
+Enable/Disable msec hourly security check.
+
+MSEC parameter: \fIENABLE_MSEC_CRON\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIenable_sulogin\fP
+Enable/Disable sulogin(8) in single user level.
+
+MSEC parameter: \fIENABLE_SULOGIN\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIallow_xauth_from_root\fP
+Allow/forbid to export display when passing from the root account to the other users. See pam_xauth(8) for more details.
+
+MSEC parameter: \fIALLOW_XAUTH_FROM_ROOT\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIset_user_umask\fP
+Set the user umask.
+
+MSEC parameter: \fIUSER_UMASK\fP
+
+Accepted values: \fI*\fP
+
+
+.TP 4
+.B \fIaccept_icmp_echo\fP
+Accept/Refuse icmp echo.
+
+MSEC parameter: \fIACCEPT_ICMP_ECHO\fP
+
+Accepted values: \fIyes, no\fP
+
+
+.TP 4
+.B \fIauthorize_services\fP
+Configure access to tcp_wrappers services (see hosts.deny(5)). If arg = yes, all services are authorized. If arg = local, only local ones are, and if arg = no, no services are authorized. In this case, To authorize the services you need, use /etc/hosts.allow (see hosts.allow(5)).
+
+MSEC parameter: \fIAUTHORIZE_SERVICES\fP
+
+Accepted values: \fIyes, no, local\fP
+
+
+.TP 4
+.B \fItty_warn\fP
+Enables periodic security check results to terminal.
+
+MSEC parameter: \fITTY_WARN\fP
+
+Accepted values: \fIyes, no\fP
+
+.RE
+.SH NOTES
+Msec applications must be run by root.
+.SH AUTHORS
+Frederic Lepied <flepied@mandriva.com>
+
+Eugeni Dodonov <eugeni@mandriva.com>
-.SH AUTHOR
-Vandoorselaere Yoann, Mandriva
diff --git a/man/C/mseclib.3 b/man/C/mseclib.3
deleted file mode 100644
index d5999a5..0000000
--- a/man/C/mseclib.3
+++ /dev/null
@@ -1,228 +0,0 @@
-.ds q \N'34'
-.TH mseclib 3 V0 msec "Mandriva Linux"
-.SH NAME
-mseclib
-.SH SYNOPSIS
-.nf
-.B from mseclib import *
-.B function1(yes)
-.B function2(ignore)
-.fi
-.SH DESCRIPTION
-.B mseclib
-is a python library to access the function used by the msec program. This functions can be used
-in /etc/security/msec/level.local to override the behaviour of the msec program or in standalone
-scripts. The first argument of the functions takes a value of 1 or 0 or -1 (or yes/no/ignore)
-except when specified otherwise.
-
-.TP 4
-.B \fIaccept_bogus_error_responses(arg)\fP
-Accept/Refuse bogus IPv4 error messages.
-
-.TP 4
-.B \fIaccept_broadcasted_icmp_echo(arg)\fP
- Accept/Refuse broadcasted icmp echo.
-
-.TP 4
-.B \fIaccept_icmp_echo(arg)\fP
- Accept/Refuse icmp echo.
-
-.TP 4
-.B \fIallow_autologin(arg)\fP
-Allow/Forbid autologin.
-
-.TP 4
-.B \fIallow_issues(arg)\fP
-If \fIarg\fP = ALL allow /etc/issue and /etc/issue.net to exist. If \fIarg\fP = NONE no issues are
-allowed else only /etc/issue is allowed.
-
-.TP 4
-.B \fIallow_reboot(arg)\fP
-Allow/Forbid reboot by the console user.
-
-.TP 4
-.B \fIallow_remote_root_login(arg)\fP
-Allow/Forbid remote root login via sshd. You can specify
-yes, no and without-password. See sshd_config(5) man page for more
-information.
-
-.TP 4
-.B \fIallow_root_login(arg)\fP
-Allow/Forbid direct root login.
-
-.TP 4
-.B \fIallow_user_list(arg)\fP
-Allow/Forbid the list of users on the system on display managers (kdm and gdm).
-
-.TP 4
-.B \fIallow_x_connections(arg, listen_tcp=None)\fP
-Allow/Forbid X connections. First arg specifies what is done
-on the client side: ALL (all connections are allowed), LOCAL (only
-local connection) and NONE (no connection).
-
-.TP 4
-.B \fIallow_xauth_from_root(arg)\fP
-llow/forbid to export display when passing from the root account
-to the other users. See pam_xauth(8) for more details.
-
-.TP 4
-.B \fIallow_xserver_to_listen(arg)\fP
-The argument specifies if clients are authorized to connect
-to the X server on the tcp port 6000 or not.
-
-.TP 4
-.B \fIauthorize_services(arg)\fP
-Authorize all services controlled by tcp_wrappers (see hosts.deny(5)) if \fIarg\fP = ALL. Only local ones
-if \fIarg\fP = LOCAL and none if \fIarg\fP = NONE. To authorize the services you need, use /etc/hosts.allow
-(see hosts.allow(5)).
-
-.TP 4
-.B \fIcreate_server_link()\fP
-If SERVER_LEVEL (or SECURE_LEVEL if absent) is greater than 3
-in /etc/security/msec/security.conf, creates the symlink /etc/security/msec/server
-to point to /etc/security/msec/server.<SERVER_LEVEL>. The /etc/security/msec/server
-is used by chkconfig --add to decide to add a service if it is present in the file
-during the installation of packages.
-
-.TP 4
-.B \fIenable_at_crontab(arg)\fP
-Enable/Disable crontab and at for users. Put allowed users in /etc/cron.allow and /etc/at.allow
-(see man at(1) and crontab(1)).
-
-.TP 4
-.B \fIenable_console_log(arg, expr='*.*', dev='tty12')\fP
-Enable/Disable syslog reports to console 12. \fIexpr\fP is the
-expression describing what to log (see syslog.conf(5) for more details) and
-dev the device to report the log.
-
-.TP 4
-.B \fIenable_dns_spoofing_protection(arg, alert=1)\fP
-Enable/Disable name resolution spoofing protection. If
-\fIalert\fP is true, also reports to syslog.
-
-.TP 4
-.B \fIenable_ip_spoofing_protection(arg, alert=1)\fP
-Enable/Disable IP spoofing protection.
-
-.TP 4
-.B \fIenable_libsafe(arg)\fP
-Enable/Disable libsafe if libsafe is found on the system.
-
-.TP 4
-.B \fIenable_log_strange_packets(arg)\fP
-Enable/Disable the logging of IPv4 strange packets.
-
-.TP 4
-.B \fIenable_msec_cron(arg)\fP
-Enable/Disable msec hourly security check.
-
-.TP 4
-.B \fIenable_pam_root_from_wheel(arg)\fP
- Allow root access without password for the members of the wheel group.
-
-.TP 4
-.B \fIenable_pam_wheel_for_su(arg)\fP
- Enabling su only from members of the wheel group or allow su from any user.
-
-.TP 4
-.B \fIenable_password(arg)\fP
-Use password to authenticate users.
-
-.TP 4
-.B \fIenable_promisc_check(arg)\fP
-Activate/Disable ethernet cards promiscuity check.
-
-.TP 4
-.B \fIenable_security_check(arg)\fP
- Activate/Disable daily security check.
-
-.TP 4
-.B \fIenable_sulogin(arg)\fP
- Enable/Disable sulogin(8) in single user level.
-
-.TP 4
-.B \fIno_password_aging_for(name)\fP
-Add the name as an exception to the handling of password aging by msec.
-Name must be put between '. Msec will then no more manage password aging for
-name so you have to use chage(1) to manage it by hand.
-
-.TP 4
-.B \fIpassword_aging(max, inactive=-1)\fP
-Set password aging to \fImax\fP days and delay to change to \fIinactive\fP.
-
-.TP 4
-.B \fIpassword_history(arg)\fP
-Set the password history length to prevent password reuse.
-
-.TP 4
-.B \fIpassword_length(length, ndigits=0, nupper=0)\fP
-Set the password minimum length and minimum number of digit and minimum number of capitalized letters.
-
-.TP 4
-.B \fIset_root_umask(umask)\fP
-Set the root umask.
-
-.TP 4
-.B \fIset_security_conf(var, value)\fP
-Set the variable \fIvar\fP to the value \fIvalue\fP in /var/lib/msec/security.conf.
-The best way to override the default setting is to create /etc/security/msec/security.conf
-with the value you want. These settings are used to configure the daily check run each night.
-
-The following variables are currentrly recognized by msec:
-
-CHECK_UNOWNED if set to yes, report unowned files.
-
-CHECK_SHADOW if set to yes, check empty password in /etc/shadow.
-
-CHECK_SUID_MD5 if set to yes, verify checksum of the suid/sgid files.
-
-CHECK_SECURITY if set to yes, run the daily security checks.
-
-CHECK_PASSWD if set to yes, check for empty passwords, for no password in /etc/shadow and for users with the 0 id other than root.
-
-SYSLOG_WARN if set to yes, report check result to syslog.
-
-CHECK_SUID_ROOT if set to yes, check additions/removals of suid root files.
-
-CHECK_PERMS if set to yes, check permissions of files in the users' home.
-
-CHKROOTKIT_CHECK if set to yes, run chkrootkit checks.
-
-CHECK_PROMISC if set to yes, check if the network devices are in promiscuous mode.
-
-RPM_CHECK if set to yes, run some checks against the rpm database.
-
-TTY_WARN if set to yes, reports check result to tty.
-
-CHECK_WRITABLE if set to yes, check files/directories writable by everybody.
-
-MAIL_WARN if set to yes, report check result by mail.
-
-MAIL_USER if set, send the mail report to this email address else send it to root.
-
-CHECK_OPEN_PORT if set to yes, check open ports.
-
-CHECK_SGID if set to yes, check additions/removals of sgid files.
-
-EXCLUDE_REGEXP is used to exclude files from consideration by msec.
-
-.TP 4
-.B \fIset_shell_history_size(size)\fP
-Set shell commands history size. A value of -1 means unlimited.
-
-.TP 4
-.B \fIset_shell_timeout(val)\fP
-Set the shell timeout. A value of zero means no timeout.
-
-.TP 4
-.B \fIset_user_umask(umask)\fP
-Set the user umask.
-
-.TP 4
-.B \fIset_win_parts_umask(umask)\fP
-Set umask option for mounting vfat and ntfs partitions. A value of None means default umask.
-.RE
-.SH "SEE ALSO"
-msec(8)
-.SH AUTHORS
-Frederic Lepied <flepied@mandriva.com>
diff --git a/share/Config.py b/share/Config.py
deleted file mode 100644
index 14699b4..0000000
--- a/share/Config.py
+++ /dev/null
@@ -1,44 +0,0 @@
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : msec
-# File : Config.py
-# Version : $Id$
-# Author : Frederic Lepied
-# Created On : Thu Dec 6 19:54:35 2001
-# Purpose : configuration settings
-#---------------------------------------------------------------
-
-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=int(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
deleted file mode 100644
index 26a93e6..0000000
--- a/share/ConfigFile.py
+++ /dev/null
@@ -1,453 +0,0 @@
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : msec
-# 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
-
-STRING_TYPE = type('')
-
-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
- try:
- os.rename(old, new)
- except:
- error('rename %s %s: %s' % (old, new, str(sys.exc_value)))
-
-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, really=0):
- return os.path.exists(self.path) or (not really and 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():
- try:
- os.unlink(self.path)
- except:
- error('unlink %s: %s' % (self.path, str(sys.exc_value)))
- log(_('deleted %s') % (self.path,))
- elif self.is_modified:
- content = string.join(self.lines, "\n")
- mkdir_p(os.path.dirname(self.path))
- 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):
- try:
- os.utime(self.path, None)
- except:
- error('utime %s: %s' % (self.path, str(sys.exc_value)))
- elif self.suffix and os.path.exists(self.path + self.suffix):
- move(self.path + self.suffix, self.path)
- try:
- os.utime(self.path, None)
- except:
- error('utime %s: %s' % (self.path, str(sys.exc_value)))
- 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:
- try:
- os.unlink(self.path)
- except:
- error('unlink %s: %s' % (self.path, str(sys.exc_value)))
- log(_('deleted %s') % (self.path,))
- if not done:
- try:
- os.symlink(self.sym_link, self.path)
- except:
- error('symlink %s %s: %s' % (self.sym_link, self.path, str(sys.exc_value)))
- 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)
- start_regexp = start
-
- 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 status == BEFORE:
- # never found the start delimiter
- log(_('WARNING: never found regexp %s in %s, not writing changes') % (start_regexp, 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, start=None, end=None):
- if end:
- end=re.compile(end)
- if start:
- start=re.compile(start)
- regex = re.compile('^' + var + '="?([^#"]+)"?(.*)')
- lines = self.get_lines()
- llen = len(lines)
- start_idx = 0
- end_idx = llen
- if start:
- found = 0
- for idx in range(0, llen):
- if start.search(lines[idx]):
- start_idx = idx
- found = 1
- break
- if found:
- for idx in range(start_idx, llen):
- if end.search(lines[idx]):
- end_idx = idx
- break
- else:
- start_idx = 0
- for idx in range(end_idx - 1, start_idx - 1, -1):
- 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()
- for idx in range(0, len(lines)):
- res = r.search(lines[idx])
- if res:
- if replace:
- s = substitute_re_result(res, replace)
- return s
- else:
- return lines[idx]
- return None
-
- def replace_line_matching(self, regex, value, at_end_if_not_found=0, all=0, start=None, end=None):
- # if at_end_if_not_found is a string its value will be used as the string to inster
- 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:
- if type(at_end_if_not_found) == STRING_TYPE:
- value = 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_before(self, regex, value, at_top_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 before the line %d:\n%s\nthe line:\n%s") % (self.path, idx, lines[idx], s))
- lines.insert(idx, s)
- self.modified()
- matches = matches + 1
- if not all:
- return matches
- if matches == 0 and at_top_if_not_found:
- log(_("inserted at the top of %s the line:\n%s") % (self.path, value))
- lines.insert(0, 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)
-
-def mkdir_p(path):
- if not os.path.exists(path):
- os.makedirs(path)
-
-# ConfigFile.py ends here
diff --git a/share/Log.py b/share/Log.py
deleted file mode 100644
index 061a5fb..0000000
--- a/share/Log.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#---------------------------------------------------------------
-# Project : Mandriva Llinux
-# Module : msec
-# 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 Mandriva 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(s)
-
-# Log.py ends here
diff --git a/share/Makefile b/share/Makefile
deleted file mode 100644
index 639324d..0000000
--- a/share/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : share
-# File : Makefile
-# Version : $Id$
-# Author : Frederic Lepied
-# Created On : Sat Jan 26 20:17:55 2002
-#---------------------------------------------------------------
-
-MAN=../man/C/mseclib.3
-PFILES=Config.py ConfigFile.py Log.py Perms.py compile.py draksec_help.py libmsec.py man.py
-
-all: mseclib.py compile man help.pm
-
-compile:
- ./compile.py '/usr/share/msec/' *.py
- rm -f msec.pyo
- @for f in $(PFILES); do if grep -q print $$f; then echo "print statement in $$f:"; grep -Hn print $$f; exit 1; fi; done
-
-mseclib.py: libmsec.py shadow.py
- rm -f $@
- ./shadow.py libmsec > $@
-
-man: $(MAN) levels
-
-help.pm: libmsec.py draksec_help.py
- rm -f $@
- ./draksec_help.py libmsec > $@
-
-$(MAN): libmsec.py man.py
- rm -f $@
- ./man.py libmsec > $@
-
-levels:
- for l in 0 1 2 3 4 5; do rm -f level.$$l; \
- ./msec.py -o log=stderr -o print=1 -o nolocal=1 $$l < /dev/null | sort > level.$$l; \
- done
-clean:
- rm -f *.pyc *.pyo mseclib.py *~ level.[0-5] help.pm
-
-# Local variables:
-# mode: makefile
-# End:
-#
-# Makefile ends here
diff --git a/share/Perms.py b/share/Perms.py
deleted file mode 100755
index ff4af73..0000000
--- a/share/Perms.py
+++ /dev/null
@@ -1,305 +0,0 @@
-#!/usr/bin/python -O
-#---------------------------------------------------------------
-# Project : Mandriva 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
-import sys
-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]
-
-# Build a regexp that matches all the non local filesystems
-REGEXP_START = '^('
-REGEXP_END = ')'
-
-def build_non_localfs_regexp():
- # Allow to avoid this feature
- if Config.get_config('all-local-files', '0') == '1':
- return None
-
- try:
- file = open('/proc/mounts', 'r')
- except IOError:
- error(_('Unable to check /proc/mounts. Assuming all file systems are local.'))
- return None
-
- non_localfs = Config.get_config('non-local-fstypes', None)
- if non_localfs:
- non_localfs = string.split(non_localfs)
- else:
- non_localfs = ('nfs', 'codafs', 'smbfs')
-
- regexp = None
-
- for line in file.readlines():
- fields = string.split(line)
- if fields[2] in non_localfs:
- if regexp:
- regexp = regexp + '|' + fields[1]
- else:
- regexp = REGEXP_START + fields[1]
-
- file.close()
-
- if not regexp:
- return None
- else:
- return re.compile(regexp + REGEXP_END)
-
-# put the new perm/group/owner in the assoc variable according to the
-# content of the path file.
-assoc = {}
-
-def fix_perms(path, _interactive, force):
- try:
- file = open(path, 'r')
- except IOError:
- return
- root = Config.get_config('root', '')
-
- fs_regexp = build_non_localfs_regexp()
-
- lineno = 0
- for line in file.readlines():
- lineno = lineno + 1
-
- if comment_regex.search(line):
- continue
-
- fields = re.split('\s*', line)
- try:
- mode_str = fields[2]
- except IndexError:
- error(_("%s: syntax error line %d") % (path, lineno))
- continue
-
- if mode_str == 'current':
- perm = -1
- else:
- try:
- perm = int(mode_str, 8)
- except ValueError:
- error(_("%s: syntax error line %d") % (path, lineno))
- continue
-
- if fields[1] == 'current':
- user = group = -1
- user_str = group_str = ''
- else:
- (user_str, group_str) = string.split(fields[1], '.')
- if user_str != '':
- user = get_user_id(user_str)
- else:
- user = -1
- if group_str != '':
- group = get_group_id(group_str)
- else:
- group = -1
-
- fieldcount = len(fields)
- if fieldcount == 5:
- if fields[3] == 'force':
- mandatory = 1
- fieldcount = 4
- else:
- mandatory = 0
-
- if fieldcount == 4:
- for f in glob.glob(fields[0]):
- newperm = perm
- f = os.path.realpath(f)
- try:
- full = os.lstat(f)
- except OSError:
- continue
-
- if fs_regexp and fs_regexp.search(f):
- _interactive and log(_('Non local file: "%s". Nothing changed.') % fields[0])
- continue
-
- mode = stat.S_IMODE(full[stat.ST_MODE])
-
- if newperm != -1 and 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, mandatory or force)
- else:
- error(_('invalid syntax in %s line %d') % (path, lineno))
- file.close()
-
-# commit the changes to the files
-def act(change):
- for f in assoc.keys():
- (mode, uid, gid, newperm, user, group, user_str, group_str, mandatory) = assoc[f]
- # if we don't change the security level, try not to lower the security
- # if the user has changed it manually
- if not change and not mandatory:
- newperm = newperm & mode
- if newperm != -1 and mode != newperm:
- try:
- os.chmod(f, newperm)
- log(_('changed mode of %s from %o to %o') % (f, mode, newperm))
- except:
- error('chmod %s %o: %s' % (f, newperm, str(sys.exc_value)))
- if user != -1 and user != uid:
- try:
- os.chown(f, user, -1)
- log(_('changed owner of %s from %s to %s') % (f, get_user_name(uid), user_str))
- except:
- error('chown %s %s: %s' % (f, user, str(sys.exc_value)))
- if group != -1 and group != gid:
- try:
- os.chown(f, -1, group)
- log(_('changed group of %s from %s to %s') % (f, get_group_name(gid), group_str))
- except:
- error('chgrp %s %s: %s' % (f, group, str(sys.exc_value)))
-
-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))
- try:
- os.chmod(f, newperm)
- except:
- error('chmod %s %o: %s' % (f, newperm, str(sys.exc_value)))
- return 1
-
-if __name__ == '__main__':
- import getopt
-
- _interactive = sys.stdin.isatty()
- change = 0
-
- # process the options
- try:
- (opt, args) = getopt.getopt(sys.argv[1:], 'co:',
- ['change', '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])
- elif o[0] == '-c' or o[0] == '--change':
- change = 1
-
- # 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')
-
- _interactive and log(_('Fixing owners and permissions of files and directories'))
-
- # process the files
- fix_perms(args[0], _interactive, 0)
- for p in args[1:]:
- _interactive and log(_('Reading data from %s') % p)
- fix_perms(p, _interactive, 1)
-
- # do the modifications
- act(change)
-
-# Perms.py ends here
diff --git a/share/draksec_help.py b/share/draksec_help.py
deleted file mode 100755
index b57ab86..0000000
--- a/share/draksec_help.py
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/usr/bin/python
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : share
-# File : draksec_help.py
-# Version : $Id$
-# Author : Thierry Vignaud
-# Created On : Sat Jan 26 17:38:39 2002
-# Purpose : loads a python module and creates a help hash
-# for draksec.
-#---------------------------------------------------------------
-
-import sys
-import imp
-import inspect
-import re
-
-header = '''package security::help;
-# !! THIS FILE WAS AUTO-GENERATED BY draksec_help.py !!
-# !! DO NOT MODIFY HERE, MODIFY IN THE *MSEC* CVS !!
-
-use strict;
-use common;
-
-our %help = (
-'''
-
-footer = ''');
-'''
-
-### strings used in the rewritting
-function_str = '''
-'%s' => N("Arguments: %s
-%s"),
-'''
-
-### code
-modulename = sys.argv[1]
-
-module = __import__(modulename)
-
-sys.stdout.write(header)
-
-clean = re.compile('^.[a-z].*\n', re.M)
-clean2 = re.compile('^\n', re.M)
-perl = re.compile('^([A-Z_0-9]*) (.*)$', re.M)
-
-for f in inspect.getmembers(module, inspect.isfunction):
- (args, varargs, varkw, locals) = inspect.getargspec(f[1])
- doc = f[1].__doc__
- if doc and len(doc) > 2:
- doc = doc[2:]
- argspec = inspect.formatargspec(args, varargs, varkw, locals) + '\n'
- if f[0] == 'set_security_conf':
- doc = clean.sub('', doc)
- doc = clean2.sub('', doc)
- doc = perl.sub('\\1 => N("\\2"),', doc)
- sys.stdout.write(doc)
- else:
- s = function_str % (f[0], argspec, doc)
- sys.stdout.write(s)
-
-sys.stdout.write(footer)
-
-# draksec_help.py ends here
diff --git a/share/libmsec.py b/share/libmsec.py
deleted file mode 100644
index 2ef2e3c..0000000
--- a/share/libmsec.py
+++ /dev/null
@@ -1,1391 +0,0 @@
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : msec
-# File : libmsec.py
-# Version : $Id$
-# Author : Frederic Lepied
-# Created On : Mon Dec 10 22:52:17 2001
-# Purpose : all access points of the msec utility.
-#---------------------------------------------------------------
-
-import ConfigFile
-import Config
-from Log import *
-
-import os
-import grp
-import Perms
-import gettext
-import pwd
-import re
-import string
-import commands
-import time
-import traceback
-
-try:
- cat = gettext.Catalog('msec')
- _ = cat.gettext
-except IOError:
- _ = str
-
-SUFFIX='.msec'
-_interactive=0
-_same_level=1
-FORCED = {}
-
-# 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'
-FSTAB = '/etc/fstab'
-GDM = '/etc/pam.d/gdm'
-GDMCONF = '/etc/X11/gdm/custom.conf'
-HALT = '/usr/bin/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'
-OPASSWD = '/etc/security/opasswd'
-PASSWD = '/etc/pam.d/passwd'
-POWEROFF = '/usr/bin/poweroff'
-REBOOT = '/usr/bin/reboot'
-SECURETTY = '/etc/securetty'
-SECURITYCONF = '/var/lib/msec/security.conf'
-SECURITYCONF2 = '/etc/security/msec/security.conf'
-SECURITYCRON = '/etc/cron.daily/msec'
-SECURITYSH = '/usr/share/msec/security.sh'
-SERVER = '/etc/security/msec/server'
-SHADOW = '/etc/shadow'
-SHUTDOWN = '/usr/bin/shutdown'
-SHUTDOWNALLOW = '/etc/shutdown.allow'
-SIMPLE_ROOT_AUTHEN = '/etc/pam.d/simple_root_authen'
-SSHDCONFIG = '/etc/ssh/sshd_config'
-STARTX = '/usr/bin/startx'
-SU = '/etc/pam.d/su'
-SYSCTLCONF = '/etc/sysctl.conf'
-SYSLOGCONF = '/etc/syslog.conf'
-SYSTEM_AUTH = '/etc/pam.d/system-auth'
-XDM = '/etc/pam.d/xdm'
-XSERVERS = '/etc/X11/xdm/Xservers'
-EXPORT = '/root/.xauth/export'
-
-# constants to keep in sync with shadow.py
-NONE=0
-ALL=1
-LOCAL=2
-
-no=0
-yes=1
-without_password=2
-
-ALL_LOCAL_NONE_TRANS = {ALL : 'ALL', NONE: 'NONE', LOCAL : 'LOCAL'}
-YES_NO_TRANS = {yes : 'yes', no : 'no'}
-ALLOW_ROOT_LOGIN_TRANS = {no : 'no', yes : 'yes', without_password : 'without_password'}
-
-# 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')
-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')
-
-# functions
-
-################################################################################
-
-# The same_level function inspects the call stack in the 2 previous
-# levels to see if a function is used that has been registered by
-# force_val and if this is the case we act as if we were changing the
-# security level to force the value to be used.
-def same_level():
- 'D'
- tb = traceback.extract_stack()
- if FORCED.has_key(tb[-2][2]) or FORCED.has_key(tb[-3][2]):
- return 0
- else:
- return _same_level
-
-def changing_level():
- 'D'
- global _same_level
- _same_level=0
-
-def force_val(name):
- 'D'
- global FORCED
- FORCED[name] = 1
-
-# configuration rules
-
-################################################################################
-
-def set_secure_level(level):
- msec = ConfigFile.get_config_file(MSEC)
-
- val = msec.get_shell_variable('SECURE_LEVEL')
-
- if not val or int(val) != level:
- _interactive and log(_('Setting secure level to %s') % level)
- msec.set_shell_variable('SECURE_LEVEL', level)
-
-################################################################################
-
-def get_secure_level():
- 'D'
- msec = ConfigFile.get_config_file(MSEC)
- return msec.get_shell_variable('SECURE_LEVEL')
-
-################################################################################
-
-def set_server_level(level):
- _interactive and log(_('Setting server level to %s') % level)
- securityconf = ConfigFile.get_config_file(SECURITYCONF2)
- securityconf.set_shell_variable('SERVER_LEVEL', level)
-
-################################################################################
-
-def get_server_level():
- 'D'
- securityconf = ConfigFile.get_config_file(SECURITYCONF2)
- level = securityconf.get_shell_variable('SERVER_LEVEL')
- if level: return level
- msec = ConfigFile.get_config_file(MSEC)
- return msec.get_shell_variable('SECURE_LEVEL')
-
-################################################################################
-
-def create_server_link():
- ''' If SERVER_LEVEL (or SECURE_LEVEL if absent) is greater than 3
-in /etc/security/msec/security.conf, creates the symlink /etc/security/msec/server
-to point to /etc/security/msec/server.<SERVER_LEVEL>. The /etc/security/msec/server
-is used by chkconfig --add to decide to add a service if it is present in the file
-during the installation of packages.'''
- level = get_server_level()
- server = ConfigFile.get_config_file(SERVER)
- if level in ('0', '1', '2', '3'):
- _interactive and log(_('Allowing chkconfig --add from rpm'))
- server.exists() and server.unlink()
- else:
- _interactive and log(_('Restricting chkconfig --add from rpm'))
- server.symlink(SERVER + '.' + str(level))
-
-create_server_link.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-STRING_TYPE = type('')
-
-# helper function for set_root_umask and set_user_umask
-def set_umask(variable, umask, msg):
- 'D'
- msec = ConfigFile.get_config_file(MSEC)
-
- if type(umask) == STRING_TYPE:
- umask = int(umask, 8)
-
- if msec.exists():
- val = msec.get_shell_variable(variable)
- else:
- val = None
-
- # don't lower security when not changing security level
- if same_level():
- if val:
- octal = umask | int(val, 8)
- umask = '0%o' % octal
-
- if type(umask) != STRING_TYPE:
- umask = '0%o' % umask
-
- if val != umask:
- _interactive and log(_('Setting %s umask to %s') % (msg, umask))
- msec.set_shell_variable(variable, umask)
-
-def set_root_umask(umask):
- ''' Set the root umask.'''
- set_umask('UMASK_ROOT', umask, 'root')
-
-def set_user_umask(umask):
- ''' Set the user umask.'''
- set_umask('UMASK_USER', umask, 'users')
-
-################################################################################
-
-# the listen_tcp argument is kept for backward compatibility
-def allow_x_connections(arg, listen_tcp=None):
- ''' Allow/Forbid X connections. First arg specifies what is done
-on the client side: ALL (all connections are allowed), LOCAL (only
-local connection) and NONE (no connection).'''
-
- msec = ConfigFile.get_config_file(MSEC_XINIT)
-
- val = msec.exists() and msec.get_match('/usr/bin/xhost\s*\+\s*([^#]*)')
-
- if val:
- if val == '':
- val = ALL
- elif val == 'localhost':
- val = LOCAL
- else:
- val = NONE
- else:
- val = NONE
-
- # don't lower security when not changing security level
- if same_level():
- if val == NONE or (val == LOCAL and arg == ALL):
- return
-
- if arg == ALL:
- if val != arg:
- _interactive and log(_('Allowing users to connect X server from everywhere'))
- msec.exists() and msec.replace_line_matching('/usr/bin/xhost', '/usr/bin/xhost +', 1)
-
- elif arg == LOCAL:
- if val != arg:
- _interactive and log(_('Allowing users to connect X server from localhost'))
- msec.exists() and msec.replace_line_matching('/usr/bin/xhost', '/usr/bin/xhost + localhost', 1)
-
- elif arg == NONE:
- if val != arg:
- _interactive and log(_('Restricting X server connection to the console user'))
- msec.exists() and msec.remove_line_matching('/usr/bin/xhost', 1)
-
- else:
- error(_('invalid allow_x_connections arg: %s') % arg)
- return
-
-allow_x_connections.arg_trans=ALL_LOCAL_NONE_TRANS
-allow_x_connections.one_arg = 1
-
-################################################################################
-
-STARTX_REGEXP = '(\s*serverargs=".*) -nolisten tcp(.*")'
-XSERVERS_REGEXP = '(\s*[^#]+/usr/bin/X .*) -nolisten tcp(.*)'
-GDMCONF_REGEXP = '(\s*command=.*/X.*?) -nolisten tcp(.*)$'
-KDMRC_REGEXP = re.compile('(.*?)-nolisten tcp(.*)$')
-
-def allow_xserver_to_listen(arg):
- ''' The argument specifies if clients are authorized to connect
-to the X server on the tcp port 6000 or not.'''
-
- startx = ConfigFile.get_config_file(STARTX)
- xservers = ConfigFile.get_config_file(XSERVERS)
- gdmconf = ConfigFile.get_config_file(GDMCONF)
- kdmrc = ConfigFile.get_config_file(KDMRC)
-
- val_startx = startx.exists() and startx.get_match(STARTX_REGEXP)
- val_xservers = xservers.exists() and xservers.get_match(XSERVERS_REGEXP)
- val_gdmconf = gdmconf.exists() and gdmconf.get_match(GDMCONF_REGEXP)
- str = kdmrc.exists() and kdmrc.get_shell_variable('ServerArgsLocal', 'X-\*-Core', '^\s*$')
-
- if str:
- val_kdmrc = KDMRC_REGEXP.search(str)
- else:
- val_kdmrc = None
-
- # don't lower security when not changing security level
- if same_level():
- if val_startx and val_xservers and val_gdmconf and val_kdmrc:
- return
-
- if arg:
- if val_startx or val_xservers or val_gdmconf or val_kdmrc:
- _interactive and log(_('Allowing the X server to listen to tcp connections'))
- if not (same_level() and val_startx):
- startx.exists() and startx.replace_line_matching(STARTX_REGEXP, '@1@2')
- if not (same_level() and val_xservers):
- xservers.exists() and xservers.replace_line_matching(XSERVERS_REGEXP, '@1@2', 0, 1)
- if not (same_level() and val_gdmconf):
- gdmconf.exists() and gdmconf.replace_line_matching(GDMCONF_REGEXP, '@1@2', 0, 1)
- if not (same_level() and val_kdmrc):
- kdmrc.exists() and kdmrc.replace_line_matching('^(ServerArgsLocal=.*?)-nolisten tcp(.*)$', '@1@2', 0, 0, 'X-\*-Core', '^\s*$')
- else:
- if not val_startx or not val_xservers or not val_gdmconf or not val_kdmrc:
- _interactive and log(_('Forbidding the X server to listen to tcp connection'))
- startx.exists() and not val_startx and startx.replace_line_matching('serverargs="(.*?)( -nolisten tcp)?"', 'serverargs="@1 -nolisten tcp"')
- xservers.exists() and not val_xservers and xservers.replace_line_matching('(\s*[^#]+/usr/bin/X .*?)( -nolisten tcp)?$', '@1 -nolisten tcp', 0, 1)
- gdmconf.exists() and not val_gdmconf and gdmconf.replace_line_matching('(\s*command=.*/X.*?)( -nolisten tcp)?$', '@1 -nolisten tcp', 0, 1)
- kdmrc.exists() and not val_kdmrc and kdmrc.replace_line_matching('^(ServerArgsLocal=.*)( -nolisten tcp)?$', '@1 -nolisten tcp', 'ServerArgsLocal=-nolisten tcp', 0, 'X-\*-Core', '^\s*$')
-
-allow_xserver_to_listen.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def set_shell_timeout(val):
- ''' Set the shell timeout. A value of zero means no timeout.'''
-
- msec = ConfigFile.get_config_file(MSEC)
-
- if msec.exists():
- old = msec.get_shell_variable('TMOUT')
- if old != None:
- old = int(old)
- else:
- old = None
-
- # don't lower security when not changing security level
- if same_level():
- if old != None and old > val:
- return
-
- if old != val:
- _interactive and log(_('Setting shell timeout to %s') % val)
- msec.set_shell_variable('TMOUT', val)
-
-################################################################################
-
-def set_shell_history_size(size):
- ''' Set shell commands history size. A value of -1 means unlimited.'''
- msec = ConfigFile.get_config_file(MSEC)
-
- if msec.exists():
- val = msec.get_shell_variable('HISTFILESIZE')
- else:
- val = None
-
- # don't lower security when not changing security level
- if same_level():
- if val != None:
- val = int(val)
- if size == -1 or val < size:
- return
-
- if size >= 0:
- if val != size:
- _interactive and log(_('Setting shell history size to %s') % size)
- msec.set_shell_variable('HISTFILESIZE', size)
- else:
- if val != None:
- _interactive and log(_('Removing limit on shell history size'))
- msec.remove_line_matching('^HISTFILESIZE=')
-
-################################################################################
-
-def set_win_parts_umask(umask):
- ''' Set umask option for mounting vfat and ntfs partitions. A value of None means default umask.'''
- fstab = ConfigFile.get_config_file(FSTAB)
-
- # don't lower security when not changing security level
- if same_level():
- if umask != None:
- return
-
- if umask == None:
- fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+)umask=\d+(\s.*)", "@1defaults@3", 0, 1)
- fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+)umask=\d+,(.*)", "@1@3", 0, 1)
- fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+\S+),umask=\d+(.*)", "@1@3", 0, 1)
- else:
- fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+\S*)umask=\d+(.*)", "@1umask=0@3", 0, 1)
- fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+)(?!.*umask=)(\S+)(.*)", "@1@3,umask=0@4", 0, 1)
-
-################################################################################
-
-def get_index(val, array):
- for loop in range(0, len(array)):
- if val == array[loop]:
- return loop
- return -1
-
-################################################################################
-ALLOW_SHUTDOWN_VALUES = ('All', 'Root', 'None')
-CTRALTDEL_REGEXP = '^ca::ctrlaltdel:/sbin/shutdown.*'
-CONSOLE_HELPER = 'consolehelper'
-
-def allow_reboot(arg):
- ''' Allow/Forbid reboot by the console user.'''
- shutdownallow = ConfigFile.get_config_file(SHUTDOWNALLOW)
- sysctlconf = ConfigFile.get_config_file(SYSCTLCONF)
- kdmrc = ConfigFile.get_config_file(KDMRC)
- gdmconf = ConfigFile.get_config_file(GDMCONF)
- inittab = ConfigFile.get_config_file(INITTAB)
-
- val_shutdownallow = shutdownallow.exists()
- val_sysctlconf = sysctlconf.exists() and sysctlconf.get_shell_variable('kernel.sysrq')
- val_inittab = inittab.exists() and inittab.get_match(CTRALTDEL_REGEXP)
- num = 0
- val = {}
- for f in [SHUTDOWN, POWEROFF, REBOOT, HALT]:
- val[f] = ConfigFile.get_config_file(f).exists()
- if val[f]:
- num = num + 1
- val_gdmconf = gdmconf.exists() and gdmconf.get_shell_variable('SystemMenu')
- oldval_kdmrc = kdmrc.exists() and kdmrc.get_shell_variable('AllowShutdown', 'X-:\*-Core', '^\s*$')
- if oldval_kdmrc:
- oldval_kdmrc = get_index(oldval_kdmrc, ALLOW_SHUTDOWN_VALUES)
- if arg:
- val_kdmrc = 0
- else:
- val_kdmrc = 2
-
- # don't lower security when not changing security level
- if same_level():
- if val_shutdownallow and val_sysctlconf == '0' and num == 0 and oldval_kdmrc >= val_kdmrc and val_gdmconf == 'false' and not val_inittab:
- return
- if oldval_kdmrc > val_kdmrc:
- val_kdmrc = oldval_kdmrc
-
- if arg:
- _interactive and log(_('Allowing reboot to the console user'))
- if not (same_level() and val_shutdownallow):
- shutdownallow.exists() and shutdownallow.move(SUFFIX)
- for f in [SHUTDOWN, POWEROFF, REBOOT, HALT]:
- cfg = ConfigFile.get_config_file(f)
- if not (same_level() and not val[f]):
- cfg.exists() or cfg.symlink(CONSOLE_HELPER)
- if not (same_level() and val_sysctlconf == '0'):
- sysctlconf.set_shell_variable('kernel.sysrq', 1)
- if not same_level() and val_gdmconf == 'false':
- gdmconf.exists() and gdmconf.set_shell_variable('SystemMenu', 'true', '\[greeter\]', '^\s*$')
- if not (same_level() and not val_inittab):
- inittab.replace_line_matching(CTRALTDEL_REGEXP, 'ca::ctrlaltdel:/sbin/shutdown -t3 -r now', 1)
- else:
- _interactive and log(_('Forbidding reboot to the console user'))
- ConfigFile.get_config_file(SHUTDOWNALLOW, SUFFIX).touch()
- for f in [SHUTDOWN, POWEROFF, REBOOT, HALT]:
- ConfigFile.get_config_file(f).unlink()
- sysctlconf.set_shell_variable('kernel.sysrq', 0)
- gdmconf.exists() and gdmconf.set_shell_variable('SystemMenu', 'false', '\[greeter\]', '^\s*$')
- inittab.remove_line_matching(CTRALTDEL_REGEXP)
-
- kdmrc.exists() and kdmrc.set_shell_variable('AllowShutdown', ALLOW_SHUTDOWN_VALUES[val_kdmrc], 'X-:\*-Core', '^\s*$')
-
-allow_reboot.arg_trans = YES_NO_TRANS
-
-################################################################################
-SHOW_USERS_VALUES = ('NotHidden', 'Selected')
-
-def allow_user_list(arg):
- ''' Allow/Forbid the list of users on the system on display managers (kdm and gdm).'''
- kdmrc = ConfigFile.get_config_file(KDMRC)
- gdmconf = ConfigFile.get_config_file(GDMCONF)
-
- oldval_gdmconf = gdmconf.exists() and gdmconf.get_shell_variable('Browser')
- oldval_kdmrc = kdmrc.exists() and kdmrc.get_shell_variable('ShowUsers', 'X-\*-Greeter', '^\s*$')
- if oldval_kdmrc:
- oldval_kdmrc = get_index(oldval_kdmrc, SHOW_USERS_VALUES)
-
- if arg:
- msg = 'Allowing the listing of users in display managers'
- val_kdmrc = 0
- val_gdmconf = 'true'
- else:
- msg = 'Disabling the listing of users in display managers'
- val_kdmrc = 1
- val_gdmconf = 'false'
-
- # don't lower security when not changing security level
- if same_level():
- if oldval_kdmrc >= val_kdmrc and oldval_gdmconf == 'false':
- return
- if oldval_kdmrc > val_kdmrc:
- val_kdmrc = oldval_kdmrc
- if oldval_gdmconf == 'false':
- val_gdmconf = 'false'
-
- if (gdmconf.exists() and oldval_gdmconf != val_gdmconf) or (kdmrc.exists() and oldval_kdmrc != val_kdmrc):
- _interactive and log(_(msg))
- oldval_kdmrc != val_gdmconf and kdmrc.exists() and kdmrc.set_shell_variable('ShowUsers', SHOW_USERS_VALUES[val_kdmrc], 'X-\*-Greeter', '^\s*$')
- oldval_gdmconf != val_gdmconf and gdmconf.exists() and gdmconf.set_shell_variable('Browser', val_gdmconf)
-
-allow_user_list.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def allow_root_login(arg):
- ''' Allow/Forbid direct root login.'''
- securetty = ConfigFile.get_config_file(SECURETTY)
- kde = ConfigFile.get_config_file(KDE)
- gdm = ConfigFile.get_config_file(GDM)
- gdmconf = ConfigFile.get_config_file(GDMCONF)
- xdm = ConfigFile.get_config_file(XDM)
-
- val = {}
- val[kde] = kde.exists() and kde.get_match('auth required (?:/lib/security/)?pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login')
- val[gdm] = gdm.exists() and gdm.get_match('auth required (?:/lib/security/)?pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login')
- val[xdm] = xdm.exists() and xdm.get_match('auth required (?:/lib/security/)?pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login')
- num = 0
- for n in range(1, 7):
- s = 'tty' + str(n)
- if securetty.get_match(s):
- num = num + 1
- val[s] = 1
- else:
- val[s] = 0
- s = 'vc/' + str(n)
- if securetty.get_match(s):
- num = num + 1
- val[s] = 1
- else:
- val[s] = 0
-
- # don't lower security when not changing security level
- if same_level():
- if (not kde.exists() or val[kde]) and (not gdm.exists() or val[gdm]) and (not xdm.exists() or val[xdm]) and num == 12:
- return
-
- if arg:
- if val[kde] or val[gdm] or val[xdm] or num != 12:
- _interactive and log(_('Allowing direct root login'))
- gdmconf.exists() and gdmconf.set_shell_variable('ConfigAvailable', 'true', '\[greeter\]', '^\s*')
-
-
- for cnf in (kde, gdm, xdm):
- if not (same_level() and val[cnf]):
- cnf.exists() and cnf.remove_line_matching('^auth\s*required\s*(?:/lib/security/)?pam_listfile.so.*bastille-no-login', 1)
-
- for n in range(1, 7):
- s = 'tty' + str(n)
- if not (same_level() and not val[s]):
- securetty.replace_line_matching(s, s, 1)
- s = 'vc/' + str(n)
- if not (same_level() and not val[s]):
- securetty.replace_line_matching(s, s, 1)
- else:
- gdmconf.exists() and gdmconf.set_shell_variable('ConfigAvailable', 'false', '\[greeter\]', '^\s*')
- if (kde.exists() and not val[kde]) or (gdm.exists() and not val[gdm]) or (xdm.exists() and not val[xdm]) or num > 0:
- _interactive and log(_('Forbidding direct root login'))
-
- bastillenologin = ConfigFile.get_config_file(BASTILLENOLOGIN)
- bastillenologin.replace_line_matching('^\s*root', 'root', 1)
-
- 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 pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login') or \
- cnf.insert_at(0, 'auth required pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login'))
- securetty.remove_line_matching('.+', 1)
-
-allow_root_login.arg_trans = YES_NO_TRANS
-
-PERMIT_ROOT_LOGIN_REGEXP = '^\s*PermitRootLogin\s+(no|yes|without-password|forced-commands-only)'
-
-################################################################################
-
-def allow_remote_root_login(arg):
- ''' Allow/Forbid remote root login via sshd. You can specify
-yes, no and without-password. See sshd_config(5) man page for more
-information.'''
- sshd_config = ConfigFile.get_config_file(SSHDCONFIG)
-
- if sshd_config.exists():
- val = sshd_config.get_match(PERMIT_ROOT_LOGIN_REGEXP, '@1')
- else:
- val = None
-
- # don't lower security when not changing security level
- if same_level():
- if val == 'no':
- return
- if val == 'forced-commands-only':
- return
-
- if val == 'yes':
- val = yes
- elif val == 'no':
- val = no
- elif val == 'without-password':
- val = without_password
- else:
- val = yes
-
- if val != arg:
- if arg == yes:
- _interactive and log(_('Allowing remote root login'))
- sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
- 'PermitRootLogin yes', 1)
- elif arg == no:
- _interactive and log(_('Forbidding remote root login'))
- sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
- 'PermitRootLogin no', 1)
- elif arg == without_password:
- _interactive and log(_('Allowing remote root login only by passphrase'))
- sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
- 'PermitRootLogin without-password', 1)
-
-allow_remote_root_login.arg_trans = ALLOW_ROOT_LOGIN_TRANS
-
-################################################################################
-
-def enable_pam_wheel_for_su(arg):
- ''' Enabling su only from members of the wheel group or allow su from any user.'''
- su = ConfigFile.get_config_file(SU)
-
- val = su.exists() and su.get_match('^auth\s+required\s+(?:/lib/security/)?pam_wheel.so\s+use_uid\s*$')
-
- # don't lower security when not changing security level
- if same_level():
- if val:
- return
-
- if arg:
- if not val:
- _interactive and log(_('Allowing su only from wheel group members'))
- try:
- ent = grp.getgrnam('wheel')
- except KeyError:
- error(_('no wheel group'))
- return
- members = ent[3]
- if members == [] or members == ['root']:
- _interactive and error(_('wheel group is empty'))
- return
- su.exists() and (su.replace_line_matching('^auth\s+required\s+(?:/lib/security/)?pam_wheel.so\s+use_uid\s*$',
- 'auth required pam_wheel.so use_uid') or \
- su.insert_after('^auth\s+required',
- 'auth required pam_wheel.so use_uid'))
- else:
- if val:
- _interactive and log(_('Allowing su for all'))
- su.exists() and su.remove_line_matching('^auth\s+required\s+(?:/lib/security/)?pam_wheel.so\s+use_uid\s*$')
-
-enable_pam_wheel_for_su.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-SUCCEED_MATCH = '^auth\s+sufficient\s+pam_succeed_if.so\s+use_uid\s+user\s+ingroup\s+wheel\s*$'
-SUCCEED_LINE = 'auth sufficient pam_succeed_if.so use_uid user ingroup wheel'
-
-def enable_pam_root_from_wheel(arg):
- ''' Allow root access without password for the members of the wheel group.'''
- su = ConfigFile.get_config_file(SU)
- simple = ConfigFile.get_config_file(SIMPLE_ROOT_AUTHEN)
-
- if not su.exists():
- return
-
- val = su.get_match(SUCCEED_MATCH)
-
- if simple.exists():
- val_simple = simple.get_match(SUCCEED_MATCH)
- else:
- val_simple = False
-
- # don't lower security when not changing security level
- if same_level():
- if not val and not val_simple:
- return
-
- if arg:
- if not val or (simple.exists() and not val_simple):
- _interactive and log(_('Allowing transparent root access for wheel group members'))
- if not val:
- su.insert_before('^auth\s+required', SUCCEED_LINE)
- if simple.exists() and not val_simple:
- simple.insert_before('^auth\s+required', SUCCEED_LINE)
- else:
- if val or (simple.exists() and val_simple):
- _interactive and log(_('Disabling transparent root access for wheel group members'))
- if val:
- su.remove_line_matching(SUCCEED_MATCH)
- if simple.exists() and val_simple:
- simple.remove_line_matching(SUCCEED_MATCH)
-
-enable_pam_root_from_wheel.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def allow_issues(arg):
- ''' If \\fIarg\\fP = ALL allow /etc/issue and /etc/issue.net to exist. If \\fIarg\\fP = NONE no issues are
-allowed else only /etc/issue is allowed.'''
- issue = ConfigFile.get_config_file(ISSUE, SUFFIX)
- issuenet = ConfigFile.get_config_file(ISSUENET, SUFFIX)
-
- val = issue.exists(1)
- valnet = issuenet.exists(1)
-
- # don't lower security when not changing security level
- if same_level():
- if not val and not valnet:
- return
- if arg == ALL and not valnet:
- return
-
- if arg == ALL:
- if not (val and valnet):
- _interactive and log(_('Allowing network pre-login messages'))
- issue.exists() and issue.get_lines()
- issuenet.exists() and issuenet.get_lines()
- else:
- if arg == NONE:
- if val:
- _interactive and log(_('Disabling pre-login message'))
- issue.exists(1) and issue.move(SUFFIX) and issue.modified()
- else:
- if not val:
- _interactive and log(_('Allowing pre-login message'))
- issue.exists() and issue.get_lines()
- if valnet:
- _interactive and log(_('Disabling network pre-login message'))
- issuenet.exists(1) and issuenet.move(SUFFIX)
-
-allow_issues.arg_trans = ALL_LOCAL_NONE_TRANS
-
-################################################################################
-
-def allow_autologin(arg):
- ''' Allow/Forbid autologin.'''
- autologin = ConfigFile.get_config_file(AUTOLOGIN)
-
- if autologin.exists():
- val = autologin.get_shell_variable('AUTOLOGIN')
- else:
- val = None
-
- # don't lower security when not changing security level
- if same_level():
- if val == 'no':
- return
-
- if arg:
- if val != 'yes':
- _interactive and log(_('Allowing autologin'))
- autologin.exists() and autologin.set_shell_variable('AUTOLOGIN', 'yes')
- else:
- if val != 'no':
- _interactive and log(_('Forbidding autologin'))
- autologin.exists() and autologin.set_shell_variable('AUTOLOGIN', 'no')
-
-allow_autologin.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def password_loader(value):
- 'D'
- _interactive and log(_('Activating password in boot loader'))
- liloconf = ConfigFile.get_config_file(LILOCONF)
- liloconf.exists() and (liloconf.replace_line_matching('^password=', 'password="' + value + '"', 0, 1) or \
- liloconf.insert_after('^boot=', 'password="' + value + '"')) and \
- Perms.chmod(liloconf.path, 0600)
- # TODO encrypt password in grub
- menulst = ConfigFile.get_config_file(MENULST)
- menulst.exists() and (menulst.replace_line_matching('^password\s', 'password "' + value + '"') or \
- menulst.insert_at(0, 'password "' + value + '"')) and \
- Perms.chmod(menulst.path, 0600)
- # TODO add yaboot support
-
-################################################################################
-
-def nopassword_loader():
- 'D'
- _interactive and log(_('Removing password in boot loader'))
- liloconf = ConfigFile.get_config_file(LILOCONF)
- liloconf.exists() and liloconf.remove_line_matching('^password=', 1)
- menulst = ConfigFile.get_config_file(MENULST)
- menulst.exists() and menulst.remove_line_matching('^password\s')
-
-################################################################################
-
-def enable_console_log(arg, expr='*.*', dev='tty12'):
- ''' Enable/Disable syslog reports to console 12. \\fIexpr\\fP is the
-expression describing what to log (see syslog.conf(5) for more details) and
-dev the device to report the log.'''
-
- syslogconf = ConfigFile.get_config_file(SYSLOGCONF)
-
- if syslogconf.exists():
- val = syslogconf.get_match('\s*[^#]+/dev/([^ ]+)', '@1')
- else:
- val = None
-
- # don't lower security when not changing security level
- if same_level():
- if val:
- return
-
- if arg:
- if dev != val:
- _interactive and log(_('Enabling log on console'))
- syslogconf.exists() and syslogconf.replace_line_matching('\s*[^#]+/dev/', expr + ' /dev/' + dev, 1)
- else:
- if val != None:
- _interactive and log(_('Disabling log on console'))
- syslogconf.exists() and syslogconf.remove_line_matching('\s*[^#]+/dev/')
-
-enable_console_log.arg_trans = YES_NO_TRANS
-
-CRON_ENTRY = '*/1 * * * * root /usr/share/msec/promisc_check.sh'
-CRON_REGEX = '[^#]+/usr/share/msec/promisc_check.sh'
-
-################################################################################
-
-def enable_promisc_check(arg):
- ''' Activate/Disable ethernet cards promiscuity check.'''
- cron = ConfigFile.get_config_file(CRON)
-
- val = cron.exists() and cron.get_match(CRON_REGEX)
-
- # don't lower security when not changing security level
- if same_level():
- if val == CRON_ENTRY:
- return
-
- if arg:
- if val != CRON_ENTRY:
- _interactive and log(_('Activating periodic promiscuity check'))
- cron.replace_line_matching(CRON_REGEX, CRON_ENTRY, 1)
- else:
- if val:
- _interactive and log(_('Disabling periodic promiscuity check'))
- cron.remove_line_matching('[^#]+/usr/share/msec/promisc_check.sh')
-
-enable_promisc_check.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def enable_security_check(arg):
- ''' Activate/Disable daily security check.'''
- cron = ConfigFile.get_config_file(CRON)
- cron.remove_line_matching('[^#]+/usr/share/msec/security.sh')
-
- securitycron = ConfigFile.get_config_file(SECURITYCRON)
-
- val = securitycron.exists()
-
- # don't lower security when not changing security level
- if same_level():
- if val:
- return
-
- if arg:
- if not val:
- _interactive and log(_('Activating daily security check'))
- securitycron.symlink(SECURITYSH)
- else:
- if val:
- _interactive and log(_('Disabling daily security check'))
- securitycron.unlink()
-
-enable_security_check.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-ALL_REGEXP = '^ALL:ALL:DENY'
-ALL_LOCAL_REGEXP = '^ALL:ALL EXCEPT 127\.0\.0\.1:DENY'
-def authorize_services(arg):
- ''' Authorize all services controlled by tcp_wrappers (see hosts.deny(5)) if \\fIarg\\fP = ALL. Only local ones
-if \\fIarg\\fP = LOCAL and none if \\fIarg\\fP = NONE. To authorize the services you need, use /etc/hosts.allow
-(see hosts.allow(5)).'''
- hostsdeny = ConfigFile.get_config_file(HOSTSDENY)
-
- if hostsdeny.exists():
- if hostsdeny.get_match(ALL_REGEXP):
- val = NONE
- elif hostsdeny.get_match(ALL_LOCAL_REGEXP):
- val = LOCAL
- else:
- val = ALL
- else:
- val = ALL
-
- # don't lower security when not changing security level
- if same_level():
- if val == NONE or (val == LOCAL and arg == ALL):
- return
-
- if arg == ALL:
- if arg != val:
- _interactive and log(_('Authorizing all services'))
- hostsdeny.remove_line_matching(ALL_REGEXP, 1)
- hostsdeny.remove_line_matching(ALL_LOCAL_REGEXP, 1)
- elif arg == NONE:
- if arg != val:
- _interactive and log(_('Disabling all services'))
- hostsdeny.remove_line_matching('^ALL:ALL EXCEPT 127\.0\.0\.1:DENY', 1)
- hostsdeny.replace_line_matching('^ALL:ALL:DENY', 'ALL:ALL:DENY', 1)
- elif arg == LOCAL:
- if arg != val:
- _interactive and log(_('Disabling non local services'))
- hostsdeny.remove_line_matching(ALL_REGEXP, 1)
- hostsdeny.replace_line_matching(ALL_LOCAL_REGEXP, 'ALL:ALL EXCEPT 127.0.0.1:DENY', 1)
- else:
- error(_('authorize_services invalid argument: %s') % arg)
-
-authorize_services.arg_trans = ALL_LOCAL_NONE_TRANS
-
-################################################################################
-
-def boolean2bit(bool):
- if bool:
- return 1
- else:
- return 0
-
-# helper function for enable_ip_spoofing_protection, accept_icmp_echo, accept_broadcasted_icmp_echo,
-# accept_bogus_error_responses and enable_log_strange_packets.
-def set_zero_one_variable(file, variable, value, secure_value, one_msg, zero_msg):
- 'D'
- f = ConfigFile.get_config_file(file)
-
- if f.exists():
- val = f.get_shell_variable(variable)
- if val:
- val = int(val)
- else:
- val = None
-
- # don't lower security when not changing security level
- if same_level():
- if val == secure_value:
- return
-
- if value != val:
- if value:
- msg = _(one_msg)
- else:
- msg = _(zero_msg)
-
- _interactive and log(msg)
- f.set_shell_variable(variable, boolean2bit(value))
-
-################################################################################
-
-# the alert argument is kept for backward compatibility
-def enable_ip_spoofing_protection(arg, alert=1):
- ''' Enable/Disable IP spoofing protection.'''
- set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.rp_filter', arg, 1, 'Enabling ip spoofing protection', 'Disabling ip spoofing protection')
-
-enable_ip_spoofing_protection.arg_trans = YES_NO_TRANS
-enable_ip_spoofing_protection.one_arg = 1
-
-################################################################################
-
-def enable_dns_spoofing_protection(arg, alert=1):
- ''' Enable/Disable name resolution spoofing protection. If
-\\fIalert\\fP is true, also reports to syslog.'''
- hostconf = ConfigFile.get_config_file(HOSTCONF)
-
- val = hostconf.exists() and hostconf.get_match('nospoof\s+on')
-
- # don't lower security when not changing security level
- if same_level():
- if val:
- return
-
- if arg:
- if not val:
- _interactive and log(_('Enabling name resolution spoofing protection'))
- hostconf.replace_line_matching('nospoof', 'nospoof on', 1)
- hostconf.replace_line_matching('spoofalert', 'spoofalert on', (alert != 0))
- else:
- if val:
- _interactive and log(_('Disabling name resolution spoofing protection'))
- hostconf.remove_line_matching('nospoof')
- hostconf.remove_line_matching('spoofalert')
-
-enable_dns_spoofing_protection.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def accept_icmp_echo(arg):
- ''' Accept/Refuse icmp echo.'''
- set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_all', not arg, 1, 'Ignoring icmp echo', 'Accepting icmp echo')
-
-accept_icmp_echo.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def accept_broadcasted_icmp_echo(arg):
- ''' Accept/Refuse broadcasted icmp echo.'''
- set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_broadcasts', not arg, 1, 'Ignoring broadcasted icmp echo', 'Accepting broadcasted icmp echo')
-
-accept_broadcasted_icmp_echo.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def accept_bogus_error_responses(arg):
- ''' Accept/Refuse bogus IPv4 error messages.'''
- set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_ignore_bogus_error_responses', not arg, 1, 'Ignoring bogus icmp error responses', 'Accepting bogus icmp error responses')
-
-accept_bogus_error_responses.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def enable_log_strange_packets(arg):
- ''' Enable/Disable the logging of IPv4 strange packets.'''
- set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.log_martians', arg, 1, 'Enabling logging of strange packets', 'Disabling logging of strange packets')
-
-enable_log_strange_packets.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def enable_libsafe(arg):
- ''' Enable/Disable libsafe if libsafe is found on the system.'''
-
- ldsopreload = ConfigFile.get_config_file(LDSOPRELOAD)
-
- val = ldsopreload.exists() and ldsopreload.get_match('/lib/libsafe.so.2')
-
- # don't lower security when not changing security level
- if same_level():
- if val:
- return
-
- if arg:
- if not val:
- if os.path.exists(Config.get_config('root', '') + '/lib/libsafe.so.2'):
- _interactive and log(_('Enabling libsafe'))
- ldsopreload.replace_line_matching('[^#]*libsafe', '/lib/libsafe.so.2', 1)
- else:
- if val:
- _interactive and log(_('Disabling libsafe'))
- ldsopreload.remove_line_matching('[^#]*libsafe')
-
-enable_libsafe.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-LENGTH_REGEXP = re.compile('^(password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*?)\sminlen=([0-9]+)\s(.*)')
-NDIGITS_REGEXP = re.compile('^(password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*?)\sdcredit=([0-9]+)\s(.*)')
-UCREDIT_REGEXP = re.compile('^(password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*?)\sucredit=([0-9]+)\s(.*)')
-
-def password_length(length, ndigits=0, nupper=0):
- ''' Set the password minimum length and minimum number of digit and minimum number of capitalized letters.'''
-
- passwd = ConfigFile.get_config_file(SYSTEM_AUTH)
-
- val_length = val_ndigits = val_ucredit = 999999
-
- if passwd.exists():
- val_length = passwd.get_match(LENGTH_REGEXP, '@2')
- if val_length:
- val_length = int(val_length)
-
- val_ndigits = passwd.get_match(NDIGITS_REGEXP, '@2')
- if val_ndigits:
- val_ndigits = int(val_ndigits)
-
- val_ucredit = passwd.get_match(UCREDIT_REGEXP, '@2')
- if val_ucredit:
- val_ucredit = int(val_ucredit)
-
- # don't lower security when not changing security level
- if same_level():
- if val_length > length and val_ndigits > ndigits and val_ucredit > nupper:
- return
-
- if val_length > length:
- length = val_length
-
- if val_ndigits > ndigits:
- ndigits = val_ndigits
-
- if val_ucredit > nupper:
- nupper = val_ucredit
-
- if passwd.exists() and (val_length != length or val_ndigits != ndigits or val_ucredit != nupper):
- _interactive and log(_('Setting minimum password length %d') % length)
- (passwd.replace_line_matching(LENGTH_REGEXP,
- '@1 minlen=%s @3' % length) or \
- passwd.replace_line_matching('^password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*',
- '@0 minlen=%s ' % length))
-
- (passwd.replace_line_matching(NDIGITS_REGEXP,
- '@1 dcredit=%s @3' % ndigits) or \
- passwd.replace_line_matching('^password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*',
- '@0 dcredit=%s ' % ndigits))
-
- (passwd.replace_line_matching(UCREDIT_REGEXP,
- '@1 ucredit=%s @3' % nupper) or \
- passwd.replace_line_matching('^password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*',
- '@0 ucredit=%s ' % nupper))
-
-################################################################################
-
-PASSWORD_REGEXP = '^\s*auth\s+sufficient\s+(?:/lib/security/)?pam_permit.so'
-def enable_password(arg):
- ''' Use password to authenticate users.'''
- system_auth = ConfigFile.get_config_file(SYSTEM_AUTH)
-
- val = system_auth.exists() and system_auth.get_match(PASSWORD_REGEXP)
-
- # don't lower security when not changing security level
- if same_level():
- if not val:
- return
-
- if arg:
- if val:
- _interactive and log(_('Using password to authenticate users'))
- system_auth.remove_line_matching(PASSWORD_REGEXP)
- else:
- if not val:
- _interactive and log(_('Don\'t use password to authenticate users'))
- system_auth.replace_line_matching(PASSWORD_REGEXP, 'auth sufficient pam_permit.so') or \
- system_auth.insert_before('auth\s+sufficient', 'auth sufficient pam_permit.so')
-
-enable_password.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-UNIX_REGEXP = re.compile('(^\s*password\s+sufficient\s+(?:/lib/security/)?pam_unix.so.*)\sremember=([0-9]+)(.*)')
-
-def password_history(arg):
- ''' Set the password history length to prevent password reuse.'''
- system_auth = ConfigFile.get_config_file(SYSTEM_AUTH)
-
- if system_auth.exists():
- val = system_auth.get_match(UNIX_REGEXP, '@2')
-
- if val and val != '':
- val = int(val)
- else:
- val = 0
- else:
- val = 0
-
- # don't lower security when not changing security level
- if same_level():
- if val >= arg:
- return
-
- if arg != val:
- if arg > 0:
- _interactive and log(_('Setting password history to %d.') % arg)
- system_auth.replace_line_matching(UNIX_REGEXP, '@1 remember=%d@3' % arg) or \
- system_auth.replace_line_matching('(^\s*password\s+sufficient\s+(?:/lib/security/)?pam_unix.so.*)', '@1 remember=%d' % arg)
- opasswd = ConfigFile.get_config_file(OPASSWD)
- opasswd.exists() or opasswd.touch()
- else:
- _interactive and log(_('Disabling password history'))
- system_auth.replace_line_matching(UNIX_REGEXP, '@1@3')
-
-################################################################################
-
-SULOGIN_REGEXP = '~~:S:wait:/sbin/sulogin'
-def enable_sulogin(arg):
- ''' Enable/Disable sulogin(8) in single user level.'''
- inittab = ConfigFile.get_config_file(INITTAB)
-
- val = inittab.exists() and inittab.get_match(SULOGIN_REGEXP)
-
- # don't lower security when not changing security level
- if same_level():
- if val:
- return
-
- if arg:
- if not val:
- _interactive and log(_('Enabling sulogin in single user runlevel'))
- inittab.replace_line_matching('[^#]+:S:', '~~:S:wait:/sbin/sulogin', 1)
- else:
- if val:
- _interactive and log(_('Disabling sulogin in single user runlevel'))
- inittab.remove_line_matching('~~:S:wait:/sbin/sulogin')
-
-enable_sulogin.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def enable_msec_cron(arg):
- ''' Enable/Disable msec hourly security check.'''
- mseccron = ConfigFile.get_config_file(MSECCRON)
-
- val = mseccron.exists()
-
- # don't lower security when not changing security level
- if same_level():
- if val:
- return
-
- if arg:
- if arg != val:
- _interactive and log(_('Enabling msec periodic runs'))
- mseccron.symlink(MSECBIN)
- else:
- if arg != val:
- _interactive and log(_('Disabling msec periodic runs'))
- mseccron.unlink()
-
-enable_msec_cron.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-def enable_at_crontab(arg):
- ''' Enable/Disable crontab and at for users. Put allowed users in /etc/cron.allow and /etc/at.allow
-(see man at(1) and crontab(1)).'''
- cronallow = ConfigFile.get_config_file(CRONALLOW)
- atallow = ConfigFile.get_config_file(ATALLOW)
-
- val_cronallow = cronallow.exists() and cronallow.get_match('root')
- val_atallow = atallow.exists() and atallow.get_match('root')
-
- # don't lower security when not changing security level
- if same_level():
- if val_cronallow and val_atallow:
- return
-
- if arg:
- if val_cronallow or val_atallow:
- _interactive and log(_('Enabling crontab and at'))
- if not (same_level() and val_cronallow):
- cronallow.exists() and cronallow.move(SUFFIX)
- if not (same_level() and val_atallow):
- atallow.exists() and atallow.move(SUFFIX)
- else:
- if not val_cronallow or not val_atallow:
- _interactive and log(_('Disabling crontab and at'))
- cronallow.replace_line_matching('root', 'root', 1)
- atallow.replace_line_matching('root', 'root', 1)
-
-enable_at_crontab.arg_trans = YES_NO_TRANS
-
-################################################################################
-
-maximum_regex = re.compile('^Maximum.*:\s*([0-9]+|-1)', re.MULTILINE)
-inactive_regex = re.compile('^(Inactive|Password inactive\s*):\s*(-?[0-9]+|never)', re.MULTILINE)
-no_aging_list = []
-
-def no_password_aging_for(name):
- '''D Add the name as an exception to the handling of password aging by msec.
-Name must be put between '. Msec will then no more manage password aging for
-name so you have to use chage(1) to manage it by hand.'''
- no_aging_list.append(name)
-
-def password_aging(max, inactive=-1):
- ''' Set password aging to \\fImax\\fP days and delay to change to \\fIinactive\\fP.'''
- uid_min = 500
- _interactive and log(_('Setting password maximum aging for new user to %d') % max)
- logindefs = ConfigFile.get_config_file(LOGINDEFS)
- if logindefs.exists():
- logindefs.replace_line_matching('^\s*PASS_MAX_DAYS', 'PASS_MAX_DAYS ' + str(max), 1)
- uid_min = logindefs.get_match('^\s*UID_MIN\s+([0-9]+)', '@1')
- if uid_min:
- uid_min = int(uid_min)
- shadow = ConfigFile.get_config_file(SHADOW)
- if shadow.exists():
- _interactive and log(_('Setting password maximum aging for root and users with id greater than %d to %d and delay to %d days') % (uid_min, max, inactive))
- for line in shadow.get_lines():
- field = string.split(line, ':')
- if len(field) < 2:
- continue
- name = field[0]
- password = field[1]
- if name in no_aging_list:
- _interactive and log(_('User %s in password aging exception list') % (name,))
- continue
- try:
- entry = pwd.getpwnam(name)
- except KeyError:
- error(_('User %s in shadow but not in passwd file') % name)
- continue
- if (len(password) > 0 and password[0] != '!') and password != '*' and password != 'x' and (entry[2] >= uid_min or entry[2] == 0):
- if field[4] == '':
- current_max = 99999
- else:
- current_max = int(field[4])
- if field[6] == '':
- current_inactive = -1
- else:
- current_inactive = int(field[6])
- new_max = max
- new_inactive = inactive
- # don't lower security when not changing security level
- if same_level():
- if current_max < max and current_inactive < inactive:
- continue
- if current_max < max:
- new_max = current_max
- if current_inactive < inactive:
- new_inactive = current_inactive
- if new_max != current_max or current_inactive != new_inactive:
- cmd = 'LC_ALL=C /usr/bin/chage -M %d -I %d -d %s \'%s\'' % (new_max, new_inactive, time.strftime('%Y-%m-%d'), entry[0])
- ret = commands.getstatusoutput(cmd)
- log(_('changed maximum password aging for user \'%s\' with command %s') % (entry[0], cmd))
-
-################################################################################
-
-def allow_xauth_from_root(arg):
- ''' Allow/forbid to export display when passing from the root account
-to the other users. See pam_xauth(8) for more details.'''
- export = ConfigFile.get_config_file(EXPORT)
-
- allow = export.exists() and export.get_match('^\*$')
-
- # don't lower security when not changing security level
- if same_level():
- if not allow:
- return
-
- if arg:
- if not allow:
- _interactive and log(_('Allowing export display from root'))
- export.insert_at(0, '*')
- else:
- if allow:
- _interactive and log(_('Forbidding export display from root'))
- export.remove_line_matching('^\*$')
-
-################################################################################
-
-def set_security_conf(var, value):
- '''1 Set the variable \\fIvar\\fP to the value \\fIvalue\\fP in /var/lib/msec/security.conf.
-The best way to override the default setting is to create /etc/security/msec/security.conf
-with the value you want. These settings are used to configure the daily check run each night.
-
-The following variables are currentrly recognized by msec:
-
-CHECK_UNOWNED if set to yes, report unowned files.
-
-CHECK_SHADOW if set to yes, check empty password in /etc/shadow.
-
-CHECK_SUID_MD5 if set to yes, verify checksum of the suid/sgid files.
-
-CHECK_SECURITY if set to yes, run the daily security checks.
-
-CHECK_PASSWD if set to yes, check for empty passwords, for no password in /etc/shadow and for users with the 0 id other than root.
-
-SYSLOG_WARN if set to yes, report check result to syslog.
-
-CHECK_SUID_ROOT if set to yes, check additions/removals of suid root files.
-
-CHECK_PERMS if set to yes, check permissions of files in the users' home.
-
-CHKROOTKIT_CHECK if set to yes, run chkrootkit checks.
-
-CHECK_PROMISC if set to yes, check if the network devices are in promiscuous mode.
-
-RPM_CHECK if set to yes, run some checks against the rpm database.
-
-TTY_WARN if set to yes, reports check result to tty.
-
-CHECK_WRITABLE if set to yes, check files/directories writable by everybody.
-
-MAIL_WARN if set to yes, report check result by mail.
-
-MAIL_USER if set, send the mail report to this email address else send it to root.
-
-CHECK_OPEN_PORT if set to yes, check open ports.
-
-CHECK_SGID if set to yes, check additions/removals of sgid files.
-'''
- securityconf = ConfigFile.get_config_file(SECURITYCONF)
- securityconf.set_shell_variable(var, value)
-
-# various
-
-def set_interactive(v):
- "D"
-
- global _interactive
-
- _interactive = v
-
-# libmsec.py ends here
-
diff --git a/share/man.py b/share/man.py
deleted file mode 100755
index 7859ed8..0000000
--- a/share/man.py
+++ /dev/null
@@ -1,67 +0,0 @@
-#!/usr/bin/python
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : share
-# File : man.py
-# Version : $Id$
-# Author : Frederic Lepied
-# Created On : Sat Jan 26 17:38:39 2002
-# Purpose : loads a python module and creates a man page from
-# the doc strings of the functions.
-#---------------------------------------------------------------
-
-import sys
-import imp
-import inspect
-
-header = '''.ds q \N'34'
-.TH mseclib 3 V0 msec "Mandriva Linux"
-.SH NAME
-mseclib
-.SH SYNOPSIS
-.nf
-.B from mseclib import *
-.B function1(yes)
-.B function2(ignore)
-.fi
-.SH DESCRIPTION
-.B mseclib
-is a python library to access the function used by the msec program. This functions can be used
-in /etc/security/msec/level.local to override the behaviour of the msec program or in standalone
-scripts. The first argument of the functions takes a value of 1 or 0 or -1 (or yes/no/ignore)
-except when specified otherwise.
-'''
-
-footer = '''.RE
-.SH "SEE ALSO"
-msec(8)
-.SH AUTHORS
-Frederic Lepied <flepied@mandriva.com>
-'''
-
-### strings used in the rewritting
-function_str = '''
-.TP 4
-.B \\fI%s%s\\fP
-%s
-'''
-
-### code
-modulename = sys.argv[1]
-
-module = __import__(modulename)
-
-sys.stdout.write(header)
-
-for f in inspect.getmembers(module, inspect.isfunction):
- (args, varargs, varkw, locals) = inspect.getargspec(f[1])
- doc = f[1].__doc__
- if doc and len(doc) > 2:
- doc = doc[2:]
- argspec = inspect.formatargspec(args, varargs, varkw, locals)
- s = function_str % (f[0], argspec, doc)
- sys.stdout.write(s)
-
-sys.stdout.write(footer)
-
-# man.py ends here
diff --git a/share/msec b/share/msec
deleted file mode 100755
index f19cd9b..0000000
--- a/share/msec
+++ /dev/null
@@ -1,85 +0,0 @@
-#!/bin/sh
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : share
-# File : msec
-# Version : $Id$
-# Author : Frederic Lepied
-# Created On : Thu Dec 13 11:36:50 2001
-# Purpose : entry script to run hardness script or change
-# the security level.
-#---------------------------------------------------------------
-
-if [ "`whoami`" != "root" ]; then
- echo 'msec: sorry, you must be root !'
- exit 1
-fi
-
-LCK=/var/run/msec.pid
-
-function cleanup() {
- rm -f $LCK
-}
-
-if [ -f $LCK ]; then
- if [ -d /proc/`cat $LCK` ]; then
- exit 0
- else
- rm -f $LCK
- fi
-fi
-
-echo -n $$ > $LCK
-
-trap cleanup 0
-
-MSEC=/usr/share/msec/msec.py
-OPT=""
-
-for a in "$@"; do
- if [ "$a" = '-o' ]; then
- OPT="$OPT -o"
- NEXT=1
- else
- if [ "$NEXT" = 1 ]; then
- OPT="$OPT $a"
- else
- last="$a"
- fi
- NEXT=0
- fi
-done
-
-if [ -n "$last" ]; then
- CHANGE=-c
- 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 "$MSEC 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 $CHANGE $OPT /usr/share/msec/perm.$PERM_LEVEL $LOCAL
-fi
-
-# msec ends here
diff --git a/share/msec.py b/share/msec.py
deleted file mode 100755
index 553890c..0000000
--- a/share/msec.py
+++ /dev/null
@@ -1,290 +0,0 @@
-#!/usr/bin/python -O
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : msec/share
-# File : msec.py
-# Version : $Id$
-# Author : Frederic Lepied
-# Created On : Wed Dec 5 20:20:21 2001
-#---------------------------------------------------------------
-
-from mseclib import *
-from Log import *
-from Log import _name
-import Config
-import ConfigFile
-
-import sys
-import os
-import string
-import getopt
-import gettext
-import imp
-
-try:
- cat = gettext.Catalog('msec')
- _ = cat.gettext
-except IOError:
- _ = str
-
-# Eval a file
-import mseclib
-def import_only_mseclib(name, globals = None, locals = None, fromlist = None):
- """ Import hook to allow only the mseclib module to be imported. """
-
- if name == 'mseclib':
- return mseclib
- else:
- raise ImportError, '%s cannot be imported' % name
-
-def eval_file(name):
- """ Eval level.local file. Only allow mseclib to be imported for
- backward compatibility. """
-
- globals = {}
- locals = {}
- builtins = {}
-
- # Insert symbols from mseclib into globals
- non_exported_names = ['FAKE', 'indirect', 'commit_changes', 'print_changes', 'get_translation']
- for attrib_name in dir(mseclib):
- if attrib_name[0] != '_' and attrib_name not in non_exported_names:
- globals[attrib_name] = getattr(mseclib, attrib_name)
-
- # Set import hook -- it needs to be in globals['__builtins'] so we make
- # a copy of builtins to put there
- builtins.update(__builtins__.__dict__)
- builtins['__import__'] = import_only_mseclib
- globals['__builtins__'] = builtins
-
- # Exec file
- execfile(os.path.expanduser(name), globals, locals)
-
-# 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])') % _name)
- 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.') % _name)
- sys.exit(1)
-else:
- level = args[0]
- changing_level()
-
-try:
- level = int(level)
-except ValueError:
- error(_('Invalid secure level %s. Use %s [0-5] to set it.') % (level, _name))
- sys.exit(1)
-
-if level < 0 or level > 5:
- error(_('Invalid secure level %s. Use %s [0-5] to set it.') % (level, _name))
- sys.exit(1)
-
-interactive and log(_('### Program is starting ###'))
-
-set_secure_level(level)
-
-server=(level in range(3, 6))
-
-# process options
-server_level = Config.get_config('server_level')
-if server_level:
- set_server_level(server_level)
-
-create_server_link()
-
-# 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:
- plength = (level - 1) * 2
-else:
- plength = 0
-
-password_length(plength, level / 4, level / 4)
-
-enable_ip_spoofing_protection(server)
-enable_dns_spoofing_protection(server)
-
-# differences between level 5 and others
-if level == 5:
- set_root_umask('077')
- set_shell_timeout(900)
- authorize_services(NONE)
- enable_pam_wheel_for_su(yes)
- password_history(5)
-else:
- set_root_umask('022')
- if level == 4:
- set_shell_timeout(3600)
- authorize_services(LOCAL)
- else:
- set_shell_timeout(0)
- authorize_services(ALL)
- enable_pam_wheel_for_su(no)
- password_history(0)
-
-# differences between level 4,5 and others
-if level >= 4:
- set_user_umask('077')
- set_shell_history_size(10)
- allow_root_login(no)
- enable_sulogin(yes)
- allow_user_list(no)
- enable_promisc_check(yes)
- accept_icmp_echo(no)
- accept_broadcasted_icmp_echo(no)
- accept_bogus_error_responses(no)
- allow_reboot(no)
- enable_at_crontab(no)
- if level == 4:
- password_aging(60, 30)
- else:
- password_aging(30, 15)
- allow_xauth_from_root(no)
- set_win_parts_umask(None)
-else:
- set_user_umask('022')
- set_shell_history_size(-1)
- allow_root_login(yes)
- enable_sulogin(no)
- allow_user_list(yes)
- enable_promisc_check(no)
- accept_icmp_echo(yes)
- accept_broadcasted_icmp_echo(yes)
- accept_bogus_error_responses(yes)
- allow_reboot(yes)
- enable_at_crontab(yes)
- password_aging(99999)
- allow_xauth_from_root(yes)
-
-# special exception for ssh; if level == 3, set
-# PermitRootLogin to without_password, otherwise set to no
-# see https://qa.mandriva.com/show_bug.cgi?id=19726
-if level >= 3:
- if level == 3:
- allow_remote_root_login(without_password)
- else:
- allow_remote_root_login(no)
-else:
- allow_remote_root_login(yes)
-
-# differences between level 3,4,5 and others
-if server:
- allow_autologin(no)
- enable_console_log(yes)
- if level == 5:
- allow_issues(NONE)
- else:
- allow_issues(LOCAL)
- enable_log_strange_packets(yes)
- enable_pam_root_from_wheel(no)
-else:
- allow_autologin(yes)
- enable_console_log(no)
- allow_issues(ALL)
- enable_log_strange_packets(no)
- enable_pam_root_from_wheel(yes)
- set_win_parts_umask('0')
-
-# differences between level 0 and others
-if level != 0:
- enable_security_check(yes)
- enable_password(yes)
- if level < 3:
- allow_x_connections(LOCAL)
- allow_xserver_to_listen(yes)
- else:
- if level == 3:
- allow_x_connections(NONE)
- allow_xserver_to_listen(yes)
- else:
- allow_x_connections(NONE)
- allow_xserver_to_listen(no)
-else:
- enable_security_check(no)
- enable_password(no)
- allow_x_connections(ALL, 1)
- allow_xserver_to_listen(yes)
-
-# msec cron
-enable_msec_cron(1)
-
-# 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_SGID' : ('no', 'no', 'yes', 'yes', 'yes', 'yes', ),
- 'CHECK_WRITABLE' : ('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', ),
- 'MAIL_EMPTY_CONTENT':('no', 'no', 'no', 'no', 'yes', 'yes', ),
- 'SYSLOG_WARN' : ('no', 'no', 'yes', 'yes', 'yes', 'yes', ),
- 'RPM_CHECK' : ('no', 'no', 'no', 'yes', 'yes', 'yes', ),
- 'CHKROOTKIT_CHECK' : ('no', 'no', 'no', 'yes', 'yes', 'yes', ),
- }
-
-for k in FILE_CHECKS.keys():
- set_security_conf(k, FILE_CHECKS[k][level])
-
-if Config.get_config('nolocal', '0') == '0':
- # load local customizations
- CONFIG='/etc/security/msec/level.local'
- if os.path.exists(CONFIG):
- interactive and log(_('Reading local rules from %s') % CONFIG)
- local_config(1)
- try:
- eval_file(CONFIG)
- except:
- log(_('Error loading %s: %s') % (CONFIG, str(sys.exc_value)))
- local_config(0)
-
-if Config.get_config('print', '0') == '1':
- print_changes()
-else:
- commit_changes()
-
-interactive and log(_('Writing config files and then taking needed actions'))
-ConfigFile.write_files()
-
-closelog()
-
-# msec.py ends here
diff --git a/share/shadow.py b/share/shadow.py
deleted file mode 100755
index e49ebb4..0000000
--- a/share/shadow.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/python
-#---------------------------------------------------------------
-# Project : Mandriva Linux
-# Module : msec/share
-# File : shadow.py
-# Version : $Id$
-# Author : Frederic Lepied
-# Created On : Sat Jan 26 17:38:39 2002
-# Purpose : loads a python module and creates another one
-# on stdout. All the functions of the module are shadowed according
-# to their doc string: "D" direct mapping, "1" indirect call but
-# name + first arg used as the key and all other cases indirect
-# call with the name as the key.
-#---------------------------------------------------------------
-
-import sys
-import imp
-import inspect
-
-### strings used in the rewritting
-direct_str = """
-%s=%s.%s
-
-"""
-
-indirect_str = """
-def %s(*args):
- indirect(\"%s\", %s.%s, %d, args)
-
-"""
-
-header = """
-
-NONE=0
-ALL=1
-LOCAL=2
-
-yes=1
-no=0
-without_password=2
-ignore=-1
-
-FAKE = {}
-
-_force = 0
-
-def local_config(val):
- global _force
- _force = val
-
-def indirect(name, func, type, args):
- if type == 1:
- key = (name, args[0])
- else:
- key = name
- FAKE[key] = (func, args)
- if _force:
- force_val(name)
-
-def commit_changes():
- for f in FAKE.values():
- if len(f[1]) >= 1 and (f[1][0] != -1 or f[0].__name__ == 'set_shell_history_size'):
- f[0](*f[1])
- elif len(f[1]) == 0:
- f[0]()
-
-def print_changes():
- import sys
- for f in FAKE.values():
- l = len(f[1])
- if l >= 1 and (f[1][0] != -1 or f[0].__name__ == 'set_shell_history_size'):
- name = f[0].__name__
- try:
- if f[0].one_arg:
- l = 1
- except AttributeError:
- pass
- if l == 1:
- print name, get_translation(f[0], f[1][0])
- else:
- sys.stdout.write(name)
- for a in f[1]:
- sys.stdout.write(' ' + str(a))
- sys.stdout.write('\\n')
-
-def get_translation(func, value):
- try:
- return func.arg_trans[value]
- except (KeyError, AttributeError):
- return value
-
-"""
-
-### code
-modulename = sys.argv[1]
-
-module = __import__(modulename)
-
-sys.stdout.write(header)
-
-sys.stdout.write("import %s\n\n" % modulename)
-
-for f in inspect.getmembers(module, inspect.isfunction):
- (args, varargs, varkw, locals) = inspect.getargspec(f[1])
- if f[1].__doc__ and f[1].__doc__[0] == 'D':
- #argspec = inspect.formatargspec(args, varargs, varkw, locals)
- s = direct_str % (f[0], modulename, f[0])
- else:
- if f[1].__doc__ and f[1].__doc__[0] == '1':
- type = 1
- else:
- type = 0
- s = indirect_str % (f[0], f[0], modulename, f[0], type)
- sys.stdout.write(s)
-
-# shadow.py ends here
diff --git a/share/.svnignore b/src/msec/.svnignore
index 17005aa..17005aa 100644
--- a/share/.svnignore
+++ b/src/msec/.svnignore
diff --git a/share/CHANGES b/src/msec/CHANGES
index 22e546a..22e546a 100644
--- a/share/CHANGES
+++ b/src/msec/CHANGES
diff --git a/src/msec/Makefile b/src/msec/Makefile
new file mode 100644
index 0000000..8ff37b0
--- /dev/null
+++ b/src/msec/Makefile
@@ -0,0 +1,34 @@
+#---------------------------------------------------------------
+# Project : Mandriva Linux
+# Module : share
+# File : Makefile
+# Version : $Id$
+# Author : Frederic Lepied
+# Created On : Sat Jan 26 20:17:55 2002
+#---------------------------------------------------------------
+
+MAN=../../man/C/msec.8
+PFILES=msecperms.py compile.py libmsec.py man.py
+
+all: compile man help
+
+compile:
+ ./compile.py '/usr/share/msec/' *.py
+
+man: $(MAN)
+
+help: libmsec.py help_draksec.py
+ ./help_draksec.py
+
+$(MAN): libmsec.py man.py
+ rm -f $@
+ ./man.py libmsec > $@
+
+clean:
+ rm -f *.pyc *.pyo mseclib.py *~ help.*
+
+# Local variables:
+# mode: makefile
+# End:
+#
+# Makefile ends here
diff --git a/share/README b/src/msec/README
index 4bb3846..4bb3846 100644
--- a/share/README
+++ b/src/msec/README
diff --git a/share/compile.py b/src/msec/compile.py
index a325016..c42a0c2 100755
--- a/share/compile.py
+++ b/src/msec/compile.py
@@ -1,11 +1,11 @@
#!/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.
+# 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
diff --git a/src/msec/config.py b/src/msec/config.py
new file mode 100644
index 0000000..ec55de1
--- /dev/null
+++ b/src/msec/config.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python -O
+"""This is the configuration file for msec.
+The following variables are defined here:
+ SECURITY_LEVELS: list of supported security levels
+ SECURITYCONF: location of msec configuration file
+ SECURITYLOG: log file for msec messages
+ SETTINGS: all security settings, with correspondent options for each
+ level, callback functions, and regexp of valid parameters.
+
+A helper function load_defaults parses the SETTINGS variable.
+
+The MsecConfig class processes the main msec configuration file.
+"""
+
+import gettext
+import sys
+import traceback
+import re
+
+# security levels
+SECURITY_LEVELS = [ "none", "default", "secure" ]
+DEFAULT_LEVEL="default"
+SECURITY_LEVEL="/etc/security/msec/level.%s"
+
+# msec configuration file
+SECURITYCONF = '/etc/security/msec/security.conf'
+
+# permissions
+PERMCONF = '/etc/security/msec/perms.conf'
+PERMISSIONS_LEVEL = '/etc/security/msec/perm.%s' # for level
+
+# logging
+SECURITYLOG = '/var/log/msec.log'
+
+# localization
+try:
+ cat = gettext.Catalog('msec')
+ _ = cat.gettext
+except IOError:
+ _ = str
+
+# shared strings
+MODIFICATIONS_FOUND = _('Modified system files')
+MODIFICATIONS_NOT_FOUND = _('No changes in system files')
+
+# msec callbacks and valid values
+# OPTION callback valid values
+SETTINGS = {'CHECK_SECURITY' : ("check_security", ['yes', 'no']),
+ 'CHECK_PERMS' : ("check_perms", ['yes', 'no']),
+ 'CHECK_USER_FILES' : ("check_user_files", ['yes', 'no']),
+ 'CHECK_SUID_ROOT' : ("check_suid_root", ['yes', 'no']),
+ 'CHECK_SUID_MD5' : ("check_suid_md5", ['yes', 'no']),
+ 'CHECK_SGID' : ("check_sgid", ['yes', 'no']),
+ 'CHECK_WRITABLE' : ("check_writable", ['yes', 'no']),
+ 'CHECK_UNOWNED' : ("check_unowned", ['yes', 'no']),
+ 'CHECK_PROMISC' : ("check_promisc", ['yes', 'no']),
+ 'CHECK_OPEN_PORT' : ("check_open_port", ['yes', 'no']),
+ 'CHECK_PASSWD' : ("check_passwd", ['yes', 'no']),
+ 'CHECK_SHADOW' : ("check_shadow", ['yes', 'no']),
+ 'CHECK_CHKROOTKIT' : ("check_chkrootkit", ['yes', 'no']),
+ 'CHECK_RPM' : ("check_rpm", ['yes', 'no']),
+ 'CHECK_SHOSTS' : ("check_shosts", ['yes', 'no']),
+ # notifications
+ 'TTY_WARN' : ("tty_warn", ['yes', 'no']),
+ 'MAIL_WARN' : ("mail_warn", ['yes', 'no']),
+ 'MAIL_USER' : ("mail_user", ['*']),
+ 'MAIL_EMPTY_CONTENT': ("mail_empty_content", ['yes', 'no']),
+ 'SYSLOG_WARN' : ("syslog_warn", ['yes', 'no']),
+ 'NOTIFY_WARN' : ("notify_warn", ['yes', 'no']),
+ # security options
+ 'USER_UMASK': ("set_user_umask", ['*']),
+ 'ROOT_UMASK': ("set_root_umask", ['*']),
+ 'WIN_PARTS_UMASK': ("set_win_parts_umask", ['no', '*']),
+ 'ACCEPT_BOGUS_ERROR_RESPONSES': ("accept_bogus_error_responses", ['yes', 'no']),
+ 'ACCEPT_BROADCASTED_ICMP_ECHO': ("accept_broadcasted_icmp_echo", ['yes', 'no']),
+ 'ACCEPT_ICMP_ECHO': ("accept_icmp_echo", ['yes', 'no']),
+ 'ALLOW_AUTOLOGIN': ("allow_autologin", ['yes', 'no']),
+ 'ALLOW_REBOOT': ("allow_reboot", ['yes', 'no']),
+ 'ALLOW_REMOTE_ROOT_LOGIN': ("allow_remote_root_login", ['yes', 'no', 'without_password']),
+ 'ALLOW_ROOT_LOGIN': ("allow_root_login", ['yes', 'no']),
+ 'ALLOW_USER_LIST': ("allow_user_list", ['yes', 'no']),
+ 'ALLOW_X_CONNECTIONS': ("allow_x_connections", ['yes', 'no', 'local']),
+ 'ALLOW_XAUTH_FROM_ROOT': ("allow_xauth_from_root", ['yes', 'no']),
+ 'ALLOW_XSERVER_TO_LISTEN': ("allow_xserver_to_listen", ['yes', 'no']),
+ 'AUTHORIZE_SERVICES': ("authorize_services", ['yes', 'no', 'local']),
+ 'CREATE_SERVER_LINK': ("create_server_link", ['no', 'default', 'secure']),
+ 'ENABLE_AT_CRONTAB': ("enable_at_crontab", ['yes', 'no']),
+ 'ENABLE_CONSOLE_LOG': ("enable_console_log", ['yes', 'no']),
+ 'ENABLE_DNS_SPOOFING_PROTECTION':("enable_ip_spoofing_protection", ['yes', 'no']),
+ 'ENABLE_IP_SPOOFING_PROTECTION': ("enable_dns_spoofing_protection", ['yes', 'no']),
+ 'ENABLE_LOG_STRANGE_PACKETS': ("enable_log_strange_packets", ['yes', 'no']),
+ 'ENABLE_MSEC_CRON': ("enable_msec_cron", ['yes', 'no']),
+ 'ENABLE_PAM_ROOT_FROM_WHEEL': ("enable_pam_root_from_wheel", ['yes', 'no']),
+ 'ENABLE_SUDO': ("enable_sudo", ['yes', 'no', 'wheel']),
+ 'ENABLE_PAM_WHEEL_FOR_SU': ("enable_pam_wheel_for_su", ['yes', 'no']),
+ 'ENABLE_SULOGIN': ("enable_sulogin", ['yes', 'no']),
+ 'ENABLE_APPARMOR': ("enable_apparmor", ['yes', 'no']),
+ 'ENABLE_POLICYKIT': ("enable_policykit", ['yes', 'no']),
+ # password stuff
+ 'ENABLE_PASSWORD': ("enable_password", ['yes', 'no']),
+ 'PASSWORD_HISTORY': ("password_history", ['*']),
+ # format: min length, num upper, num digits
+ 'PASSWORD_LENGTH': ("password_length", ['*']),
+ 'SHELL_HISTORY_SIZE': ("set_shell_history_size", ['*']),
+ 'SHELL_TIMEOUT': ("set_shell_timeout", ['*']),
+ }
+
+def find_callback(param):
+ '''Finds a callback for security option'''
+ if param not in SETTINGS:
+ return None
+ else:
+ callback, valid_params = SETTINGS[param]
+ return callback
+
+def find_valid_params(param):
+ '''Finds valid parameters for security option'''
+ if param not in SETTINGS:
+ return None
+ else:
+ callback, valid_params = SETTINGS[param]
+ return valid_params
+
+# helper functions
+def load_defaults(log, level):
+ """Loads default configuration for given security level, returning a
+ MsecConfig instance.
+ """
+ config = MsecConfig(log, config=SECURITY_LEVEL % level)
+ config.load()
+ return config
+
+def load_default_perms(log, level):
+ """Loads default permissions for given security level, returning a
+ MsecConfig instance.
+ """
+ config = PermConfig(log, config=PERMISSIONS_LEVEL % level)
+ config.load()
+ return config
+
+# {{{ MsecConfig
+class MsecConfig:
+ """Msec configuration parser"""
+ def __init__(self, log, config=SECURITYCONF):
+ self.config = config
+ self.options = {}
+ self.comments = []
+ self.log = log
+
+ def load(self):
+ """Loads and parses configuration file"""
+ try:
+ fd = open(self.config)
+ except:
+ self.log.error(_("Unable to load configuration file %s: %s") % (self.config, sys.exc_value[1]))
+ return False
+ for line in fd.readlines():
+ line = line.strip()
+ if line[0] == "#":
+ # comment
+ self.comments.append(line)
+ continue
+ try:
+ option, val = line.split("=", 1)
+ self.options[option] = val
+ except:
+ self.log.warn(_("Bad config option: %s") % line)
+ continue
+ fd.close()
+ return True
+
+ def get(self, option, default=None):
+ """Gets a configuration option, or defines it if not defined"""
+ if option not in self.options:
+ self.options[option] = default
+ return self.options[option]
+
+ def remove(self, option):
+ """Removes a configuration option."""
+ if option in self.options:
+ del self.options[option]
+
+ def set(self, option, value):
+ """Sets a configuration option"""
+ self.options[option] = value
+
+ def list_options(self):
+ """Sorts and returns configuration parameters"""
+ sortedparams = self.options.keys()
+ if sortedparams:
+ sortedparams.sort()
+ return sortedparams
+
+ def save(self):
+ """Saves configuration. Comments go on top"""
+ try:
+ fd = open(self.config, "w")
+ except:
+ self.log.error(_("Unable to save %s: %s") % (self.config, sys.exc_value))
+ return False
+ for comment in self.comments:
+ print >>fd, comment
+ # sorting keys
+ for option in self.list_options():
+ print >>fd, "%s=%s" % (option, self.options[option])
+ return True
+# }}}
+
+# {{{ PermConfig
+class PermConfig(MsecConfig):
+ """Msec file permission parser"""
+ def __init__(self, log, config=PERMCONF):
+ self.config = config
+ self.options = {}
+ self.options_order = []
+ self.comments = []
+ self.log = log
+ self.regexp = re.compile("^([^\s]*)\s*([a-z]*)\.([a-z]*)\s*([\d]?\d\d\d)\s*(force)?$")
+
+ def load(self):
+ """Loads and parses configuration file"""
+ try:
+ fd = open(self.config)
+ except:
+ self.log.error(_("Unable to load configuration file %s: %s") % (self.config, sys.exc_value))
+ return False
+ for line in fd.readlines():
+ line = line.strip()
+ if line[0] == "#":
+ # comment
+ self.comments.append(line)
+ continue
+ try:
+ res = self.regexp.findall(line)
+ if res:
+ if len(res[0]) == 5:
+ file, user, group, perm, force = res[0]
+ else:
+ force = None
+ file, user, group, perm = res[0]
+ self.options[file] = (user, group, perm, force)
+ self.options_order.append(file)
+ except:
+ traceback.print_exc()
+ self.log.warn(_("Bad config option: %s") % line)
+ continue
+ fd.close()
+ return True
+
+ def list_options(self):
+ """Sorts and returns configuration parameters"""
+ return self.options_order
+
+ def save(self):
+ """Saves configuration. Comments go on top"""
+ try:
+ fd = open(self.config, "w")
+ except:
+ self.log.error(_("Unable to save %s: %s") % (self.config, sys.exc_value))
+ return False
+ for comment in self.comments:
+ print >>fd, comment
+ # sorting keys
+ for file in self.options_order:
+ user, group, perm, force = self.options[file]
+ if force:
+ force = "\tforce"
+ else:
+ force = ""
+ print >>fd, "%s\t%s.%s\t%s%s" % (file, user, group, perm, force)
+ return True
+# }}}
+
diff --git a/src/msec/help_draksec.py b/src/msec/help_draksec.py
new file mode 100755
index 0000000..48f0df0
--- /dev/null
+++ b/src/msec/help_draksec.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+#
+# This script creates DrakSec help strings from libmsec code docstrings.
+#
+
+import sys
+import imp
+import inspect
+import re
+
+import config
+from libmsec import MSEC, Log
+
+help_perl_file = 'help.pm'
+help_python_file = 'help.py'
+
+header_perl = '''package security::help;
+# !! THIS FILE WAS AUTO-GENERATED BY draksec_help.py !!
+# !! DO NOT MODIFY HERE, MODIFY IN THE *MSEC* CVS !!
+
+use strict;
+use common;
+
+our %help = (
+'''
+
+header_python = '''# libmsec documentation for python.
+
+import gettext
+
+# localization
+try:
+ cat = gettext.Catalog('msec')
+ _ = cat.gettext
+except IOError:
+ _ = str
+
+HELP = {
+'''
+
+footer_perl = ''');
+'''
+
+footer_python = '''}
+'''
+
+### strings used in the rewritting
+function_str_perl = ''''%s' => N("%s"),
+'''
+
+function_str_python = '''
+ '%s': _("%s"), '''
+
+help_perl = open(help_perl_file, "w")
+help_python = open(help_python_file, "w")
+
+# process all configuration parameters
+log = Log(log_syslog=False, log_file=False)
+msec = MSEC(log)
+
+print >>help_perl, header_perl
+print >>help_python, header_python
+
+for variable in config.SETTINGS:
+ callback, params = config.SETTINGS[variable]
+ func = msec.get_action(callback)
+ if func:
+ print >>help_perl, function_str_perl % (variable, func.__doc__.strip())
+ print >>help_python, function_str_python % (variable, func.__doc__.strip())
+
+print >>help_perl, footer_perl
+print >>help_python, footer_python
+
+# draksec_help.py ends here
diff --git a/src/msec/libmsec.py b/src/msec/libmsec.py
new file mode 100755
index 0000000..80de3fc
--- /dev/null
+++ b/src/msec/libmsec.py
@@ -0,0 +1,1763 @@
+#!/usr/bin/python -O
+"""This is the main msec module, responsible for all msec operations.
+
+The following classes are defined here:
+
+ ConfigFile: an individual config file. This class is responsible for
+ configuration modification, variable searching and replacing,
+ and so on.
+
+ ConfigFiles: this file contains the entire set of modifications performed
+ by msec, stored in list of ConfigFile instances. When required, all
+ changes are commited back to physical files. This way, no real
+ change occurs on the system until the msec app explicitly tells
+ to do so.
+
+ Log: logging class, that supports logging to terminal, a fixed log file,
+ and syslog. A single log instance can be shared by all other
+ classes.
+
+ MSEC: main msec class. It contains the callback functions for all msec
+ operations.
+
+All configuration variables, and config file names are defined here as well.
+"""
+
+#---------------------------------------------------------------
+# Project : Mandriva Linux
+# Module : mseclib
+# File : libmsec.py
+# Version : $Id$
+# Author : Eugeni Dodonov
+# Original Author : Frederic Lepied
+# Created On : Mon Dec 10 22:52:17 2001
+# Purpose : low-level msec functions
+#---------------------------------------------------------------
+
+import os
+import grp
+import gettext
+import pwd
+import re
+import string
+import commands
+import time
+import stat
+import traceback
+import sys
+import glob
+
+# logging
+import logging
+from logging.handlers import SysLogHandler
+
+# configuration
+import config
+
+# localization
+try:
+ cat = gettext.Catalog('msec')
+ _ = cat.gettext
+except IOError:
+ _ = str
+
+# backup file suffix
+SUFFIX = '.msec'
+
+# 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'
+FSTAB = '/etc/fstab'
+GDM = '/etc/pam.d/gdm'
+GDMCONF = '/etc/X11/gdm/custom.conf'
+HALT = '/usr/bin/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'
+SHELLCONF = '/etc/sysconfig/shell'
+MSECBIN = '/usr/sbin/msec'
+MSECCRON = '/etc/cron.hourly/msec'
+MSEC_XINIT = '/etc/X11/xinit.d/msec'
+OPASSWD = '/etc/security/opasswd'
+PASSWD = '/etc/pam.d/passwd'
+POWEROFF = '/usr/bin/poweroff'
+REBOOT = '/usr/bin/reboot'
+SECURETTY = '/etc/securetty'
+SECURITYCRON = '/etc/cron.daily/msec'
+SECURITYSH = '/usr/share/msec/security.sh'
+SERVER = '/etc/security/msec/server'
+SHADOW = '/etc/shadow'
+SHUTDOWN = '/usr/bin/shutdown'
+SHUTDOWNALLOW = '/etc/shutdown.allow'
+SIMPLE_ROOT_AUTHEN = '/etc/pam.d/simple_root_authen'
+SSHDCONFIG = '/etc/ssh/sshd_config'
+STARTX = '/usr/bin/startx'
+SU = '/etc/pam.d/su'
+SYSCTLCONF = '/etc/sysctl.conf'
+SYSLOGCONF = '/etc/syslog.conf'
+SYSTEM_AUTH = '/etc/pam.d/system-auth'
+XDM = '/etc/pam.d/xdm'
+XSERVERS = '/etc/X11/xdm/Xservers'
+EXPORT = '/root/.xauth/export'
+
+# ConfigFile constants
+STRING_TYPE = type('')
+
+BEFORE=0
+INSIDE=1
+AFTER=2
+
+# regexps
+space = re.compile('\s')
+# X server
+STARTX_REGEXP = '(\s*serverargs=".*) -nolisten tcp(.*")'
+XSERVERS_REGEXP = '(\s*[^#]+/usr/bin/X .*) -nolisten tcp(.*)'
+GDMCONF_REGEXP = '(\s*command=.*/X.*?) -nolisten tcp(.*)$'
+KDMRC_REGEXP = re.compile('(.*?)-nolisten tcp(.*)$')
+# ctrl-alt-del
+CTRALTDEL_REGEXP = '^ca::ctrlaltdel:/sbin/shutdown.*'
+# consolehelper
+CONSOLE_HELPER = 'consolehelper'
+# ssh PermitRootLogin
+PERMIT_ROOT_LOGIN_REGEXP = '^\s*PermitRootLogin\s+(no|yes|without-password|forced-commands-only)'
+# pam
+SUCCEED_MATCH = '^auth\s+sufficient\s+pam_succeed_if.so\s+use_uid\s+user\s+ingroup\s+wheel\s*$'
+SUCCEED_LINE = 'auth sufficient pam_succeed_if.so use_uid user ingroup wheel'
+# cron
+CRON_ENTRY = '*/1 * * * * root /usr/share/msec/promisc_check.sh'
+CRON_REGEX = '[^#]+/usr/share/msec/promisc_check.sh'
+# tcp_wrappers
+ALL_REGEXP = '^ALL:ALL:DENY'
+ALL_LOCAL_REGEXP = '^ALL:ALL EXCEPT 127\.0\.0\.1:DENY'
+# password stuff
+LENGTH_REGEXP = re.compile('^(password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*?)\sminlen=([0-9]+)\s(.*)')
+NDIGITS_REGEXP = re.compile('^(password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*?)\sdcredit=([0-9]+)\s(.*)')
+UCREDIT_REGEXP = re.compile('^(password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*?)\sucredit=([0-9]+)\s(.*)')
+PASSWORD_REGEXP = '^\s*auth\s+sufficient\s+(?:/lib/security/)?pam_permit.so'
+UNIX_REGEXP = re.compile('(^\s*password\s+sufficient\s+(?:/lib/security/)?pam_unix.so.*)\sremember=([0-9]+)(.*)')
+PAM_TCB_REGEXP = re.compile('(^\s*password\s+sufficient\s+(?:/lib/security/)?pam_tcb.so.*)')
+# sulogin
+SULOGIN_REGEXP = '~~:S:wait:/sbin/sulogin'
+
+# {{{ helper functions
+def move(old, new):
+ """Renames files, deleting existent ones when necessary."""
+ try:
+ os.unlink(new)
+ except OSError:
+ pass
+ try:
+ os.rename(old, new)
+ except:
+ error('rename %s %s: %s' % (old, new, str(sys.exc_value)))
+
+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
+# }}}
+
+# {{{ Log
+class Log:
+ """Logging class. Logs to both syslog and log file"""
+ def __init__(self,
+ app_name="msec",
+ log_syslog=True,
+ log_file=True,
+ log_level = logging.INFO,
+ log_facility=SysLogHandler.LOG_AUTHPRIV,
+ syslog_address="/dev/log",
+ log_path="/var/log/msec.log",
+ interactive=True):
+ self.log_facility = log_facility
+ self.log_path = log_path
+
+ # buffer
+ self.buffer = None
+
+ # common logging stuff
+ self.logger = logging.getLogger(app_name)
+
+ # syslog
+ if log_syslog:
+ self.syslog_h = SysLogHandler(facility=log_facility, address=syslog_address)
+ formatter = logging.Formatter('%(name)s: %(levelname)s: %(message)s')
+ self.syslog_h.setFormatter(formatter)
+ self.logger.addHandler(self.syslog_h)
+
+ # log to file
+ if log_file:
+ self.file_h = logging.FileHandler(self.log_path)
+ formatter = logging.Formatter('%(asctime)s %(levelname)s: %(message)s')
+ self.file_h.setFormatter(formatter)
+ self.logger.addHandler(self.file_h)
+
+ # interactive logging
+ if interactive:
+ self.interactive_h = logging.StreamHandler(sys.stderr)
+ formatter = logging.Formatter('%(levelname)s: %(message)s')
+ self.interactive_h.setFormatter(formatter)
+ self.logger.addHandler(self.interactive_h)
+
+ self.logger.setLevel(log_level)
+
+ def info(self, message):
+ """Informative message (normal msec operation)"""
+ if self.buffer:
+ self.buffer["info"].append(message)
+ else:
+ self.logger.info(message)
+
+ def error(self, message):
+ """Error message (security has changed: authentication, passwords, etc)"""
+ if self.buffer:
+ self.buffer["error"].append(message)
+ else:
+ self.logger.error(message)
+
+ def debug(self, message):
+ """Debugging message"""
+ if self.buffer:
+ self.buffer["debug"].append(message)
+ else:
+ self.logger.debug(message)
+
+ def critical(self, message):
+ """Critical message (big security risk, e.g., rootkit, etc)"""
+ if self.buffer:
+ self.buffer["critical"].append(message)
+ else:
+ self.logger.critical(message)
+
+ def warn(self, message):
+ """Warning message (slight security change, permissions change, etc)"""
+ if self.buffer:
+ self.buffer["warn"].append(message)
+ else:
+ self.logger.warn(message)
+
+ def start_buffer(self):
+ """Beginns message buffering"""
+ self.buffer = {"info": [], "error": [], "debug": [], "critical": [], "warn": []}
+
+ def get_buffer(self):
+ """Returns buffered messages"""
+ messages = self.buffer.copy()
+ del self.buffer
+ self.buffer = None
+ return messages
+
+# }}}
+
+# {{{ ConfigFiles - stores references to all configuration files
+class ConfigFiles:
+ """This class is responsible to store references to all configuration files,
+ mark them as changed, and update on disk when necessary"""
+ def __init__(self, log):
+ """Initializes list of ConfigFiles"""
+ self.files = {}
+ self.modified_files = []
+ self.action_assoc = []
+ self.log = log
+
+ def add(self, file, path):
+ """Appends a path to list of files"""
+ self.files[path] = file
+
+ def modified(self, path):
+ """Marks a file as modified"""
+ if not path in self.modified_files:
+ self.modified_files.append(path)
+
+ def get_config_file(self, path, suffix=None):
+ """Retreives corresponding config file"""
+ try:
+ return self.files[path]
+ except KeyError:
+ return ConfigFile(path, self, self.log, suffix=suffix)
+
+ def add_config_assoc(self, regex, action):
+ """Adds association between a file and an action"""
+ self.log.debug("Adding custom command '%s' for '%s'" % (action, regex))
+ self.action_assoc.append((re.compile(regex), action))
+
+ def write_files(self, commit=True):
+ """Writes all files back to disk"""
+ for f in self.files.values():
+ self.log.debug("Attempting to write %s" % f.path)
+ if commit:
+ f.write()
+
+ if len(self.modified_files) > 0:
+ self.log.info("%s: %s" % (config.MODIFICATIONS_FOUND, " ".join(self.modified_files)))
+ else:
+ self.log.info(config.MODIFICATIONS_NOT_FOUND)
+
+ for f in self.modified_files:
+ for a in self.action_assoc:
+ res = a[0].search(f)
+ if res:
+ s = substitute_re_result(res, a[1])
+ if commit:
+ self.log.info(_('%s modified so launched command: %s') % (f, s))
+ cmd = commands.getstatusoutput(s)
+ cmd = [0, '']
+ if cmd[0] == 0:
+ if cmd[1]:
+ self.log.info(cmd[1])
+ else:
+ self.log.error(cmd[1])
+ else:
+ self.log.info(_('%s modified so should have run command: %s') % (f, s))
+
+# }}}
+
+# {{{ ConfigFile - an individual config file
+class ConfigFile:
+ """This class represents an individual config file.
+ All config files are stored in meta (which is ConfigFiles).
+ All operations are performed in memory, and written when required"""
+ def __init__(self, path, meta, log, root='', suffix=None):
+ """Initializes a config file, and put reference to meta (ConfigFiles)"""
+ self.meta=meta
+ self.path = 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.log = log
+ 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
+ self.meta.modified(self.path)
+ 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.lexists(self.path)
+ #return os.path.exists(self.path) or (self.suffix and os.path.exists(self.path + self.suffix))
+
+ def realpath(self):
+ return os.path.realpath(self.path)
+
+ 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():
+ try:
+ os.unlink(self.path)
+ except:
+ error('unlink %s: %s' % (self.path, str(sys.exc_value)))
+ self.log.info(_('deleted %s') % (self.path,))
+ elif self.is_modified:
+ content = string.join(self.lines, "\n")
+ dirname = os.path.dirname(self.path)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+ 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):
+ try:
+ os.utime(self.path, None)
+ except:
+ self.log.error('utime %s: %s' % (self.path, str(sys.exc_value)))
+ elif self.suffix and os.path.exists(self.path + self.suffix):
+ move(self.path + self.suffix, self.path)
+ try:
+ os.utime(self.path, None)
+ except:
+ self.log.error('utime %s: %s' % (self.path, str(sys.exc_value)))
+ else:
+ self.lines = []
+ self.is_modified = 1
+ file = open(self.path, 'w')
+ file.close()
+ self.log.info(_('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:
+ try:
+ os.unlink(self.path)
+ except:
+ self.log.error('unlink %s: %s' % (self.path, str(sys.exc_value)))
+ self.log.info(_('deleted %s') % (self.path,))
+ if not done:
+ try:
+ os.symlink(self.sym_link, self.path)
+ except:
+ self.log.error('symlink %s %s: %s' % (self.sym_link, self.path, str(sys.exc_value)))
+ self.log.info(_('made symbolic link from %s to %s') % (self.sym_link, self.path))
+
+ if self.is_moved:
+ move(self.path, self.path + self.suffix)
+ self.log.info(_('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)
+ start_regexp = start
+
+ 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()
+ self.log.debug(_('set variable %s to %s in %s') % (var, value, self.path,))
+ return self
+ if status == BEFORE:
+ # never found the start delimiter
+ self.log.warning(_('WARNING: never found regexp %s in %s, not writing changes') % (start_regexp, 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()
+ self.log.info(_('set variable %s to %s in %s') % (var, value, self.path,))
+ return self
+
+ def get_shell_variable(self, var, start=None, end=None):
+ # if file does not exists, fail quickly
+ if not self.exists():
+ return None
+ if end:
+ end=re.compile(end)
+ if start:
+ start=re.compile(start)
+ regex = re.compile('^' + var + '="?([^#"]+)"?(.*)')
+ lines = self.get_lines()
+ llen = len(lines)
+ start_idx = 0
+ end_idx = llen
+ if start:
+ found = 0
+ for idx in range(0, llen):
+ if start.search(lines[idx]):
+ start_idx = idx
+ found = 1
+ break
+ if found:
+ for idx in range(start_idx, llen):
+ if end.search(lines[idx]):
+ end_idx = idx
+ break
+ else:
+ start_idx = 0
+ for idx in range(end_idx - 1, start_idx - 1, -1):
+ res = regex.search(lines[idx])
+ if res:
+ return res.group(1)
+ return None
+
+ def get_match(self, regex, replace=None):
+ # if file does not exists, fail quickly
+ if not self.exists():
+ return None
+ r=re.compile(regex)
+ lines = self.get_lines()
+ for idx in range(0, len(lines)):
+ res = r.search(lines[idx])
+ if res:
+ if replace:
+ s = substitute_re_result(res, replace)
+ return s
+ else:
+ return lines[idx]
+ return None
+
+ def replace_line_matching(self, regex, value, at_end_if_not_found=0, all=0, start=None, end=None):
+ # if at_end_if_not_found is a string its value will be used as the string to inster
+ 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:
+ self.log.debug(_("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:
+ if type(at_end_if_not_found) == STRING_TYPE:
+ value = at_end_if_not_found
+ self.log.debug(_("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)
+ self.log.debug(_("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:
+ self.log.debug(_("appended in %s the line:\n%s") % (self.path, value))
+ self.append(value)
+ self.modified()
+ matches = matches + 1
+ return matches
+
+ def insert_before(self, regex, value, at_top_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)
+ self.log.debug(_("inserted in %s before the line %d:\n%s\nthe line:\n%s") % (self.path, idx, lines[idx], s))
+ lines.insert(idx, s)
+ self.modified()
+ matches = matches + 1
+ if not all:
+ return matches
+ if matches == 0 and at_top_if_not_found:
+ self.log.debug(_("inserted at the top of %s the line:\n%s") % (self.path, value))
+ lines.insert(0, value)
+ self.modified()
+ matches = matches + 1
+ return matches
+
+ def insert_at(self, idx, value):
+ lines = self.get_lines()
+ try:
+ lines.insert(idx, value)
+ self.log.debug(_("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:
+ self.log.debug(_("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
+# }}}
+
+# {{{ MSEC - main class
+class MSEC:
+ """Main msec class. Contains all functions and performs the actions"""
+ def __init__(self, log):
+ """Initializes config files and associations"""
+ # all config files
+ self.log = log
+ self.configfiles = ConfigFiles(log)
+
+ # associate helper commands with files
+ self.configfiles.add_config_assoc(INITTAB, '/sbin/telinit q')
+ self.configfiles.add_config_assoc('/etc(?:/rc.d)?/init.d/(.+)', '[ -f /var/lock/subsys/@1 ] && @0 reload')
+ self.configfiles.add_config_assoc(SYSCTLCONF, '/sbin/sysctl -e -p /etc/sysctl.conf')
+ self.configfiles.add_config_assoc(SSHDCONFIG, '[ -f /var/lock/subsys/sshd ] && /etc/rc.d/init.d/sshd restart')
+ self.configfiles.add_config_assoc(LILOCONF, '[ `/usr/sbin/detectloader` = LILO ] && /sbin/lilo')
+ self.configfiles.add_config_assoc(SYSLOGCONF, '[ -f /var/lock/subsys/syslog ] && service syslog reload')
+ self.configfiles.add_config_assoc('^/etc/issue$', '/usr/bin/killall mingetty')
+
+ # TODO: add a common function to check parameters
+
+ def reset(self):
+ """Resets the configuration"""
+ self.log.debug("Resetting msec data.")
+ self.configfiles = ConfigFiles(self.log)
+
+ def get_action(self, name):
+ """Determines correspondent function for requested action."""
+ try:
+ func = getattr(self, name)
+ return func
+ except:
+ return None
+
+ def commit(self, really_commit=True):
+ """Commits changes"""
+ if not really_commit:
+ self.log.info(_("In check-only mode, nothing is written back to disk."))
+ self.configfiles.write_files(really_commit)
+
+ def apply(self, curconfig):
+ '''Applies configuration from a MsecConfig instance'''
+ # first, reset previous msec data
+ self.reset()
+ # process all options
+ for opt in curconfig.list_options():
+ # Determines correspondent function
+ action = None
+ callback = config.find_callback(opt)
+ valid_params = config.find_valid_params(opt)
+ if callback:
+ action = self.get_action(callback)
+ if not action:
+ # The required functionality is not supported
+ self.log.info(_("'%s' is not available in this version") % opt)
+ continue
+ self.log.debug("Processing action %s: %s(%s)" % (opt, callback, curconfig.get(opt)))
+ # validating parameters
+ param = curconfig.get(opt)
+ if param not in valid_params and '*' not in valid_params:
+ self.log.error(_("Invalid parameter for %s: '%s'. Valid parameters: '%s'.") % (opt,
+ param,
+ valid_values[opt]))
+ continue
+ action(curconfig.get(opt))
+
+ def create_server_link(self, param):
+ ''' Creates the symlink /etc/security/msec/server to point to /etc/security/msec/server.<SERVER_LEVEL>. The /etc/security/msec/server is used by chkconfig --add to decide to add a service if it is present in the file during the installation of packages.'''
+ __params__ = ["no", "default", "secure"]
+
+ server = self.configfiles.get_config_file(SERVER)
+
+ if param == "no":
+ if server.exists():
+ self.log.info(_('Allowing unrestricted chkconfig for packages'))
+ server.unlink()
+ else:
+ newpath = "%s.%s" % (SERVER, param)
+ if server.realpath() != newpath:
+ self.log.info(_('Restricting chkconfig for packages according to "%s" profile') % param)
+ server.symlink(newpath)
+
+ def set_root_umask(self, umask):
+ ''' Set the root umask.'''
+ msec = self.configfiles.get_config_file(SHELLCONF)
+
+ val = msec.get_shell_variable('UMASK_ROOT')
+
+ if val != umask:
+ self.log.info(_('Setting root umask to %s') % (umask))
+ msec.set_shell_variable('UMASK_ROOT', umask)
+
+ def set_user_umask(self, umask):
+ ''' Set the user umask.'''
+ msec = self.configfiles.get_config_file(SHELLCONF)
+
+ val = msec.get_shell_variable('UMASK_USER')
+
+ if val != umask:
+ self.log.info(_('Setting users umask to %s') % (umask))
+ msec.set_shell_variable('UMASK_USER', umask)
+
+ def allow_x_connections(self, arg):
+ ''' Allow/Forbid X connections. Accepted arguments: yes (all connections are allowed), local (only local connection), no (no connection).'''
+
+ xinit = self.configfiles.get_config_file(MSEC_XINIT)
+ val = xinit.get_match('/usr/bin/xhost\s*(\+\s*[^#]*)', '@1')
+
+ if val:
+ if val == '+':
+ val = "yes"
+ elif val == "+ localhost":
+ val = "local"
+ else:
+ val = "no"
+ else:
+ val = "no"
+
+ if val != arg:
+ if arg == "yes":
+ self.log.info(_('Allowing users to connect X server from everywhere'))
+ xinit.replace_line_matching('/usr/bin/xhost', '/usr/bin/xhost +', 1)
+ elif arg == "local":
+ self.log.info(_('Allowing users to connect X server from localhost'))
+ xinit.replace_line_matching('/usr/bin/xhost', '/usr/bin/xhost + localhost', 1)
+ elif arg == "no":
+ self.log.info(_('Restricting X server connection to the console user'))
+ xinit.remove_line_matching('/usr/bin/xhost', 1)
+ else:
+ self.log.error(_('invalid allow_x_connections arg: %s') % arg)
+
+ def allow_xserver_to_listen(self, arg):
+ ''' The argument specifies if clients are authorized to connect to the X server on the tcp port 6000 or not.'''
+
+ startx = self.configfiles.get_config_file(STARTX)
+ xservers = self.configfiles.get_config_file(XSERVERS)
+ gdmconf = self.configfiles.get_config_file(GDMCONF)
+ kdmrc = self.configfiles.get_config_file(KDMRC)
+
+ val_startx = startx.get_match(STARTX_REGEXP)
+ val_xservers = xservers.get_match(XSERVERS_REGEXP)
+ val_gdmconf = gdmconf.get_shell_variable('DisallowTCP')
+ str = kdmrc.get_shell_variable('ServerArgsLocal', 'X-\*-Core', '^\s*$')
+ if str:
+ val_kdmrc = KDMRC_REGEXP.search(str)
+ else:
+ val_kdmrc = None
+
+ # TODO: better check for file existance
+
+ if arg == "yes":
+ if val_startx or val_xservers or val_kdmrc or val_gdmconf != 'false':
+ self.log.info(_('Allowing the X server to listen to tcp connections'))
+ if startx.exists():
+ startx.replace_line_matching(STARTX_REGEXP, '@1@2')
+ if xservers.exists():
+ xservers.replace_line_matching(XSERVERS_REGEXP, '@1@2', 0, 1)
+ if gdmconf.exists():
+ gdmconf.set_shell_variable('DisallowTCP', 'false', '\[security\]', '^\s*$')
+ if kdmrc.exists():
+ kdmrc.replace_line_matching('^(ServerArgsLocal=.*?)-nolisten tcp(.*)$', '@1@2', 0, 0, 'X-\*-Core', '^\s*$')
+ else:
+ if not val_startx or not val_xservers or not val_kdmrc or val_gdmconf != 'true':
+ self.log.info(_('Forbidding the X server to listen to tcp connection'))
+ if not val_startx:
+ startx.exists() and startx.replace_line_matching('serverargs="(.*?)( -nolisten tcp)?"', 'serverargs="@1 -nolisten tcp"')
+ if not val_xservers:
+ xservers.exists() and xservers.replace_line_matching('(\s*[^#]+/usr/bin/X .*?)( -nolisten tcp)?$', '@1 -nolisten tcp', 0, 1)
+ if val_gdmconf != 'true':
+ gdmconf.exists() and gdmconf.set_shell_variable('DisallowTCP', 'true', '\[security\]', '^\s*$')
+ if val_kdmrc:
+ if not val_kdmrc.get_match('^ServerArgsLocal=.* -nolisten tcp'):
+ kdmrc.exists() and kdmrc.replace_line_matching('^(ServerArgsLocal=.*)$', '@1 -nolisten tcp', 'ServerArgsLocal=-nolisten tcp', 0, 'X-\*-Core', '^\s*$')
+
+ def set_shell_timeout(self, val):
+ ''' Set the shell timeout. A value of zero means no timeout.'''
+ msec = self.configfiles.get_config_file(SHELLCONF)
+ try:
+ timeout = int(val)
+ except:
+ self.log.error(_('Invalid shell timeout "%s"') % size)
+ return
+
+ old = msec.get_shell_variable('TMOUT')
+ if old:
+ old = int(old)
+
+ if old != timeout:
+ self.log.info(_('Setting shell timeout to %s') % timeout)
+ msec.set_shell_variable('TMOUT', timeout)
+
+ def set_shell_history_size(self, size):
+ ''' Set shell commands history size. A value of -1 means unlimited.'''
+ try:
+ size = int(size)
+ except:
+ self.log.error(_('Invalid shell history size "%s"') % size)
+ return
+
+ msec = self.configfiles.get_config_file(SHELLCONF)
+
+ val = msec.get_shell_variable('HISTFILESIZE')
+ if val:
+ val = int(val)
+
+ if size >= 0:
+ if val != size:
+ self.log.info(_('Setting shell history size to %s') % size)
+ msec.set_shell_variable('HISTFILESIZE', size)
+ else:
+ if val != None:
+ self.log.info(_('Removing limit on shell history size'))
+ msec.remove_line_matching('^HISTFILESIZE=')
+
+ def set_win_parts_umask(self, umask):
+ ''' Set umask option for mounting vfat and ntfs partitions. A value of None means default umask.'''
+ fstab = self.configfiles.get_config_file(FSTAB)
+
+ if umask == "no":
+ fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+)umask=\d+(\s.*)", "@1defaults@3", 0, 1)
+ fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+)umask=\d+,(.*)", "@1@3", 0, 1)
+ fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+\S+),umask=\d+(.*)", "@1@3", 0, 1)
+ else:
+ fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+\S*)umask=\d+(.*)", "@1umask=0@3", 0, 1)
+ fstab.replace_line_matching("(.*\s(vfat|ntfs)\s+)(?!.*umask=)(\S+)(.*)", "@1@3,umask=0@4", 0, 1)
+
+ def allow_reboot(self, arg):
+ ''' Allow/Forbid system reboot and shutdown to local users.'''
+ shutdownallow = self.configfiles.get_config_file(SHUTDOWNALLOW)
+ sysctlconf = self.configfiles.get_config_file(SYSCTLCONF)
+ kdmrc = self.configfiles.get_config_file(KDMRC)
+ gdmconf = self.configfiles.get_config_file(GDMCONF)
+ inittab = self.configfiles.get_config_file(INITTAB)
+ shutdown = self.configfiles.get_config_file(SHUTDOWN)
+ poweroff = self.configfiles.get_config_file(POWEROFF)
+ reboot = self.configfiles.get_config_file(REBOOT)
+ halt = self.configfiles.get_config_file(HALT)
+
+ val_shutdownallow = shutdownallow.exists()
+ val_shutdown = shutdown.exists()
+ val_poweroff = poweroff.exists()
+ val_reboot = reboot.exists()
+ val_halt = halt.exists()
+ val_sysctlconf = sysctlconf.get_shell_variable('kernel.sysrq')
+ val_inittab = inittab.get_match(CTRALTDEL_REGEXP)
+ val_gdmconf = gdmconf.get_shell_variable('SystemMenu')
+ oldval_kdmrc = kdmrc.get_shell_variable('AllowShutdown', 'X-:\*-Core', '^\s*$')
+
+ if arg == "yes":
+ if val_shutdownallow or not val_shutdown or not val_poweroff or not val_reboot or not val_halt:
+ self.log.info(_('Allowing reboot and shutdown to the console user'))
+ shutdownallow.exists() and shutdownallow.move(SUFFIX)
+ shutdown.exists() or shutdown.symlink(CONSOLE_HELPER)
+ poweroff.exists() or poweroff.symlink(CONSOLE_HELPER)
+ reboot.exists() or reboot.symlink(CONSOLE_HELPER)
+ halt.exists() or halt.symlink(CONSOLE_HELPER)
+ if val_sysctlconf == '0':
+ self.log.info(_('Allowing SysRq key to the console user'))
+ sysctlconf.set_shell_variable('kernel.sysrq', 1)
+ if val_gdmconf == 'false':
+ self.log.info(_('Allowing Shutdown/Reboot in GDM'))
+ gdmconf.exists() and gdmconf.set_shell_variable('SystemMenu', 'true', '\[greeter\]', '^\s*$')
+ if kdmrc.exists():
+ if oldval_kdmrc != 'All':
+ self.log.info(_('Allowing Shutdown/Reboot in KDM'))
+ kdmrc.set_shell_variable('AllowShutdown', 'All', 'X-:\*-Core', '^\s*$')
+ if not val_inittab:
+ self.log.info(_('Allowing Ctrl-Alt-Del from console'))
+ inittab.replace_line_matching(CTRALTDEL_REGEXP, 'ca::ctrlaltdel:/sbin/shutdown -t3 -r now', 1)
+ else:
+ if not val_shutdownallow or val_shutdown or val_poweroff or val_reboot or val_halt:
+ self.log.info(_('Forbidding reboot and shutdown to the console user'))
+ if not shutdownallow.exists():
+ self.configfiles.get_config_file(SHUTDOWNALLOW, SUFFIX).touch()
+ shutdown.exists() and shutdown.unlink()
+ poweroff.exists() and poweroff.unlink()
+ reboot.exists() and reboot.unlink()
+ halt.exists() and halt.unlink()
+ if val_sysctlconf != '0':
+ self.log.info(_('Forbidding SysRq key to the console user'))
+ sysctlconf.set_shell_variable('kernel.sysrq', 0)
+ if val_gdmconf != 'false':
+ self.log.info(_('Forbidding Shutdown/Reboot in GDM'))
+ gdmconf.exists() and gdmconf.set_shell_variable('SystemMenu', 'false', '\[greeter\]', '^\s*$')
+ if kdmrc.exists():
+ if oldval_kdmrc != 'None':
+ self.log.info(_('Forbidding Shutdown/Reboot in KDM'))
+ kdmrc.set_shell_variable('AllowShutdown', 'None', 'X-:\*-Core', '^\s*$')
+ if val_inittab:
+ self.log.info(_('Forbidding Ctrl-Alt-Del from console'))
+ inittab.remove_line_matching(CTRALTDEL_REGEXP)
+
+ def allow_user_list(self, arg):
+ ''' Allow/Forbid the list of users on the system on display managers (kdm and gdm).'''
+ kdmrc = self.configfiles.get_config_file(KDMRC)
+ gdmconf = self.configfiles.get_config_file(GDMCONF)
+
+ oldval_gdmconf = gdmconf.get_shell_variable('Browser')
+ oldval_kdmrc = kdmrc.get_shell_variable('ShowUsers', 'X-\*-Greeter', '^\s*$')
+
+ if arg == "yes":
+ if kdmrc.exists():
+ if oldval_kdmrc != 'NotHidden':
+ self.log.info(_("Allowing list of users in KDM"))
+ kdmrc.set_shell_variable('ShowUsers', 'NotHidden', 'X-\*-Greeter', '^\s*$')
+ if gdmconf.exists():
+ if oldval_gdmconf != 'true':
+ self.log.info(_("Allowing list of users in GDM"))
+ gdmconf.set_shell_variable('Browser', 'true')
+ else:
+ if kdmrc.exists():
+ if oldval_kdmrc != 'Selected':
+ self.log.info(_("Forbidding list of users in KDM"))
+ kdmrc.set_shell_variable('ShowUsers', 'Selected', 'X-\*-Greeter', '^\s*$')
+ if gdmconf.exists():
+ if oldval_gdmconf != 'false':
+ self.log.info(_("Forbidding list of users in GDM"))
+ gdmconf.set_shell_variable('Browser', 'false')
+
+ def allow_root_login(self, arg):
+ ''' Allow/Forbid direct root login.'''
+ securetty = self.configfiles.get_config_file(SECURETTY)
+ kde = self.configfiles.get_config_file(KDE)
+ gdm = self.configfiles.get_config_file(GDM)
+ gdmconf = self.configfiles.get_config_file(GDMCONF)
+ xdm = self.configfiles.get_config_file(XDM)
+
+ val = {}
+ val_kde = kde.get_match('auth required (?:/lib/security/)?pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login')
+ val_gdm = gdm.get_match('auth required (?:/lib/security/)?pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login')
+ val_xdm = xdm.get_match('auth required (?:/lib/security/)?pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login')
+ num = 0
+ for n in range(1, 7):
+ s = 'tty' + str(n)
+ if securetty.get_match(s):
+ num = num + 1
+ s = 'vc/' + str(n)
+ if securetty.get_match(s):
+ num = num + 1
+
+ if arg == "yes":
+ if val_kde or val_gdm or val_xdm or num != 12:
+ self.log.info(_('Allowing direct root login'))
+ if gdmconf.exists():
+ gdmconf.set_shell_variable('ConfigAvailable', 'true', '\[greeter\]', '^\s*$')
+
+ for cnf in [kde, gdm, xdm]:
+ if cnf.exists():
+ cnf.remove_line_matching('^auth\s*required\s*(?:/lib/security/)?pam_listfile.so.*bastille-no-login', 1)
+
+ for n in range(1, 7):
+ s = 'tty' + str(n)
+ securetty.replace_line_matching(s, s, 1)
+ s = 'vc/' + str(n)
+ securetty.replace_line_matching(s, s, 1)
+ else:
+ if gdmconf.exists():
+ gdmconf.set_shell_variable('ConfigAvailable', 'false', '\[greeter\]', '^\s*$')
+ if (kde.exists() and not val_kde) or (gdm.exists() and not val_gdm) or (xdm.exists() and not val_xdm) or num > 0:
+ self.log.info(_('Forbidding direct root login'))
+
+ bastillenologin = self.configfiles.get_config_file(BASTILLENOLOGIN)
+ bastillenologin.replace_line_matching('^\s*root', 'root', 1)
+
+ # TODO: simplify this
+ for cnf in [kde, gdm, xdm]:
+ if cnf.exists():
+ (cnf.replace_line_matching('^auth\s*required\s*(?:/lib/security/)?pam_listfile.so.*bastille-no-login',
+ 'auth required pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login') or
+ cnf.insert_at(0, 'auth required pam_listfile.so onerr=succeed item=user sense=deny file=/etc/bastille-no-login'))
+ securetty.remove_line_matching('.+', 1)
+
+ def allow_remote_root_login(self, arg):
+ ''' Allow/Forbid remote root login via sshd. You can specify yes, no and without-password. See sshd_config(5) man page for more information.'''
+ sshd_config = self.configfiles.get_config_file(SSHDCONFIG)
+
+ val = sshd_config.get_match(PERMIT_ROOT_LOGIN_REGEXP, '@1')
+
+ if val != arg:
+ if arg == "yes":
+ self.log.info(_('Allowing remote root login'))
+ sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
+ 'PermitRootLogin yes', 1)
+ elif arg == "no":
+ self.log.info(_('Forbidding remote root login'))
+ sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
+ 'PermitRootLogin no', 1)
+ elif arg == "without_password":
+ self.log.info(_('Allowing remote root login only by passphrase'))
+ sshd_config.exists() and sshd_config.replace_line_matching(PERMIT_ROOT_LOGIN_REGEXP,
+ 'PermitRootLogin without-password', 1)
+
+ def enable_pam_wheel_for_su(self, arg):
+ ''' Enabling su only from members of the wheel group or allow su from any user.'''
+ su = self.configfiles.get_config_file(SU)
+
+ val = su.get_match('^auth\s+required\s+(?:/lib/security/)?pam_wheel.so\s+use_uid\s*$')
+
+ if arg == "yes":
+ if not val:
+ self.log.info(_('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']:
+ self.log.error(_('wheel group is empty'))
+ return
+ if su.exists():
+ (su.replace_line_matching('^[#\s]*auth\s+required\s+(?:/lib/security/)?pam_wheel.so\s+use_uid\s*$',
+ 'auth required pam_wheel.so use_uid') or \
+ su.insert_after('^auth\s+required', 'auth required pam_wheel.so use_uid'))
+ else:
+ if val:
+ self.log.info(_('Allowing su for all'))
+ if su.exists():
+ su.replace_line_matching('^auth\s+required\s+(?:/lib/security/)?pam_wheel.so\s+use_uid\s*$',
+ '# auth required pam_wheel.so use_uid')
+
+ def enable_pam_root_from_wheel(self, arg):
+ ''' Allow root access without password for the members of the wheel group.'''
+ su = self.configfiles.get_config_file(SU)
+ simple = self.configfiles.get_config_file(SIMPLE_ROOT_AUTHEN)
+
+ if not su.exists():
+ return
+
+ val = su.get_match(SUCCEED_MATCH)
+
+ val_simple = simple.get_match(SUCCEED_MATCH)
+
+ if arg == "yes":
+ if not val or not val_simple:
+ self.log.info(_('Allowing transparent root access for wheel group members'))
+ if not val:
+ print "here2"
+ su.insert_before('^auth\s+sufficient', SUCCEED_LINE)
+ if simple.exists() and not val_simple:
+ simple.insert_before('^auth\s+sufficient', SUCCEED_LINE)
+ else:
+ if val or val_simple:
+ self.log.info(_('Disabling transparent root access for wheel group members'))
+ if val:
+ su.remove_line_matching(SUCCEED_MATCH)
+ if simple.exists() and val_simple:
+ simple.remove_line_matching(SUCCEED_MATCH)
+
+ def allow_autologin(self, arg):
+ ''' Allow/Forbid autologin.'''
+ autologin = self.configfiles.get_config_file(AUTOLOGIN)
+
+ val = autologin.get_shell_variable('AUTOLOGIN')
+
+ if val != arg:
+ if arg == "yes":
+ self.log.info(_('Allowing autologin'))
+ autologin.set_shell_variable('AUTOLOGIN', 'yes')
+ else:
+ self.log.info(_('Forbidding autologin'))
+ autologin.set_shell_variable('AUTOLOGIN', 'no')
+
+ def password_loader(self, value):
+ '''Unused'''
+ self.log.info(_('Activating password in boot loader'))
+ liloconf = self.configfiles.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 = self.configfiles.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(self):
+ '''Unused'''
+ self.log.info(_('Removing password in boot loader'))
+ liloconf = self.configfiles.get_config_file(LILOCONF)
+ liloconf.exists() and liloconf.remove_line_matching('^password=', 1)
+ menulst = self.configfiles.get_config_file(MENULST)
+ menulst.exists() and menulst.remove_line_matching('^password\s')
+
+ def enable_console_log(self, arg, expr='*.*', dev='tty12'):
+ ''' Enable/Disable syslog reports to console 12. \\fIexpr\\fP is the expression describing what to log (see syslog.conf(5) for more details) and dev the device to report the log.'''
+
+ syslogconf = self.configfiles.get_config_file(SYSLOGCONF)
+
+ val = syslogconf.get_match('\s*[^#]+/dev/([^ ]+)', '@1')
+
+ if arg == "yes":
+ if dev != val:
+ self.log.info(_('Enabling log on console'))
+ syslogconf.exists() and syslogconf.replace_line_matching('\s*[^#]+/dev/', expr + ' /dev/' + dev, 1)
+ else:
+ if val != None:
+ self.log.info(_('Disabling log on console'))
+ syslogconf.exists() and syslogconf.remove_line_matching('\s*[^#]+/dev/')
+
+ def enable_security_check(self, arg):
+ ''' Activate/Disable daily security check.'''
+ cron = self.configfiles.get_config_file(CRON)
+ cron.remove_line_matching('[^#]+/usr/share/msec/security.sh')
+
+ securitycron = self.configfiles.get_config_file(SECURITYCRON)
+
+ if arg == "yes":
+ if not securitycron.exists():
+ self.log.info(_('Activating daily security check'))
+ securitycron.symlink(SECURITYSH)
+ else:
+ if securitycron.exists():
+ self.log.info(_('Disabling daily security check'))
+ securitycron.unlink()
+
+ def authorize_services(self, arg):
+ ''' Configure access to tcp_wrappers services (see hosts.deny(5)). If arg = yes, all services are authorized. If arg = local, only local ones are, and if arg = no, no services are authorized. In this case, To authorize the services you need, use /etc/hosts.allow (see hosts.allow(5)).'''
+
+ hostsdeny = self.configfiles.get_config_file(HOSTSDENY)
+
+ if hostsdeny.get_match(ALL_REGEXP):
+ val = "no"
+ elif hostsdeny.get_match(ALL_LOCAL_REGEXP):
+ val = "local"
+ else:
+ val = "yes"
+
+ if val != arg:
+ if arg == "yes":
+ self.log.info(_('Authorizing all services'))
+ hostsdeny.remove_line_matching(ALL_REGEXP, 1)
+ hostsdeny.remove_line_matching(ALL_LOCAL_REGEXP, 1)
+ elif arg == "no":
+ self.log.info(_('Disabling all services'))
+ hostsdeny.remove_line_matching(ALL_LOCAL_REGEXP, 1)
+ hostsdeny.replace_line_matching(ALL_REGEXP, 'ALL:ALL:DENY', 1)
+ elif arg == "local":
+ self.log.info(_('Disabling non local services'))
+ hostsdeny.remove_line_matching(ALL_REGEXP, 1)
+ hostsdeny.replace_line_matching(ALL_LOCAL_REGEXP, 'ALL:ALL EXCEPT 127.0.0.1:DENY', 1)
+
+ def set_zero_one_variable(self, file, variable, value, one_msg, zero_msg):
+ ''' Helper function for enable_ip_spoofing_protection, accept_icmp_echo, accept_broadcasted_icmp_echo,
+ # accept_bogus_error_responses and enable_log_strange_packets.'''
+ f = self.configfiles.get_config_file(file)
+ curvalue = f.get_shell_variable(variable)
+ if value == "yes":
+ value = "1"
+ else:
+ value = "0"
+ if value != curvalue:
+ if value == "1":
+ self.log.info(one_msg)
+ f.set_shell_variable(variable, 1)
+ else:
+ self.log.info(zero_msg)
+ f.set_shell_variable(variable, 0)
+
+ def enable_ip_spoofing_protection(self, arg, alert=1):
+ ''' Enable/Disable IP spoofing protection.'''
+ # the alert argument is kept for backward compatibility
+ self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.rp_filter', arg, 'Enabling ip spoofing protection', 'Disabling ip spoofing protection')
+
+ def enable_dns_spoofing_protection(self, arg, alert=1):
+ ''' Enable/Disable name resolution spoofing protection. If \\fIalert\\fP is true, also reports to syslog.'''
+ hostconf = self.configfiles.get_config_file(HOSTCONF)
+
+ val = hostconf.get_match('nospoof\s+on')
+
+ if arg:
+ if not val:
+ self.log.info(_('Enabling name resolution spoofing protection'))
+ hostconf.replace_line_matching('nospoof', 'nospoof on', 1)
+ hostconf.replace_line_matching('spoofalert', 'spoofalert on', (alert != 0))
+ else:
+ if val:
+ self.log.info(_('Disabling name resolution spoofing protection'))
+ hostconf.remove_line_matching('nospoof')
+ hostconf.remove_line_matching('spoofalert')
+
+ def accept_icmp_echo(self, arg):
+ ''' Accept/Refuse icmp echo.'''
+ self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_all', arg, 'Ignoring icmp echo', 'Accepting icmp echo')
+
+ def accept_broadcasted_icmp_echo(self, arg):
+ ''' Accept/Refuse broadcasted icmp echo.'''
+ self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_echo_ignore_broadcasts', arg, 'Ignoring broadcasted icmp echo', 'Accepting broadcasted icmp echo')
+
+ def accept_bogus_error_responses(self, arg):
+ ''' Accept/Refuse bogus IPv4 error messages.'''
+ self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.icmp_ignore_bogus_error_responses', arg, 'Ignoring bogus icmp error responses', 'Accepting bogus icmp error responses')
+
+ def enable_log_strange_packets(self, arg):
+ ''' Enable/Disable the logging of IPv4 strange packets.'''
+ self.set_zero_one_variable(SYSCTLCONF, 'net.ipv4.conf.all.log_martians', arg, 'Enabling logging of strange packets', 'Disabling logging of strange packets')
+
+ def password_length(self, arg):
+ ''' Set the password minimum length and minimum number of digit and minimum number of capitalized letters.'''
+
+ try:
+ length, ndigits, nupper = arg.split(",")
+ length = int(length)
+ ndigits = int(ndigits)
+ nupper = int(nupper)
+ except:
+ self.log.error(_('Invalid password length "%s". Use "length,ndigits,nupper" as parameter') % arg)
+ return
+
+ passwd = self.configfiles.get_config_file(SYSTEM_AUTH)
+
+ val_length = val_ndigits = val_ucredit = 999999
+
+ if passwd.exists():
+ val_length = passwd.get_match(LENGTH_REGEXP, '@2')
+ if val_length:
+ val_length = int(val_length)
+
+ val_ndigits = passwd.get_match(NDIGITS_REGEXP, '@2')
+ if val_ndigits:
+ val_ndigits = int(val_ndigits)
+
+ val_ucredit = passwd.get_match(UCREDIT_REGEXP, '@2')
+ if val_ucredit:
+ val_ucredit = int(val_ucredit)
+
+ if passwd.exists() and (val_length != length or val_ndigits != ndigits or val_ucredit != nupper):
+ self.log.info(_('Setting minimum password length %d') % length)
+ (passwd.replace_line_matching(LENGTH_REGEXP,
+ '@1 minlen=%s @3' % length) or \
+ passwd.replace_line_matching('^password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*',
+ '@0 minlen=%s ' % length))
+
+ (passwd.replace_line_matching(NDIGITS_REGEXP,
+ '@1 dcredit=%s @3' % ndigits) or \
+ passwd.replace_line_matching('^password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*',
+ '@0 dcredit=%s ' % ndigits))
+
+ (passwd.replace_line_matching(UCREDIT_REGEXP,
+ '@1 ucredit=%s @3' % nupper) or \
+ passwd.replace_line_matching('^password\s+required\s+(?:/lib/security/)?pam_cracklib.so.*',
+ '@0 ucredit=%s ' % nupper))
+
+ def enable_password(self, arg):
+ ''' Use password to authenticate users. Take EXTREMELY care when disabling passwords, as it will leave the machine COMPLETELY vulnerable.'''
+ system_auth = self.configfiles.get_config_file(SYSTEM_AUTH)
+
+ val = system_auth.get_match(PASSWORD_REGEXP)
+
+ if arg == "yes":
+ if val:
+ self.log.info(_('Using password to authenticate users'))
+ system_auth.remove_line_matching(PASSWORD_REGEXP)
+ else:
+ if not val:
+ self.log.info(_('Don\'t use password to authenticate users'))
+ system_auth.replace_line_matching(PASSWORD_REGEXP, 'auth sufficient pam_permit.so') or \
+ system_auth.insert_before('auth\s+sufficient', 'auth sufficient pam_permit.so')
+
+ def password_history(self, arg):
+ ''' Set the password history length to prevent password reuse. This is not supported by pam_tcb. '''
+
+ system_auth = self.configfiles.get_config_file(SYSTEM_AUTH)
+
+ pam_tcb = system_auth.get_match(PAM_TCB_REGEXP)
+ if pam_tcb:
+ self.log.info(_('Password history not supported with pam_tcb.'))
+ return
+
+ # verify parameter validity
+ # max
+ try:
+ history = int(arg)
+ except:
+ self.log.error(_('Invalid maximum password history length: "%s"') % arg)
+ return
+
+ if system_auth.exists():
+ val = system_auth.get_match(UNIX_REGEXP, '@2')
+
+ if val and val != '':
+ val = int(val)
+ else:
+ val = 0
+ else:
+ val = 0
+
+ if history != val:
+ if history > 0:
+ self.log.info(_('Setting password history to %d.') % history)
+ system_auth.replace_line_matching(UNIX_REGEXP, '@1 remember=%d@3' % history) or \
+ system_auth.replace_line_matching('(^\s*password\s+sufficient\s+(?:/lib/security/)?pam_unix.so.*)', '@1 remember=%d' % history)
+ opasswd = self.configfiles.get_config_file(OPASSWD)
+ opasswd.exists() or opasswd.touch()
+ else:
+ self.log.info(_('Disabling password history'))
+ system_auth.replace_line_matching(UNIX_REGEXP, '@1@3')
+
+ def enable_sulogin(self, arg):
+ ''' Enable/Disable sulogin(8) in single user level.'''
+ inittab = self.configfiles.get_config_file(INITTAB)
+
+ val = inittab.get_match(SULOGIN_REGEXP)
+
+ if arg == "yes":
+ if not val:
+ self.log.info(_('Enabling sulogin in single user runlevel'))
+ inittab.replace_line_matching('[^#]+:S:', '~~:S:wait:/sbin/sulogin', 1)
+ else:
+ if val:
+ self.log.info(_('Disabling sulogin in single user runlevel'))
+ inittab.remove_line_matching('~~:S:wait:/sbin/sulogin')
+
+ # Do we need this?
+ def enable_msec_cron(self, arg):
+ ''' Enable/Disable msec hourly security check.'''
+ mseccron = self.configfiles.get_config_file(MSECCRON)
+
+ val = mseccron.exists()
+
+ if arg == "yes":
+ if not val:
+ self.log.info(_('Enabling msec periodic runs'))
+ mseccron.symlink(MSECBIN)
+ else:
+ if val:
+ self.log.info(_('Disabling msec periodic runs'))
+ mseccron.unlink()
+
+ def enable_at_crontab(self, arg):
+ ''' Enable/Disable crontab and at for users. Put allowed users in /etc/cron.allow and /etc/at.allow (see man at(1) and crontab(1)).'''
+ cronallow = self.configfiles.get_config_file(CRONALLOW)
+ atallow = self.configfiles.get_config_file(ATALLOW)
+
+ val_cronallow = cronallow.get_match('root')
+ val_atallow = atallow.get_match('root')
+
+ if arg == "yes":
+ if val_cronallow or val_atallow:
+ self.log.info(_('Enabling crontab and at'))
+ if val_cronallow:
+ cronallow.exists() and cronallow.move(SUFFIX)
+ if val_atallow:
+ atallow.exists() and atallow.move(SUFFIX)
+ else:
+ if not val_cronallow or not val_atallow:
+ self.log.info(_('Disabling crontab and at'))
+ cronallow.replace_line_matching('root', 'root', 1)
+ atallow.replace_line_matching('root', 'root', 1)
+
+ def allow_xauth_from_root(self, arg):
+ ''' Allow/forbid to export display when passing from the root account to the other users. See pam_xauth(8) for more details.'''
+ export = self.configfiles.get_config_file(EXPORT)
+
+ allow = export.get_match('^\*$')
+
+ if arg == 'yes':
+ if not allow:
+ self.log.info(_('Allowing export display from root'))
+ export.insert_at(0, '*')
+ else:
+ if allow:
+ self.log.info(_('Forbidding export display from root'))
+ export.remove_line_matching('^\*$')
+
+ def check_promisc(self, param):
+ ''' Activate/Disable ethernet cards promiscuity check.'''
+ cron = self.configfiles.get_config_file(CRON)
+
+ val = cron.get_match(CRON_REGEX)
+
+ if param == "yes":
+ if val != CRON_ENTRY:
+ self.log.info(_('Activating periodic promiscuity check'))
+ cron.replace_line_matching(CRON_REGEX, CRON_ENTRY, 1)
+ else:
+ if val:
+ self.log.info(_('Disabling periodic promiscuity check'))
+ cron.remove_line_matching('[^#]+/usr/share/msec/promisc_check.sh')
+
+ # The following checks are run from crontab. We only have these functions here
+ # to get their descriptions.
+
+ def check_security(self, param):
+ """ Enables daily security checks."""
+ pass
+
+ def check_perms(self, param):
+ """ Enables periodic permission checking for system files."""
+ pass
+
+ def check_user_files(self, param):
+ """ Enables permission checking on users' files that should not be owned by someone else, or writable."""
+ pass
+
+ def check_suid_root(self, param):
+ """ Enables checking for additions/removals of suid root files."""
+ pass
+
+ def check_suid_md5(self, param):
+ """ Enables checksum verification for suid files."""
+ pass
+
+ def check_sgid(self, param):
+ """ Enables checking for additions/removals of sgid files."""
+ pass
+
+ def check_writable(self, param):
+ """ Enables checking for files/directories writable by everybody."""
+ pass
+
+ def check_unowned(self, param):
+ """ Enables checking for unowned files."""
+ pass
+
+ def check_open_port(self, param):
+ """ Enables checking for open network ports."""
+ pass
+
+ def check_passwd(self, param):
+ """ Enables password-related checks, such as empty passwords and strange super-user accounts."""
+ pass
+
+ def check_shadow(self, param):
+ """ Enables checking for empty passwords."""
+ pass
+
+ def check_chkrootkit(self, param):
+ """ Enables checking for known rootkits using chkrootkit."""
+ pass
+
+ def check_rpm(self, param):
+ """ Enables verification of installed packages."""
+ pass
+
+ def tty_warn(self, param):
+ """ Enables periodic security check results to terminal."""
+ pass
+
+ def mail_warn(self, param):
+ """ Enables security results submission by email."""
+ pass
+
+ def mail_empty_content(self, param):
+ """ Enables sending of empty mail reports."""
+ pass
+
+ def syslog_warn(self, param):
+ """ Enables logging to system log."""
+ pass
+
+ def mail_user(self, param):
+ """ Defines email to receive security notifications."""
+ pass
+
+ def check_shosts(self, param):
+ """ Enables checking for dangerous options in users' .rhosts/.shosts files."""
+ pass
+# }}}
+
+# {{{ PERMS - permissions handling
+class PERMS:
+ """Permission checking/enforcing."""
+ def __init__(self, log):
+ """Initializes internal variables"""
+ self.log = log
+ self.USER = {}
+ self.GROUP = {}
+ self.USERID = {}
+ self.GROUPID = {}
+ self.files = {}
+ self.fs_regexp = self.build_non_localfs_regexp()
+
+ def get_user_id(self, name):
+ '''Caches and retreives user id correspondent to name'''
+ try:
+ return self.USER[name]
+ except KeyError:
+ try:
+ self.USER[name] = pwd.getpwnam(name)[2]
+ except KeyError:
+ error(_('user name %s not found') % name)
+ self.USER[name] = -1
+ return self.USER[name]
+
+ def get_user_name(self, id):
+ '''Caches and retreives user name correspondent to id'''
+ try:
+ return self.USERID[id]
+ except KeyError:
+ try:
+ self.USERID[id] = pwd.getpwuid(id)[0]
+ except KeyError:
+ error(_('user name not found for id %d') % id)
+ self.USERID[id] = str(id)
+ return self.USERID[id]
+
+ def get_group_id(self, name):
+ '''Caches and retreives group id correspondent to name'''
+ try:
+ return self.GROUP[name]
+ except KeyError:
+ try:
+ self.GROUP[name] = grp.getgrnam(name)[2]
+ except KeyError:
+ error(_('group name %s not found') % name)
+ self.GROUP[name] = -1
+ return self.GROUP[name]
+
+ def get_group_name(self, id):
+ '''Caches and retreives group name correspondent to id'''
+ try:
+ return self.GROUPID[id]
+ except KeyError:
+ try:
+ self.GROUPID[id] = grp.getgrgid(id)[0]
+ except KeyError:
+ error(_('group name not found for id %d') % id)
+ self.GROUPID[id] = str(id)
+ return self.GROUPID[id]
+
+ def build_non_localfs_regexp(self,
+ non_localfs = ['nfs', 'codafs', 'smbfs', 'cifs', 'autofs']):
+ """Build a regexp that matches all the non local filesystems"""
+ try:
+ file = open('/proc/mounts', 'r')
+ except IOError:
+ self.log.error(_('Unable to check /proc/mounts. Assuming all file systems are local.'))
+ return None
+
+ regexp = None
+
+ for line in file.readlines():
+ fields = string.split(line)
+ if fields[2] in non_localfs:
+ if regexp:
+ regexp = regexp + '|' + fields[1]
+ else:
+ regexp = '^(' + fields[1]
+
+ file.close()
+
+ if not regexp:
+ return None
+ else:
+ return re.compile(regexp + ')')
+
+ def commit(self, really_commit=True, enforce=False):
+ """Commits changes.
+ If enforce is True, the permissions on all files are enforced."""
+ if not really_commit:
+ self.log.info(_("In check-only mode, nothing is written back to disk."))
+
+ if len(self.files) > 0:
+ self.log.info("%s: %s" % (config.MODIFICATIONS_FOUND, " ".join(self.files)))
+ else:
+ self.log.info(config.MODIFICATIONS_NOT_FOUND)
+
+
+ for file in self.files:
+ newperm, newuser, newgroup, force = self.files[file]
+ # are we in enforcing mode?
+ if enforce:
+ force = True
+
+ if newuser != None:
+ self.log.info(_("Enforcing user on %s to %s") % (file, self.get_user_name(newuser)))
+ if force and really_commit:
+ try:
+ os.chown(file, newuser, -1)
+ except:
+ self.log.error(_("Error changing user on %s: %s") % (file, sys.exc_value))
+ if newgroup != None:
+ self.log.info(_("Enforcing group on %s to %s") % (file, self.get_group_name(newgroup)))
+ if force and really_commit:
+ try:
+ os.chown(file, -1, newgroup)
+ except:
+ self.log.error(_("Error changing group on %s: %s") % (file, sys.exc_value))
+ # permissions should be last, as chown resets them
+ # on suid files
+ if newperm != None:
+ self.log.info(_("Enforcing permissions on %s to %o") % (file, newperm))
+ if force and really_commit:
+ try:
+ os.chmod(file, newperm)
+ except:
+ self.log.error(_("Error changing permissions on %s: %s") % (file, sys.exc_value))
+
+
+ def check_perms(self, perms):
+ '''Checks permissions for all entries in perms (PermConfig).'''
+
+ for file in perms.list_options():
+ user_s, group_s, perm_s, force = perms.get(file)
+
+ # permission
+ if perm_s == 'current':
+ perm = -1
+ else:
+ try:
+ perm = int(perm_s, 8)
+ except ValueError:
+ self.log.error(_("bad permissions for '%s': '%s'") % (file, perm_s))
+ continue
+
+ # user
+ if user_s == 'current':
+ user = -1
+ else:
+ user = self.get_user_id(user_s)
+
+ # group
+ if group_s == 'current':
+ group = -1
+ else:
+ group = self.get_group_id(group_s)
+
+ # now check the permissions
+ for f in glob.glob(file):
+ # get file properties
+ f = os.path.realpath(f)
+ try:
+ full = os.lstat(f)
+ except OSError:
+ continue
+
+ if self.fs_regexp and self.fs_regexp.search(f):
+ self.log.info(_('Non local file: "%s". Nothing changed.') % fields[0])
+ continue
+
+ curperm = perm
+ mode = stat.S_IMODE(full[stat.ST_MODE])
+
+ if perm != -1 and stat.S_ISDIR(full[stat.ST_MODE]):
+ if curperm & 0400:
+ curperm = curperm | 0100
+ if curperm & 0040:
+ curperm = curperm | 0010
+ if curperm & 0004:
+ curperm = curperm | 0001
+
+ curuser = full[stat.ST_UID]
+ curgroup = full[stat.ST_GID]
+ curperm = mode
+ # checking for subdirectory permissions
+ if f != '/' and f[-1] == '/':
+ f = f[:-1]
+ if f[-2:] == '/.':
+ f = f[:-2]
+ # check for changes
+ newperm = None
+ newuser = None
+ newgroup = None
+ if perm != -1 and perm != curperm:
+ newperm = perm
+ if user != -1 and user != curuser:
+ newuser = user
+ if group != -1 and group != curgroup:
+ newgroup = group
+ if newperm != None or newuser != None or newgroup != None:
+ self.files[f] = (newperm, newuser, newgroup, force)
+ self.log.debug("Updating %s (matched by '%s')" % (f, file))
+ else:
+ # see if any other rule put this file into the list
+ if f in self.files:
+ self.log.debug("Removing previously selected %s (matched by '%s')" % (f, file))
+ del self.files[f]
+ return self.files
+# }}}
+
+
+if __name__ == "__main__":
+ # this should never ever be run directly
+ print >>sys.stderr, """This file should not be run directly."""
+
diff --git a/src/msec/man.py b/src/msec/man.py
new file mode 100755
index 0000000..ce8fb33
--- /dev/null
+++ b/src/msec/man.py
@@ -0,0 +1,197 @@
+#!/usr/bin/python
+#---------------------------------------------------------------
+# Project : Mandriva Linux
+# Module : share
+# File : man.py
+# Version : $Id$
+# Author : Frederic Lepied
+# Created On : Sat Jan 26 17:38:39 2002
+# Purpose : loads a python module and creates a man page from
+# the doc strings of the functions.
+#---------------------------------------------------------------
+
+import sys
+import imp
+import inspect
+
+import config
+from libmsec import MSEC, Log
+try:
+ from version import version
+except:
+ version = "(development version)"
+
+header = '''.ds q \N'34'
+.TH msec %s msec "Mandriva Linux"
+.SH NAME
+msec \- Mandriva Linux security tools
+.SH SYNOPSIS
+.nf
+.B msec [options]
+.B msecperms [options]
+.B msecgui [options]
+.fi
+.SH DESCRIPTION
+.B msec
+is responsible to maintain system security in Mandriva. It supports different security
+configurations, which can be organized into several security levels. Currently, three
+preconfigured security levels are provided:
+
+.TP
+\\fBnone\\fR
+this level aims to provide the most basic security. It should be used when you want to
+manage all aspects of system security on your own.
+
+.TP
+\\fBdefault\\fR
+this is the default security level, which configures a reasonably safe set of security
+features. It activates several periodic system checks, and sends the results of their
+execution by email (by default, the local 'root' account is used).
+
+.TP
+\\fBsecure\\fR
+this level is configured to provide maximum system security, even at the cost of limiting
+the remote access to the system, and local user permissions. It also runs a wider set of
+periodic checks, enforces the local password settings, and periodically checks if the
+system security settings, configured by msec, were modified directly or by some other
+application.
+
+.PP
+
+The security settings are stored in \\fB/etc/security/msec/security.conf\\fR
+file, and default settings for each predefined level are stored in
+\\fB/etc/security/msec/level.LEVEL\\fR. Permissions for files and directories
+that should be enforced or checked for changes are stored in
+\\fB/etc/security/msec/perms.conf\\fR, and default permissions for each
+predefined level are stored in \\fB/etc/security/msec/perm.LEVEL\\fR. Note
+that user-modified parameters take precedence over default level settings. For
+example, when default level configuration forbids direct root logins, this
+setting can be overridden by the user.
+
+.PP
+
+The following options are supported by msec applications:
+
+.TP
+\\fBmsec\\fR:
+.PP
+
+This is the console version of msec. It is responsible for system security configuration
+and checking and transitions between security levels.
+
+When executed without parameters, msec will read the system configuration file
+(/etc/security/msec/security.conf), and enforce the specified security
+settings. The operations are logged to \\fB/var/log/msec.log\\fP file, and also
+to syslog, using \\fBLOG_AUTHPRIV\\fR facility. Please note that msec should
+by run as root.
+
+\\fB\-h, --help\\fR
+ This option will display the list of supported command line options.
+
+\\fB\-l, --level <level>\\fR
+ List the default configuration for given security level.
+
+\\fB\-f, --force <level>\\fR
+ Apply the specified security level to the system, overwritting all
+local changes. This is necessary to initialize a security level, either on first
+install, on when a change to a different level is required.
+
+\\fB\-d\\fR
+ Enable debugging messages.
+
+\\fB\-p, --pretend\\fR
+ Verify the actions that will be performed by msec, without actually
+doing anything to the system. In this mode of operation, msec performs all the
+required tasks, except effectively writting data back to disk.
+
+.TP
+\\fBmsecperms\\fR:
+.PP
+
+This application is responsible for system permission checking and enforcements.
+
+When executed without parameters, msecperms will read the permissions
+configuration file (/etc/security/msec/perms.conf), and enforce the specified
+security settings. The operations are logged to \\fB/var/log/msec.log\\fP file,
+and also to syslog, using \\fBLOG_AUTHPRIV\\fR facility. Please note that msecperms
+should by run as root.
+
+\\fB\-h, --help\\fR
+ This option will display the list of supported command line options.
+
+\\fB\-l, --level <level>\\fR
+ List the default configuration for given security level.
+
+\\fB\-f, --force <level>\\fR
+ Apply the specified security level to the system, overwritting all
+local changes. This is necessary to initialize a security level, either on first
+install, on when a change to a different level is required.
+
+\\fB\-e, --enforce\\fR
+ Enforce the default permissions on all files.
+
+\\fB\-d\\fR
+ Enable debugging messages.
+
+\\fB\-p, --pretend\\fR
+ Verify the actions that will be performed by msec, without actually
+doing anything to the system. In this mode of operation, msec performs all the
+required tasks, except effectively writting data back to disk.
+
+.TP
+\\fBmsecgui\\fR:
+.PP
+
+This is the GTK version of msec. It acts as frontend to all msec functionalities.
+
+\\fB\-h, --help\\fR
+ This option will display the list of supported command line options.
+
+\\fB\-d\\fR
+ Enable debugging messages.
+
+.SH "SECURITY OPTIONS"
+
+The following security options are supported by msec:
+
+''' % version
+
+footer = '''.RE
+.SH NOTES
+Msec applications must be run by root.
+.SH AUTHORS
+Frederic Lepied <flepied@mandriva.com>
+
+Eugeni Dodonov <eugeni@mandriva.com>
+'''
+
+### strings used in the rewritting
+function_str = '''
+.TP 4
+.B \\fI%s\\fP
+%s
+
+MSEC parameter: \\fI%s\\fP
+
+Accepted values: \\fI%s\\fP
+'''
+
+### code
+
+# process all configuration parameters
+log = Log(log_syslog=False, log_file=False)
+msec = MSEC(log)
+
+#print >>sys.stderr, dir(msec.create_server_link)
+
+print header
+
+for variable in config.SETTINGS:
+ callback, params = config.SETTINGS[variable]
+ func = msec.get_action(callback)
+ if func:
+ print function_str % (callback, func.__doc__.strip(), variable, ", ".join(params))
+
+print footer
+
+# man.py ends here
diff --git a/src/msec/msec b/src/msec/msec
new file mode 100755
index 0000000..48527bb
--- /dev/null
+++ b/src/msec/msec
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Wrapper for msec.py
+#
+
+if [ "`whoami`" != "root" ]; then
+ echo 'msec: sorry, you must be root !'
+ exit 1
+fi
+
+LCK=/var/run/msec.pid
+
+function cleanup() {
+ rm -f $LCK
+}
+
+if [ -f $LCK ]; then
+ if [ -d /proc/`cat $LCK` ]; then
+ exit 0
+ else
+ rm -f $LCK
+ fi
+fi
+
+echo -n $$ > $LCK
+
+trap cleanup 0
+
+MSEC=/usr/share/msec/msec.py
+OPT="$@"
+
+$MSEC $OPT
+
+# msec ends here
diff --git a/src/msec/msec.py b/src/msec/msec.py
new file mode 100755
index 0000000..010f9ce
--- /dev/null
+++ b/src/msec/msec.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python -O
+"""This is the main msec module.
+It checks/sets the security levels, configures security variables,
+and works as a frontend to libmsec.
+"""
+
+import sys
+import os
+import string
+import getopt
+import gettext
+import imp
+import re
+
+# config
+import config
+
+# version
+try:
+ from version import version
+except:
+ version = "development version"
+
+# libmsec
+from libmsec import MSEC, Log
+
+import logging
+
+# localization
+try:
+ cat = gettext.Catalog('msec')
+ _ = cat.gettext
+except IOError:
+ _ = str
+
+# {{{ usage
+def usage():
+ """Prints help message"""
+ print """Msec: Mandriva Security Center (%s).
+
+When run without parameters, msec will read the configuration from
+/etc/security/msec/msec.conf, and enforce the specified security settings.
+If no configuration file is found on the system, a default configuration
+will be created.
+
+Arguments to msec:
+ -h, --help displays this helpful message.
+ -l, --level <level> displays configuration for specified security
+ level.
+ -f, --force <level> force new level, overwriting user settings.
+ -d enable debugging messages.
+ -p, --pretend only pretend to change the level, perform no real
+ actions. Use this to see what operations msec
+ will perform.
+""" % version
+# }}}
+
+if __name__ == "__main__":
+ # default options
+ force_level = False
+ log_level = logging.INFO
+ commit = True
+ level = config.DEFAULT_LEVEL
+
+ # parse command line
+ try:
+ opt, args = getopt.getopt(sys.argv[1:], 'hl:f:dp', ['help', 'list', 'force', 'debug', 'pretend'])
+ except getopt.error:
+ usage()
+ sys.exit(1)
+ for o in opt:
+ # help
+ if o[0] == '-h' or o[0] == '--help':
+ usage()
+ sys.exit(0)
+ # list
+ elif o[0] == '-l' or o[0] == '--list':
+ level = o[1]
+ log = Log(interactive=True, log_syslog=False, log_file=False)
+ levelconf = config.load_defaults(log, level)
+ params = levelconf.list_options()
+ if not params:
+ print >>sys.stderr, _("Invalid security level '%s'.") % level
+ sys.exit(1)
+ for item in params:
+ print "%s=%s" % (item, levelconf.get(item) )
+ sys.exit(0)
+ # force new level
+ elif o[0] == '-f' or o[0] == '--force':
+ level = o[1]
+ force_level = True
+ # debugging
+ elif o[0] == '-d' or o[0] == '--debug':
+ log_level = logging.DEBUG
+ # check-only mode
+ elif o[0] == '-p' or o[0] == '--pretend':
+ commit = False
+
+ # verifying use id
+ if os.geteuid() != 0:
+ print >>sys.stderr, _("Msec: Mandriva Security Center (%s)\n") % version
+ print >>sys.stderr, _("Error: This application must be executed by root!")
+ print >>sys.stderr, _("Run with --help to get help.")
+ sys.exit(1)
+
+ # configuring logging
+ interactive = sys.stdin.isatty()
+ if interactive:
+ # logs to file and to terminal
+ log = Log(log_path=config.SECURITYLOG, interactive=True, log_syslog=False, log_level=log_level)
+ else:
+ log = Log(log_path=config.SECURITYLOG, interactive=False, log_level=log_level)
+
+ # loading initial config
+ msec_config = config.MsecConfig(log, config=config.SECURITYCONF)
+ if not msec_config.load() and not force_level:
+ log.error(_("Level configuration not found, please run '%s -f <level>' to initialize.") % sys.argv[0])
+
+ # forcing new level
+ if force_level:
+ # first load the default configuration for level
+ levelconf = config.load_defaults(log, level)
+ params = levelconf.list_options()
+ if not params:
+ log.error(_("Default configuration for level '%s' not found, aborting.") % level)
+ sys.exit(1)
+ for opt in params:
+ msec_config.set(opt, levelconf.get(opt))
+
+ # load the msec library
+ msec = MSEC(log)
+
+ # apply the config to msec
+ msec.apply(msec_config)
+ # writing back changes
+ msec.commit(commit)
+ # saving updated config
+ if force_level and commit:
+ if not msec_config.save():
+ log.error(_("Unable to save config!"))
+ sys.exit(0)
diff --git a/src/msec/msecgui b/src/msec/msecgui
new file mode 100755
index 0000000..843eed0
--- /dev/null
+++ b/src/msec/msecgui
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Wrapper for msecgui
+#
+
+if [ "`whoami`" != "root" ]; then
+ echo 'msec: sorry, you must be root !'
+ exit 1
+fi
+
+LCK=/var/run/msec.pid
+
+function cleanup() {
+ rm -f $LCK
+}
+
+if [ -f $LCK ]; then
+ if [ -d /proc/`cat $LCK` ]; then
+ exit 0
+ else
+ rm -f $LCK
+ fi
+fi
+
+echo -n $$ > $LCK
+
+trap cleanup 0
+
+MSEC=/usr/share/msec/msecgui.py
+OPT="$@"
+
+$MSEC $OPT
+
+# msec ends here
diff --git a/src/msec/msecgui.py b/src/msec/msecgui.py
new file mode 100755
index 0000000..3e86506
--- /dev/null
+++ b/src/msec/msecgui.py
@@ -0,0 +1,725 @@
+#!/usr/bin/python -O
+"""
+This is graphical frontend to msec.
+"""
+
+import os
+import sys
+import string
+import getopt
+
+# PyGTK
+import gtk
+#import gtk.glade
+import pygtk
+import gobject
+import pango
+
+# config
+import config
+
+# version
+try:
+ from version import version
+except:
+ version = "development version"
+
+# libmsec
+from libmsec import MSEC, Log
+
+import logging
+
+# localization
+import gettext
+try:
+ cat = gettext.Catalog('msec')
+ _ = cat.gettext
+except IOError:
+ _ = str
+
+# localized help
+try:
+ from help import HELP
+except:
+ HELP = {}
+
+# text strings
+BASIC_SECURITY_TEXT=_("""Basic security options.
+
+These options control the basic aspects of system security. You may select
+a pre-defined profile, or customize the options.
+
+The following security profiles are defined in this version:
+
+ - <b>None</b>: this profile disables additional system security, and it should
+ be used when you want to fine-tune the system on your own.
+
+ - <b>Default</b>: this is the default profile, which configures a reasonably
+ safe set of security features. It activates several periodic system checks,
+ and mails their results daily to the selected email (by default, the local
+ 'root' account is used to receive such emails).
+
+ - <b>Secure</b>: this profile is configured to provide maximum security, even
+ at the cost of limiting the remote access to the system. It also runs a wider
+ set of periodic checks, enforces the local password settings, and periodically
+ checks if the system security settings, configured here, were modified.
+""")
+
+SYSTEM_SECURITY_TEXT=_("""System security options.
+
+These options control the local security configuration, such as the login restrictions,
+password configurations, integration with other security tools, and default file creation
+permissions.
+""")
+
+NETWORK_SECURITY_TEXT=_("""Network security options.
+
+These options define the network security agains remote treats, unauthorized accesses,
+and breakin attempts.
+""")
+
+PERIODIC_SECURITY_TEXT=_("""Periodic security checks.
+
+These options configure the security checks that should be executed periodically.
+""")
+
+NOTIFICATIONS_TEXT=_("""Security notifications.
+
+This page allows to configure the different ways the security notifications can be
+delivered.
+
+It is possible to receive notifications by e-mail, using syslog, using an exclusive
+log file, or using desktop environment notification system.
+""")
+
+PERMISSIONS_SECURITY_TEXT=_("""File permissions.
+
+These options allow to fine-tune system permissions for important files and directores.
+
+The following permissions are checked periodically, and any change to the owner, group,
+or current permission is reported. The permissions can be enforced, automatically
+changing them to the specified values when a change is detected.
+""")
+
+class MsecGui:
+ """Msec GUI"""
+ # common columns
+ (COLUMN_OPTION, COLUMN_DESCR, COLUMN_VALUE) = range(3)
+ (COLUMN_PATH, COLUMN_USER, COLUMN_GROUP, COLUMN_PERM, COLUMN_FORCE) = range(5)
+
+ def __init__(self, log, msec, config, perms):
+ """Initializes gui"""
+ self.log = log
+ self.msec = msec
+ self.config = config
+ # save original configuration
+ self.oldconfig = {}
+ for opt in config.list_options():
+ self.oldconfig[opt] = config.get(opt)
+ self.perms = perms
+ self.window = gtk.Window()
+ self.window.set_default_size(640, 480)
+ self.window.connect('destroy', self.quit)
+
+ # are we enforcing a level
+ self.enforced_level = None
+ self.enforcing_level = False
+
+ main_vbox = gtk.VBox(homogeneous=False, spacing=5)
+ self.window.add(main_vbox)
+
+ # main frame
+ frame = gtk.Frame()
+ main_vbox.pack_start(frame)
+
+ # notebook
+ self.notebook = gtk.Notebook()
+ frame.add(self.notebook)
+
+ self.notebook.append_page(self.basic_security_page(), gtk.Label(_("Basic security")))
+ self.notebook.append_page(self.system_security_page(), gtk.Label(_("System security")))
+ self.notebook.append_page(self.network_security_page(), gtk.Label(_("Network security")))
+ self.notebook.append_page(self.periodic_security_page(), gtk.Label(_("Periodic checks")))
+ self.notebook.append_page(self.notifications_page(), gtk.Label(_("Security notifications")))
+ self.notebook.append_page(self.permissions_security_page(), gtk.Label(_("Permissions")))
+
+ # control hbox
+ hbox = gtk.HBox(homogeneous=False, spacing=10)
+ main_vbox.pack_start(hbox, False, False)
+
+ # control buttons
+ # TODO: improve spacing
+ cancel = gtk.Button(gtk.STOCK_CANCEL)
+ cancel.set_use_stock(True)
+ cancel.connect('clicked', self.cancel)
+ hbox.pack_start(cancel, expand=True, fill=True)
+ help = gtk.Button(gtk.STOCK_HELP)
+ help.set_use_stock(True)
+ help.connect('clicked', self.help)
+ hbox.pack_start(help, expand=True, fill=True)
+ ok = gtk.Button(gtk.STOCK_OK)
+ ok.set_use_stock(True)
+ ok.connect('clicked', self.ok)
+ hbox.pack_start(ok, expand=True, fill=True)
+
+ self.window.show_all()
+
+ def cancel(self, widget):
+ """Cancel button"""
+ print "Cancel clicked."
+ self.quit(widget)
+
+ def help(self, widget):
+ """Help button"""
+ print "Help clicked."
+
+ def ok(self, widget):
+ """Ok button"""
+ print "Ok clicked."
+ # first, let's reset previous msec data
+ self.msec.reset()
+ # start buffered logging
+ self.log.start_buffer()
+ # are we enforcing a level?
+ if self.enforcing_level:
+ print ">> Enforcing level %s" % self.enforced_level
+ curconfig = config.load_defaults(self.log, self.enforced_level)
+ else:
+ curconfig = self.config
+ # apply config and preview changes
+ self.msec.apply(curconfig)
+ msec.commit(False)
+ messages = self.log.get_buffer()
+
+ # creating preview window
+ dialog = gtk.Dialog(_("Preview changes"),
+ self.window, 0,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OK, gtk.RESPONSE_OK)
+ )
+ sw = gtk.ScrolledWindow()
+ sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ dialog.vbox.add(sw)
+
+ vbox = gtk.VBox()
+ dialog.vbox.set_size_request(640, 300)
+ sw.add_with_viewport(vbox)
+ label = gtk.Label(_("Click OK to commit changes, or CANCEL to leave current configuration unmodified."))
+ vbox.pack_start(label, False, False)
+
+ # informative label
+ label = gtk.Label(_('<b>New msec configuration:</b>'))
+ label.set_use_markup(True)
+ vbox.pack_start(label, False, False)
+
+ # check for changed options
+ opt_changes = []
+ for opt in self.oldconfig:
+ if curconfig.get(opt) != self.oldconfig[opt]:
+ opt_changes.append(opt)
+
+ if len(opt_changes) > 0:
+ # some configuration parameters were changed
+ label = gtk.Label(_('<b>MSEC option changed:</b> <i>%s</i>\n') % ", ".join(opt_changes))
+ label.set_use_markup(True)
+ label.set_line_wrap(True)
+ vbox.pack_start(label, False, False)
+ else:
+ label = gtk.Label(_('<b>No changes in MSEC options.</b>'))
+ label.set_use_markup(True)
+ vbox.pack_start(label, False, False)
+
+ # see if there were any changes to system files
+ for msg in messages['info']:
+ if msg.find(config.MODIFICATIONS_FOUND) != -1 or msg.find(config.MODIFICATIONS_NOT_FOUND) != -1:
+ label = gtk.Label('<i>%s</i>' % msg)
+ label.set_line_wrap(True)
+ label.set_use_markup(True)
+ vbox.pack_start(label, False, False)
+ break
+ # a separator
+ vbox.pack_start(gtk.HSeparator(), False, False)
+ # adding specific messages
+ for cat in ['info', 'critical', 'error', 'warn', 'debug']:
+ msgs = messages[cat]
+ expander = gtk.Expander(_('Verbose information (%s): %d') % (cat, len(msgs)))
+ textview = gtk.TextView()
+ textview.set_wrap_mode(gtk.WRAP_WORD_CHAR)
+ textview.set_editable(False)
+ expander.add(textview)
+ count = 1
+ for msg in msgs:
+ buffer = textview.get_buffer()
+ end = buffer.get_end_iter()
+ buffer.insert(end, "%d: %s\n" % (count, msg))
+ count += 1
+ vbox.pack_start(expander, False, False)
+
+ dialog.show_all()
+ response = dialog.run()
+ if response != gtk.RESPONSE_OK:
+ dialog.destroy()
+ return
+ dialog.destroy()
+
+ # well, let's commit it!
+ if self.enforcing_level:
+ # rewriting configuration
+ for opt in curconfig.list_options():
+ self.config.set(opt, curconfig.get(opt))
+ # saving the configuration
+ self.config.save()
+ msec.apply(self.config)
+ msec.commit(True)
+ self.quit(widget)
+
+ def create_treeview(self, options):
+ """Creates a treeview from given list of options"""
+ sw = gtk.ScrolledWindow()
+ sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+
+ # list of options
+ lstore = gtk.ListStore(
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING)
+
+ # treeview
+ treeview = gtk.TreeView(lstore)
+ treeview.set_rules_hint(True)
+ treeview.set_search_column(self.COLUMN_DESCR)
+
+ treeview.connect('row-activated', self.option_changed, lstore)
+
+ # configuring columns
+
+ # column for option names
+ column = gtk.TreeViewColumn(_('Security Option'), gtk.CellRendererText(), text=self.COLUMN_OPTION)
+ column.set_sort_column_id(self.COLUMN_OPTION)
+ treeview.append_column(column)
+
+ # column for descriptions
+ renderer = gtk.CellRendererText()
+ renderer.set_property('wrap-width', 400)
+ renderer.set_property('wrap-mode', pango.WRAP_WORD_CHAR)
+ column = gtk.TreeViewColumn(_('Description'), renderer, text=self.COLUMN_DESCR)
+ column.set_sort_column_id(self.COLUMN_DESCR)
+ treeview.append_column(column)
+
+ # column for values
+ column = gtk.TreeViewColumn(_('Value'), gtk.CellRendererText(), text=self.COLUMN_VALUE)
+ column.set_sort_column_id(self.COLUMN_VALUE)
+ treeview.append_column(column)
+
+ sw.add(treeview)
+
+ for option in options:
+ # retreiving option description
+ if not config.SETTINGS.has_key(option):
+ # invalid option
+ self.log.error(_("Invalid option '%s'!") % option)
+ continue
+ # getting level settings, callback and valid params
+ callback, params = config.SETTINGS[option]
+ # getting the function description
+ if option in HELP:
+ self.log.debug("found localized help for %s" % option)
+ doc = HELP[option]
+ else:
+ # get description from function comments
+ func = msec.get_action(callback)
+ if func:
+ doc = func.__doc__.strip()
+ else:
+ doc = callback
+
+ # now for the value
+ value = self.config.get(option)
+
+ # building the option
+ iter = lstore.append()
+ lstore.set(iter,
+ self.COLUMN_OPTION, option,
+ self.COLUMN_DESCR, doc,
+ self.COLUMN_VALUE, value,
+ )
+ return sw
+
+ def basic_security_page(self):
+ """Builds the basic security page"""
+ vbox = gtk.VBox(homogeneous=False)
+
+ # security levels
+
+ levels = config.SECURITY_LEVELS
+
+ entry = gtk.Label(BASIC_SECURITY_TEXT)
+ entry.set_use_markup(True)
+ vbox.pack_start(entry, False, False)
+
+ # Are we enforcing a new security level
+ entry = gtk.CheckButton(_("Enforce a new security level"))
+
+ # security levels
+ frame = gtk.Frame()
+ frame.set_sensitive(False)
+ levels_vbox = gtk.VBox()
+ frame.add(levels_vbox)
+ # none
+ button = gtk.RadioButton(group=None, label=_("Pre-defined security level: NONE"))
+ button.connect('clicked', self.force_level, 'none')
+ levels_vbox.pack_start(button)
+ # default
+ button = gtk.RadioButton(group=button, label=_("Pre-defined security level: DEFAULT"))
+ button.connect('clicked', self.force_level, 'default')
+ button.set_active(True)
+ levels_vbox.pack_start(button)
+ # secure
+ button = gtk.RadioButton(group=button, label=_("Pre-defined security level: SECURE"))
+ button.connect('clicked', self.force_level, 'secure')
+ levels_vbox.pack_start(button)
+
+ # adding callback for enable button
+ entry.connect('clicked', self.enforce_level, frame)
+ vbox.pack_start(entry, False, False)
+ # putting levels to vbox
+ vbox.pack_start(frame)
+
+ return vbox
+
+ def enforce_level(self, widget, options):
+ """Enforces a new security level"""
+ frame = options
+ if widget.get_active():
+ # we are enforcing a level
+ self.enforcing_level = True
+ frame.set_sensitive(True)
+ # disable notebook pages
+ npages = self.notebook.get_n_pages()
+ for page in range(1, npages):
+ curpage = self.notebook.get_nth_page(page)
+ curpage.set_sensitive(False)
+ label = self.notebook.get_tab_label(curpage)
+ label.set_sensitive(False)
+ else:
+ frame.set_sensitive(False)
+ # enable notebook pages
+ npages = self.notebook.get_n_pages()
+ for page in range(1, npages):
+ curpage = self.notebook.get_nth_page(page)
+ curpage.set_sensitive(True)
+ label = self.notebook.get_tab_label(curpage)
+ label.set_sensitive(True)
+ # disable level enforcing
+ self.enforcing_level = False
+
+ def force_level(self, widget, level):
+ """Defines a given security level"""
+ if widget.get_active():
+ self.enforced_level = level
+ print level
+
+ def notifications_page(self):
+ """Builds the notifications page"""
+ vbox = gtk.VBox(homogeneous=False)
+
+ # security levels
+
+ entry = gtk.Label(NOTIFICATIONS_TEXT)
+ entry.set_use_markup(True)
+ vbox.pack_start(entry, False, False)
+
+ # basic security options
+ options_view = self.create_treeview(["TTY_WARN", "SYSLOG_WARN", "NOTIFY_WARN", "MAIL_WARN", "MAIL_USER", "MAIL_EMPTY_CONTENT"])
+ vbox.pack_start(options_view)
+
+ return vbox
+
+ def system_security_page(self):
+ """Builds the network security page"""
+ vbox = gtk.VBox(homogeneous=False)
+
+ entry = gtk.Label(SYSTEM_SECURITY_TEXT)
+ entry.set_use_markup(True)
+ vbox.pack_start(entry, False, False)
+
+ # system security options
+ options_view = self.create_treeview(["ENABLE_APPARMOR", "ENABLE_POLICYKIT",
+ "ENABLE_SUDO", "ENABLE_MSEC_CRON", "ENABLE_PAM_WHEEL_FOR_SU",
+ "ENABLE_SULOGIN", "CREATE_SERVER_LINK", "ENABLE_AT_CRONTAB",
+ "ALLOW_ROOT_LOGIN", "ALLOW_USER_LIST", "ENABLE_PASSWORD",
+ "ALLOW_AUTOLOGIN", "ENABLE_CONSOLE_LOG",
+ "ENABLE_PAM_WHEEL_FOR_SU", "CREATE_SERVER_LINK", "ALLOW_XAUTH_FROM_ROOT",
+ "ALLOW_REBOOT", "SHELL_HISTORY_SIZE", "SHELL_TIMEOUT", "PASSWORD_LENGTH",
+ "PASSWORD_HISTORY", "USER_UMASK", "ROOT_UMASK",
+ ])
+ vbox.pack_start(options_view)
+
+ return vbox
+
+ def network_security_page(self):
+ """Builds the network security page"""
+ vbox = gtk.VBox(homogeneous=False)
+
+ entry = gtk.Label(NETWORK_SECURITY_TEXT)
+ entry.set_use_markup(True)
+ vbox.pack_start(entry, False, False)
+
+ # network security options
+ options_view = self.create_treeview(["ACCEPT_BOGUS_ERROR_RESPONSES", "ACCEPT_BROADCASTED_ICMP_ECHO",
+ "ACCEPT_ICMP_ECHO", "ALLOW_REMOTE_ROOT_LOGIN",
+ "ALLOW_X_CONNECTIONS", "ALLOW_XSERVER_TO_LISTEN",
+ "AUTHORIZE_SERVICES", "ENABLE_DNS_SPOOFING_PROTECTION",
+ "ENABLE_IP_SPOOFING_PROTECTION", "ENABLE_LOG_STRANGE_PACKETS",
+ ])
+ vbox.pack_start(options_view)
+
+ return vbox
+
+ def periodic_security_page(self):
+ """Builds the network security page"""
+ vbox = gtk.VBox(homogeneous=False)
+
+ entry = gtk.Label(PERIODIC_SECURITY_TEXT)
+ vbox.pack_start(entry, False, False)
+
+ self.periodic_checks = gtk.CheckButton(_("Enable periodic security checks"))
+ if self.config.get("CHECK_SECURITY") == "yes":
+ self.periodic_checks.set_active(True)
+ vbox.pack_start(self.periodic_checks, False, False)
+
+ # network security options
+ options_view = self.create_treeview(["CHECK_PERMS", "CHECK_USER_FILES", "CHECK_SUID_ROOT", "CHECK_SUID_MD5",
+ "CHECK_SGID", "CHECK_WRITABLE", "CHECK_UNOWNED",
+ "CHECK_PROMISC", "CHECK_OPEN_PORT", "CHECK_PASSWD",
+ "CHECK_SHADOW", "CHECK_CHKROOTKIT", "CHECK_RPM",
+ "CHECK_SHOSTS"
+ ])
+ vbox.pack_start(options_view)
+
+ # see if these tests are enabled
+ self.periodic_checks.connect('clicked', self.periodic_tests, options_view)
+ periodic_checks = self.config.get("CHECK_SECURITY")
+ if periodic_checks == 'no':
+ # disable all periodic tests
+ options_view.set_sensitive(False)
+
+ return vbox
+
+ def periodic_tests(self, widget, options):
+ '''Enables/disables periodic security tests.'''
+ status = widget.get_active()
+ if status:
+ self.config.set("CHECK_SECURITY", "yes")
+ options.set_sensitive(True)
+ else:
+ self.config.set("CHECK_SECURITY", "no")
+ options.set_sensitive(False)
+
+ def permissions_security_page(self):
+ """Builds the network security page"""
+ vbox = gtk.VBox(homogeneous=False)
+
+ entry = gtk.Label(PERMISSIONS_SECURITY_TEXT)
+ vbox.pack_start(entry, False, False)
+
+ sw = gtk.ScrolledWindow()
+ sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+
+ # list of options
+ lstore = gtk.ListStore(
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_BOOLEAN)
+
+ # treeview
+ treeview = gtk.TreeView(lstore)
+ treeview.set_rules_hint(True)
+ treeview.set_search_column(self.COLUMN_DESCR)
+
+ # TODO: fix
+ treeview.connect('row-activated', self.option_changed, lstore)
+
+ # configuring columns
+
+ # column for path mask
+ column = gtk.TreeViewColumn(_('Path'), gtk.CellRendererText(), text=self.COLUMN_PATH)
+ column.set_sort_column_id(self.COLUMN_PATH)
+ treeview.append_column(column)
+
+ # column for user
+ column = gtk.TreeViewColumn(_('User'), gtk.CellRendererText(), text=self.COLUMN_USER)
+ column.set_sort_column_id(self.COLUMN_USER)
+ treeview.append_column(column)
+
+ # column for group
+ column = gtk.TreeViewColumn(_('Group'), gtk.CellRendererText(), text=self.COLUMN_GROUP)
+ column.set_sort_column_id(self.COLUMN_GROUP)
+ treeview.append_column(column)
+
+ # column for permissions
+ column = gtk.TreeViewColumn(_('Permissions'), gtk.CellRendererText(), text=self.COLUMN_PERM)
+ column.set_sort_column_id(self.COLUMN_VALUE)
+ treeview.append_column(column)
+
+ # column for force option
+ renderer = gtk.CellRendererToggle()
+ renderer.connect('toggled', self.toggle_enforced, lstore)
+ column = gtk.TreeViewColumn(_('Enforce'), renderer, active=self.COLUMN_FORCE)
+ column.set_sort_column_id(self.COLUMN_FORCE)
+ column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ column.set_fixed_width(50)
+
+ treeview.append_column(column)
+
+ sw.add(treeview)
+
+ for file in self.perms.list_options():
+ user_s, group_s, perm_s, force = self.perms.get(file)
+
+ # convert to boolean
+ if force:
+ force = True
+ else:
+ force = False
+
+ # building the option
+ iter = lstore.append()
+ lstore.set(iter,
+ self.COLUMN_PATH, file,
+ self.COLUMN_USER, user_s,
+ self.COLUMN_GROUP, group_s,
+ self.COLUMN_PERM, perm_s,
+ self.COLUMN_FORCE, force,
+ )
+ vbox.pack_start(sw)
+ return vbox
+
+ def toggle_enforced(self, cell, path, model):
+ '''Toggles a forced permission on an item'''
+ iter = model.get_iter((int(path),))
+ fixed = model.get_value(iter, self.COLUMN_FORCE)
+
+ # do something with the value
+ fixed = not fixed
+
+ # set new value
+ model.set(iter, self.COLUMN_FORCE, fixed)
+
+ def option_changed(self, treeview, path, col, model):
+ """Processes an option change"""
+ print path
+ iter = model.get_iter(path)
+ param = model.get_value(iter, self.COLUMN_OPTION)
+ descr = model.get_value(iter, self.COLUMN_DESCR)
+ value = model.get_value(iter, self.COLUMN_VALUE)
+
+ callback, params = config.SETTINGS[param]
+
+ # asks for new parameter value
+ dialog = gtk.Dialog(_("Select new value for %s") % (param),
+ self.window, 0,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK,
+ gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
+ label = gtk.Label(_("Modifying <b>%s</b>.\n<i>%s</i>\nCurrent value: <b>%s</b>") % (param, descr, value))
+ label.set_line_wrap(True)
+ label.set_use_markup(True)
+ dialog.vbox.pack_start(label)
+ if '*' in params:
+ # string parameter
+ entry = gtk.Entry()
+ entry.set_text(value)
+ dialog.vbox.pack_start(entry)
+ else:
+ # combobox parameter
+ entry = gtk.combo_box_new_text()
+ for item in params:
+ entry.append_text(item)
+ if value not in params:
+ entry.append_text(value)
+ params.append(value)
+ active = params.index(value)
+ entry.set_active(active)
+ dialog.vbox.pack_start(entry)
+
+ dialog.show_all()
+ response = dialog.run()
+ if response != gtk.RESPONSE_OK:
+ dialog.destroy()
+ return
+
+ # process new parameter
+ if '*' in params:
+ newval = entry.get_text()
+ else:
+ newval = entry.get_active_text()
+ dialog.destroy()
+
+ # update options
+ self.config.set(param, newval)
+
+ model.set(iter, self.COLUMN_VALUE, newval)
+
+
+ def quit(self, param):
+ """Quits the application"""
+ print "Leaving.."
+ gtk.main_quit()
+
+
+# {{{ usage
+def usage():
+ """Prints help message"""
+ print """Msec: Mandriva Security Center (%s).
+
+Arguments to msecgui:
+ -h, --help displays this helpful message.
+ -d enable debugging messages.
+""" % version
+# }}}
+
+if __name__ == "__main__":
+ log_level = logging.INFO
+
+ # parse command line
+ try:
+ opt, args = getopt.getopt(sys.argv[1:], 'hd', ['help', 'debug'])
+ except getopt.error:
+ usage()
+ sys.exit(1)
+ for o in opt:
+ # help
+ if o[0] == '-h' or o[0] == '--help':
+ usage()
+ sys.exit(0)
+ # list
+ elif o[0] == '-d' or o[0] == '--debug':
+ log_level = logging.DEBUG
+
+ # configuring logging
+ log = Log(interactive=True, log_syslog=False, log_file=True, log_level=log_level, log_path=config.SECURITYLOG)
+
+ # loading initial config
+ msec_config = config.MsecConfig(log, config=config.SECURITYCONF)
+ if not msec_config.load():
+ log.info(_("Unable to load config, using default values"))
+
+ # loading permissions config
+ perm_conf = config.PermConfig(log, config=config.PERMCONF)
+ if not perm_conf.load():
+ log.info(_("Unable to load permissions, using default values"))
+
+ # creating an msec instance
+ msec = MSEC(log)
+
+ log.info("Starting gui..")
+
+ gui = MsecGui(log, msec, msec_config, perm_conf)
+ gtk.main()
+
diff --git a/src/msec/msecperms b/src/msec/msecperms
new file mode 100755
index 0000000..3ce0c50
--- /dev/null
+++ b/src/msec/msecperms
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# Wrapper for msecperms.py
+#
+
+if [ "`whoami`" != "root" ]; then
+ echo 'msec: sorry, you must be root !'
+ exit 1
+fi
+
+LCK=/var/run/msec.pid
+
+function cleanup() {
+ rm -f $LCK
+}
+
+if [ -f $LCK ]; then
+ if [ -d /proc/`cat $LCK` ]; then
+ exit 0
+ else
+ rm -f $LCK
+ fi
+fi
+
+echo -n $$ > $LCK
+
+trap cleanup 0
+
+MSEC=/usr/share/msec/msecperms.py
+OPT="$@"
+
+$MSEC $OPT
+
+# msec ends here
diff --git a/src/msec/msecperms.py b/src/msec/msecperms.py
new file mode 100755
index 0000000..a0f1676
--- /dev/null
+++ b/src/msec/msecperms.py
@@ -0,0 +1,152 @@
+#!/usr/bin/python -O
+"""This file is responsible for permissions checking and
+(optionally) enforcing.
+"""
+
+import glob
+import re
+import string
+import os
+import stat
+import pwd
+import grp
+import sys
+import logging
+import getopt
+
+# localization
+import gettext
+
+try:
+ cat = gettext.Catalog('msec')
+ _ = cat.gettext
+except IOError:
+ _ = str
+
+# config
+import config
+
+# version
+try:
+ from version import version
+except:
+ version = "development version"
+
+# libmsec
+from libmsec import Log, PERMS
+
+# {{{ usage
+def usage():
+ """Prints help message"""
+ print """Msec: Mandriva Security Center (%s).
+
+This applications verifies and (when required) enforces permissions
+of certain files and directories.
+
+The list of permissions is stored in %s.
+
+Available parameters:
+ -h, --help displays this helpful message.
+ -l, --level <level> displays configuration for specified security
+ level.
+ -f, --force <level> force new level, overwriting user settings.
+ -e, --enforce <level> enforce permissions on all files.
+ -d enable debugging messages.
+ -p, --pretend only pretend to change the level, perform no real
+ actions. Use this to see what operations msec
+ will perform.
+""" % (version, config.PERMCONF)
+# }}}
+
+if __name__ == "__main__":
+ # default options
+ log_level = logging.INFO
+ force_level = False
+ level = config.DEFAULT_LEVEL
+ commit = True
+ enforce = False
+
+ # parse command line
+ try:
+ opt, args = getopt.getopt(sys.argv[1:], 'hel:f:dp', ['help', 'enforce', 'list', 'force', 'debug', 'pretend'])
+ except getopt.error:
+ usage()
+ sys.exit(1)
+ for o in opt:
+ # help
+ if o[0] == '-h' or o[0] == '--help':
+ usage()
+ sys.exit(0)
+ # list
+ elif o[0] == '-l' or o[0] == '--list':
+ level = o[1]
+ log = Log(interactive=True, log_syslog=False, log_file=False)
+ permconf = config.load_default_perms(log, level)
+ params = permconf.list_options()
+ if not params:
+ print >>sys.stderr, _("Invalid security level '%s'.") % level
+ sys.exit(1)
+ for file in params:
+ user, group, perm, force = permconf.get(file)
+ if force:
+ print "!! forcing permissions on %s" % file
+ print "%s: %s.%s perm %s" % (file, user, group, perm)
+ sys.exit(0)
+ # force new level
+ elif o[0] == '-f' or o[0] == '--force':
+ level = o[1]
+ force_level = True
+ # debugging
+ elif o[0] == '-d' or o[0] == '--debug':
+ log_level = logging.DEBUG
+ # permission enforcing
+ elif o[0] == '-e' or o[0] == '--enforce':
+ enforce = True
+ # check-only mode
+ elif o[0] == '-p' or o[0] == '--pretend':
+ commit = False
+
+ # verifying use id
+ if os.geteuid() != 0:
+ print >>sys.stderr, _("Msec: Mandriva Security Center (%s)\n") % version
+ print >>sys.stderr, _("Error: This application must be executed by root!")
+ print >>sys.stderr, _("Run with --help to get help.")
+ sys.exit(1)
+
+ # configuring logging
+ interactive = sys.stdin.isatty()
+ if interactive:
+ # logs to file and to terminal
+ log = Log(log_path=config.SECURITYLOG, interactive=True, log_syslog=False, log_level=log_level)
+ else:
+ log = Log(log_path=config.SECURITYLOG, interactive=False, log_level=log_level)
+
+ # loading permissions
+ permconf = config.PermConfig(log, config=config.PERMCONF)
+ if not permconf.load() and not force_level:
+ log.error(_("Permissions configuration not found, please run '%s -f <level>' to initialize.") % sys.argv[0])
+
+ # forcing new level
+ if force_level:
+ # first load the default configuration for level
+ default_permconf = config.load_default_perms(log, level)
+ params = default_permconf.list_options()
+ if not params:
+ log.error(_("Default configuration for level '%s' not found, aborting.") % level)
+ sys.exit(1)
+ for opt in params:
+ permconf.set(opt, default_permconf.get(opt))
+
+ # load the main permission class
+ perm = PERMS(log)
+
+ # check permissions
+ changed_files = perm.check_perms(permconf)
+
+ # writing back changes
+ perm.commit(really_commit=commit, enforce=force_level)
+ # saving updated config
+ if force_level and commit:
+ if not permconf.save():
+ log.error(_("Unable to save config!"))
+ sys.exit(0)
diff --git a/src/msec/version.py b/src/msec/version.py
new file mode 100644
index 0000000..3e3074d
--- /dev/null
+++ b/src/msec/version.py
@@ -0,0 +1 @@
+version='0.60.1'
diff --git a/src/msec_find/Makefile b/src/msec_find/Makefile
index 3aec03f..e85221f 100644
--- a/src/msec_find/Makefile
+++ b/src/msec_find/Makefile
@@ -2,7 +2,7 @@ CC=gcc
NAME=msec_find
CFLAGS = -ggdb -Wall -Wmissing-prototypes -Wmissing-declarations \
--Wpointer-arith -m486 -O2 -finline-functions -fkeep-inline-functions \
+-Wpointer-arith -O2 -finline-functions -fkeep-inline-functions \
-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
OBJ=find.o
diff --git a/src/promisc_check/Makefile b/src/promisc_check/Makefile
index 8c87e07..776dde2 100644
--- a/src/promisc_check/Makefile
+++ b/src/promisc_check/Makefile
@@ -2,7 +2,7 @@ CC=gcc
NAME=promisc_check
CFLAGS = -ggdb -Wall -Wmissing-prototypes -Wmissing-declarations \
--Wpointer-arith -m486 -O2 -finline-functions -fkeep-inline-functions
+-Wpointer-arith -O2 -finline-functions -fkeep-inline-functions
OBJ=promisc_check.o