diff options
-rw-r--r-- | Makefile | 29 | ||||
-rw-r--r-- | conf/level.default | 54 | ||||
-rw-r--r-- | conf/level.none | 54 | ||||
-rw-r--r-- | conf/level.secure | 54 | ||||
-rw-r--r-- | conf/perm.0 | 92 | ||||
-rw-r--r-- | conf/perm.3 | 96 | ||||
-rw-r--r-- | conf/perm.5 | 96 | ||||
-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-x | cron-sh/diff_check.sh | 10 | ||||
-rwxr-xr-x | cron-sh/promisc_check.sh | 10 | ||||
-rwxr-xr-x | cron-sh/security.sh | 7 | ||||
-rwxr-xr-x | cron-sh/security_check.sh | 61 | ||||
-rw-r--r-- | man/C/msec.8 | 635 | ||||
-rw-r--r-- | man/C/mseclib.3 | 228 | ||||
-rw-r--r-- | share/Config.py | 44 | ||||
-rw-r--r-- | share/ConfigFile.py | 453 | ||||
-rw-r--r-- | share/Log.py | 54 | ||||
-rw-r--r-- | share/Makefile | 45 | ||||
-rwxr-xr-x | share/Perms.py | 305 | ||||
-rwxr-xr-x | share/draksec_help.py | 65 | ||||
-rw-r--r-- | share/libmsec.py | 1391 | ||||
-rwxr-xr-x | share/man.py | 67 | ||||
-rwxr-xr-x | share/msec | 85 | ||||
-rwxr-xr-x | share/msec.py | 290 | ||||
-rwxr-xr-x | share/shadow.py | 116 | ||||
-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/Makefile | 34 | ||||
-rw-r--r-- | src/msec/README (renamed from share/README) | 0 | ||||
-rwxr-xr-x | src/msec/compile.py (renamed from share/compile.py) | 12 | ||||
-rw-r--r-- | src/msec/config.py | 273 | ||||
-rwxr-xr-x | src/msec/help_draksec.py | 74 | ||||
-rwxr-xr-x | src/msec/libmsec.py | 1763 | ||||
-rwxr-xr-x | src/msec/man.py | 197 | ||||
-rwxr-xr-x | src/msec/msec | 34 | ||||
-rwxr-xr-x | src/msec/msec.py | 141 | ||||
-rwxr-xr-x | src/msec/msecgui | 34 | ||||
-rwxr-xr-x | src/msec/msecgui.py | 725 | ||||
-rwxr-xr-x | src/msec/msecperms | 34 | ||||
-rwxr-xr-x | src/msec/msecperms.py | 152 | ||||
-rw-r--r-- | src/msec/version.py | 1 | ||||
-rw-r--r-- | src/msec_find/Makefile | 2 | ||||
-rw-r--r-- | src/promisc_check/Makefile | 2 |
47 files changed, 4308 insertions, 3582 deletions
@@ -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 |