diff --git a/bureau/class/local.php b/bureau/class/local.php index de84f78a..3d565eed 100644 --- a/bureau/class/local.php +++ b/bureau/class/local.php @@ -13,7 +13,7 @@ $compat = array('DEFAULT_MX' => 'MX', $config_file = fopen('/etc/alternc/local.sh', 'r'); while (FALSE !== ($line = fgets($config_file))) { - if (ereg('^([A-Z0-9_]*)="([^"]*)"', $line, $regs)) { + if (preg_match('/^([A-Za-z0-9_]*) *= *"?(.*?)"?$/', trim($line), $regs)) { $GLOBALS['L_'.$regs[1]] = $regs[2]; if (isset($compat[$regs[1]])) { $GLOBALS['L_'.$compat[$regs[1]]] = $regs[2]; @@ -22,3 +22,25 @@ while (FALSE !== ($line = fgets($config_file))) { } fclose($config_file); + +$config_file = fopen('/etc/alternc/my.cnf', 'r'); +while (FALSE !== ($line = fgets($config_file))) { + if (preg_match('/^([A-Za-z0-9_]*) *= *"?(.*?)"?$/', trim($line), $regs)) { + switch ($regs[1]) { + case "user": + $GLOBALS['L_MYSQL_LOGIN'] = $regs[2]; + break; + case "password": + $GLOBALS['L_MYSQL_PWD'] = $regs[2]; + break; + case "host": + $GLOBALS['L_MYSQL_HOST'] = $regs[2]; + break; + case "database": + $GLOBALS['L_MYSQL_DATABASE'] = $regs[2]; + break; + } + } +} + +fclose($config_file); diff --git a/debian/changelog b/debian/changelog index 34527a30..f0044607 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,10 @@ alternc (0.9.7+dev) stable; urgency=low UNRELEASED + * move mysql configuration into a valid MySQL configuration file + (/etc/alternc/my.cnf). This fixes a serious security issue (#318) + where the MySQL root password was passed on the commandline. Those + changes are pretty invasive and might break upgrades, cron jobs and + your cat... * standardisation of the web interface, along with some esthetic changes, by Marc Angles, sponsored by Koumbit * styles can now be changed locally in admin/styles/base.css diff --git a/debian/config b/debian/config index 416fa16b..3709ef3c 100644 --- a/debian/config +++ b/debian/config @@ -38,6 +38,16 @@ if [ -r /etc/alternc/local.sh ]; then # source the current config . /etc/alternc/local.sh fi +if [ -r /etc/alternc/my.cnf ]; then + # make mysql configuration available as shell variables + # to convert from .cnf to shell syntax, we: + # * match only lines with "equal" in them (/=/) + # * remove whitespace around the = and add a left quote operator ' (;s) + # * add a right quote operator at the end of line (;s) + # * convert mysql variables into our MYSQL_ naming convention (;s) + # * print the result (;p) + eval `sed -n -e "/=/{s/ *= */='/;s/\$/'/;s/host/MYSQL_HOST/;s/user/MYSQL_LOGIN/;s/password/MYSQL_PASS/;s/database/MYSQL_DATABASE/;p}" /etc/alternc/my.cnf` +fi # mettre les valeurs de local.sh comme "default" pour debconf db_get alternc/hostingname diff --git a/debian/postinst b/debian/postinst index 244a0937..1debb93d 100644 --- a/debian/postinst +++ b/debian/postinst @@ -83,11 +83,8 @@ BIND_INTERNAL="" # Mail server hostname DEFAULT_MX="" -# MySQL configuration -MYSQL_HOST="" -MYSQL_DATABASE="" -MYSQL_USER="" -MYSQL_PASS="" +# Note: MySQL username/password configuration now stored in /etc/alternc/my.cnf + # quels clients mysql sont permis (%, localhost, etc) MYSQL_CLIENT="" @@ -119,16 +116,29 @@ EOF update_var alternc/ns2 NS2_HOSTNAME update_var alternc/bind_internal BIND_INTERNAL update_var alternc/default_mx DEFAULT_MX - update_var alternc/mysql/host MYSQL_HOST - update_var alternc/mysql/db MYSQL_DATABASE - update_var alternc/mysql/user MYSQL_USER - update_var alternc/mysql/password MYSQL_PASS update_var alternc/mysql/client MYSQL_CLIENT update_var alternc/alternc_location ALTERNC_LOC update_var alternc/mynetwork SMTP_RELAY_NETWORKS sed -e "$SED_SCRIPT" < $CONFIGFILE > $CONFIGFILE.tmp mv -f $CONFIGFILE.tmp $CONFIGFILE + # Setup grants + db_get "alternc/mysql/host" + MYSQL_HOST="$RET" + if [ "$MYSQL_HOST" != "localhost" -o -e /usr/sbin/mysqld ]; then + # compatibility shims with my.cnf + host="$RET" + db_get "alternc/mysql/db" + database="$RET" + db_get "alternc/mysql/user" + user="$RET" + db_get "alternc/mysql/password" + password="$RET" + + # we source (instead of forking) mysql.sh so that it gets the local environment above + . /usr/share/alternc/install/mysql.sh + fi + # forget the password db_reset alternc/mysql/password || true db_fset alternc/mysql/password "seen" "false" || true diff --git a/debian/postrm b/debian/postrm index 77adef58..838b6e96 100644 --- a/debian/postrm +++ b/debian/postrm @@ -15,7 +15,7 @@ set -e case "$1" in purge) - rm -f /etc/alternc/local.sh /etc/alternc/bureau.conf + rm -f /etc/alternc/local.sh /etc/alternc/my.cnf /etc/alternc/bureau.conf rm -f /var/backups/alternc/etc-installed.tar.gz # Purge database? diff --git a/install/alternc.install b/install/alternc.install index 57025d1a..b15db4e6 100644 --- a/install/alternc.install +++ b/install/alternc.install @@ -5,6 +5,9 @@ # on a new server. THIS SCRIPT ERASE ALL DATA ON THE AlternC SYSTEM !! # YOU HAVE BEEN WARNED ! +# This script now assumes it has MySQL connectivity through +# /etc/alternc/my.cnf + set -e . /usr/lib/alternc/functions.sh @@ -78,6 +81,18 @@ fi . /etc/alternc/local.sh +# XXX: copy-paste from debian/config +if [ -r /etc/alternc/my.cnf ]; then + # make mysql configuration available as shell variables + # to convert from .cnf to shell syntax, we: + # * match only lines with "equal" in them (/=/) + # * remove whitespace around the = and add a left quote operator ' (;s) + # * add a right quote operator at the end of line (;s) + # * convert mysql variables into our MYSQL_ naming convention (;s) + # * print the result (;p) + eval `sed -n -e "/=/{s/ *= */='/;s/\$/'/;s/host/MYSQL_HOST/;s/user/MYSQL_LOGIN/;s/password/MYSQL_PASS/;s/database/MYSQL_DATABASE/;p}" /etc/alternc/my.cnf` +fi + WARNING="WARNING: Do not edit this file, edit the one in /etc/alternc/templates and launch alternc.install again." VERSION="`dpkg -s alternc | sed -n -e 's/^Version: \(.*\)/\1/p'`" @@ -101,7 +116,10 @@ if [ -z "$MONITOR_IP" ]; then MONITOR_IP="127.0.0.1" fi -SED_SCRIPT=" +# XXX: I assume this is secure if /tmp is sticky (+t) +# we should have a better way to deal with templating, of course. +SED_SCRIPT=`mktemp` +cat > $SED_SCRIPT </dev/null || true for file in $CONFIG_FILES; do TEMPLATE="$TEMPLATE_DIR/${file##etc/}" if [ -f "$TEMPLATE" ]; then - sed -e "$SED_SCRIPT" < $TEMPLATE > /$file + sed -f "$SED_SCRIPT" < $TEMPLATE > /$file fi done +rm -f $SED_SCRIPT ####################################################################### # Save installed files to check them during next install # tar -zcf "$INSTALLED_CONFIG_TAR" -C / $CONFIG_FILES -###################################################################### -# Initialize database -# -if [ "$MYSQL_HOST" != "localhost" -o -e /usr/sbin/mysqld ]; then - echo "Setup MySQL and database..." - /usr/share/alternc/install/mysql.sh "$MYSQL_HOST" "$MYSQL_USER" "$MYSQL_PASS" "$MYSQL_DATABASE" -fi - ######################################################################## # Ad-hoc fixes # @@ -263,7 +274,7 @@ done /usr/lib/alternc/basedir_prot.sh # Creating admin user if needed -HAS_ROOT="`mysql -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASS" "$MYSQL_DATABASE" -e "SELECT COUNT(*) FROM membres WHERE login = 'admin' OR login = 'root' and su = 1" | tail -1`" +HAS_ROOT=`mysql --defaults-file=/etc/alternc/my.cnf -e "SELECT COUNT(*) FROM membres WHERE login = 'admin' OR login = 'root' and su = 1" | tail -1` if [ "$HAS_ROOT" != "1" ]; then echo "Creating admin user..." echo "" diff --git a/install/mysql.sh b/install/mysql.sh index 7798a086..71b19d3b 100755 --- a/install/mysql.sh +++ b/install/mysql.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/sh # # $Id: mysql.sh,v 1.11 2006/01/11 22:51:28 anarcat Exp $ # ---------------------------------------------------------------------- @@ -28,50 +28,125 @@ # USAGE : "mysql.sh loginroot passroot systemdb" # ---------------------------------------------------------------------- # - -sqlserver="$1" -rootlogin="$2" -rootpass="$3" -systemdb="$4" - -if [ -z "$rootlogin" -o -z "$rootpass" -o -z "$systemdb" ] -then - echo "Usage: mysql.sh " - exit 1 -fi - -mysql="/usr/bin/mysql --defaults-file=/etc/mysql/debian.cnf -h$sqlserver " + +# This script expects the following environment to exist: +# * host +# * user +# * password +# * database +# +# XXX: the sed script should be generated here +# +# So this file should generally be sourced like this: +# . /usr/share/alternc/install/mysql.sh +# +# Those values are used to set the username/passwords... # The grant all is the most important right needed in this script. +echo "Granting users..." +# cat < $MYSQL_CONFIG < $SED_SCRIPT_NAME < $MYSQL_CONFIG.$$ +mv -f $MYSQL_CONFIG.$$ $MYSQL_CONFIG +rm -f $SED_SCRIPT_NAME + +# Now we should be able to use the mysql configuration +mysql="/usr/bin/mysql --defaults-file=$MYSQL_CONFIG" + +echo "Checking for MySQL connectivity" +$mysql -e "SHOW TABLES" >/dev/null && echo "MYSQL.SH OK!" || echo "MYSQL.SH FAILED!" + +# Final mysql setup: db schema +echo "installing AlternC schema in $database..." +$mysql < /usr/share/alternc/install/mysql.sql || echo cannot load database schema -/usr/bin/mysql -h$sqlserver -u$rootlogin -p$rootpass $systemdb -e "SHOW TABLES" >/dev/null && echo "MYSQL.SH OK!" || echo "MYSQL.SH FAILED!" diff --git a/install/upgrade_check.sh b/install/upgrade_check.sh index 0e9cf5aa..02b8699e 100755 --- a/install/upgrade_check.sh +++ b/install/upgrade_check.sh @@ -45,7 +45,7 @@ do # run the proper program to interpret the upgrade script case "$ext" in sql) - mysql -f -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASS $MYSQL_DATABASE \ + mysql -f --defaults-file=/etc/alternc/my.cnf \ < $file || true ;; php) diff --git a/src/basedir_prot.sh b/src/basedir_prot.sh index 072922dd..e2533eca 100755 --- a/src/basedir_prot.sh +++ b/src/basedir_prot.sh @@ -18,11 +18,6 @@ extra_paths="/var/alternc/dns/redir:/usr/share/php/:/var/alternc/tmp/:/tmp/" . /etc/alternc/local.sh . /usr/lib/alternc/functions.sh -if [ -z "$MYSQL_HOST" ] -then - MYSQL_HOST="localhost" -fi - echo -n "adding open_base_dir protection for:" # boucle sur tous les domaines hébergés, ou sur les arguments de la # ligne de commande diff --git a/src/fixperms.sh b/src/fixperms.sh index 8b8b0177..32b6a4d0 100755 --- a/src/fixperms.sh +++ b/src/fixperms.sh @@ -63,5 +63,5 @@ function doone { done } -mysql -h"$MYSQL_HOST" -p"$MYSQL_PASS" -u"$MYSQL_USER" "$MYSQL_DATABASE" -B -e "select uid,login from membres" |grep -v ^uid|doone +mysql --defaults-file=/etc/alternc/my.cnf -B -e "select uid,login from membres" |grep -v ^uid|doone diff --git a/src/functions.sh b/src/functions.sh index 4d690b47..0d1ee916 100644 --- a/src/functions.sh +++ b/src/functions.sh @@ -309,7 +309,7 @@ get_account_by_domain() { else # implantons localement ce que nous avons besoin, puisque admintools # n'est pas là - mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DATABASE -B -N -e \ + mysql --defaults-file=/etc/alternc/my.cnf -B -N -e \ 'SELECT a.login FROM membres a, sub_domaines b WHERE a.uid = b.compte AND \ CONCAT(IF(sub="", "", CONCAT(sub, ".")), domaine) = "'"$1"'" LIMIT 1;' fi diff --git a/src/sqlbackup.sh b/src/sqlbackup.sh index 7f4bad8f..7f8176af 100755 --- a/src/sqlbackup.sh +++ b/src/sqlbackup.sh @@ -29,9 +29,6 @@ set -e -# Get mysql user and password : -. /etc/alternc/local.sh - function dobck { local ext local i @@ -63,10 +60,10 @@ function dobck { mv -f "${target_dir}/${db}.sql${ext}" \ "${target_dir}/${db}.sql.${i}${ext}" 2>/dev/null || true if [ "$compressed" -eq 1 ]; then - mysqldump -h"$MYSQL_HOST" -u"$login" -p"$pass" "$db" --add-drop-table --allow-keywords -Q -f -q -a -e | + mysqldump --defaults-file=/etc/alternc/my.cnf --add-drop-table --allow-keywords -Q -f -q -a -e | gzip -c > "${target_dir}/${db}.sql${ext}" else - mysqldump -h"$MYSQL_HOST" -u"$login" -p"$pass" "$db" --add-drop-table --allow-keywords -Q -f -q -a -e \ + mysqldump --defaults-file=/etc/alternc/my.cnf --add-drop-table --allow-keywords -Q -f -q -a -e \ > "${target_dir}/${db}.sql" fi @@ -83,8 +80,7 @@ else mode=1 fi -/usr/bin/mysql -h"$MYSQL_HOST" -u"$MYSQL_USER" -p"$MYSQL_PASS" \ - "$MYSQL_DATABASE" -B << EOF | tail -n '+2' | dobck +/usr/bin/mysql --defaults-file=/etc/alternc/my.cnf -B << EOF | tail -n '+2' | dobck SELECT login, pass, db, bck_history, bck_gzip, bck_dir FROM db WHERE bck_mode=$mode; diff --git a/src/update_domains.sh b/src/update_domains.sh index e56893a0..4257f4be 100755 --- a/src/update_domains.sh +++ b/src/update_domains.sh @@ -74,8 +74,7 @@ fi . "$CONFIG_FILE" -if [ -z "$MYSQL_HOST" -o -z "$MYSQL_DATABASE" -o -z "$MYSQL_USER" -o \ - -z "$MYSQL_PASS" -o -z "$DEFAULT_MX" -o -z "$PUBLIC_IP" ]; then +if [ -z "$DEFAULT_MX" -o -z "$PUBLIC_IP" ]; then echo "Bad configuration. Please use:" echo " dpkg-reconfigure alternc" exit 1 @@ -96,10 +95,8 @@ LOCK_FILE="$DATA_ROOT/bureau/cron.lock" HTTP_DNS="$DATA_ROOT/dns" HTML_HOME="$DATA_ROOT/html" -MYSQL_SELECT="mysql -h${MYSQL_HOST} -u${MYSQL_USER} - -p${MYSQL_PASS} -Bs ${MYSQL_DATABASE}" -MYSQL_DELETE="mysql -h${MYSQL_HOST} -u${MYSQL_USER} - -p${MYSQL_PASS} ${MYSQL_DATABASE}" +MYSQL_SELECT="mysql --defaults-file=/etc/alternc/my.cnf -Bs " +MYSQL_DELETE="mysql --defaults-file=/etc/alternc/my.cnf " ######################################################################## # Functions diff --git a/tools/get_account_by_domain b/tools/get_account_by_domain index 791ccb10..2a96d6cd 100755 --- a/tools/get_account_by_domain +++ b/tools/get_account_by_domain @@ -75,12 +75,11 @@ MYSQL_UNREACHABLE_DATABASE=`$printf "$($gettext "Cannot access accounts database [ "$1" = "-h" ] || [ "$1" = "--help" ] && { echo $HELP ; echo $USAGE ; exit 0 ; } # Have to get AlternC conf file : ! [ -f "$ALTERNC_CONF_FILE" ] && { echo $MISSING_CONF_FILE ; exit 1 ; } || . $ALTERNC_CONF_FILE -# Must have access to mysql to retreive accounts owning domains : -[ -z "$MYSQL_HOST" ] && MYSQL_HOST=localhost -$mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DATABASE -e "select count(*) from domaines_standby;" > /dev/null 2>&1 -[ "$?" != 0 ] && { echo "$MYSQL_UNREACHABLE_DATABASE" ; exit 1 ; } || mysql="$mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DATABASE -B -N -e " +mysql="$mysql --defaults-file=/etc/alternc/my.cnf" +$mysql -e "select count(*) from domaines_standby;" > /dev/null 2>&1 +[ "$?" != 0 ] && { echo "$MYSQL_UNREACHABLE_DATABASE" ; exit 1 ; } # Does the stuff -$mysql "select concat(a.login, \" (\", a.mail, \")\") from membres a, sub_domaines b where a.uid = b.compte and concat(if(sub=\"\", \"\", concat(sub, \".\")), domaine) = \"${1}\";" +$mysql -B -N -e "select concat(a.login, \" (\", a.mail, \")\") from membres a, sub_domaines b where a.uid = b.compte and concat(if(sub=\"\", \"\", concat(sub, \".\")), domaine) = \"${1}\";" diff --git a/tools/get_domains_by_account b/tools/get_domains_by_account index 39e65680..be5b613c 100755 --- a/tools/get_domains_by_account +++ b/tools/get_domains_by_account @@ -74,9 +74,9 @@ MYSQL_UNREACHABLE_DATABASE=`$printf "$($gettext "Cannot access accounts database # Have to get AlternC conf file : ! [ -f "$ALTERNC_CONF_FILE" ] && { echo $MISSING_CONF_FILE ; exit 1 ; } || . $ALTERNC_CONF_FILE # Must have access to mysql to retreive accounts owning domains : -[ -z "$MYSQL_HOST" ] && MYSQL_HOST=localhost -$mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DATABASE -e "select count(*) from domaines_standby;" > /dev/null 2>&1 -[ "$?" != 0 ] && { echo "$MYSQL_UNREACHABLE_DATABASE" ; exit 1 ; } || mysql="$mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DATABASE -B -N -e " +mysql="$mysql --defaults-file=/etc/alternc/my.cnf -B -N -e" +$mysql "select count(*) from domaines_standby;" > /dev/null 2>&1 +[ "$?" != 0 ] && { echo "$MYSQL_UNREACHABLE_DATABASE" ; exit 1 ; } # Does the stuff $mysql "select concat(a.sub, if(a.sub=\"\",\"\", \".\"), a.domaine) from sub_domaines a, membres b where a.compte = b.uid and b.login = \"${1}\";" diff --git a/tools/top_http_users b/tools/top_http_users index 8baef28d..8fae4db9 100755 --- a/tools/top_http_users +++ b/tools/top_http_users @@ -168,10 +168,9 @@ LOG_FILE=$LOG_DIR/access.log # Have to get AlternC conf file : [ -f "$ALTERNC_CONF_FILE" ] || { echo $MISSING_CONF_FILE ; exit 1 ; } && . $ALTERNC_CONF_FILE # Must have access to mysql to retreive accounts owning domains : -[ -z "$MYSQL_HOST" ] && MYSQL_HOST=localhost -$mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DATABASE -e "select count(*) from domaines_standby;" > /dev/null 2>&1 -[ "$?" != 0 ] && { echo "$MYSQL_UNREACHABLE_DATABASE" ; exit 1 ; } || mysql="$mysql -h$MYSQL_HOST -u$MYSQL_USER -p$MYSQL_PASS -D$MYSQL_DATABASE -B -N -e " - +mysql="$mysql --defaults-file=/etc/alternc/my.cnf -B -N -e" +$mysql "select count(*) from domaines_standby;" > /dev/null 2>&1 +[ "$?" != 0 ] && { echo "$MYSQL_UNREACHABLE_DATABASE" ; exit 1 ; } # Prevents executing more than one shell at the same time $lockfilecreate --retry 1 $LOCK_FILE if [ $? != 0 ]