#!/bin/bash
# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2018 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
##################################################
# Common functions #
##################################################
VERSION="2.0"
common_current_date=`date +%Y-%m-%d`
function getLogFile(){
#1 - common path
if [ ! -e "$1/logs" ];then
mkdir -p "$1/logs"
fi
current_date_time=`date +"%Y-%m-%d %k:%M:%S"`
echo "$1/logs/$common_current_date.log"
}
function writeToLog(){
#1 - message
#2 - common path
if [ ! -e "$2/logs" ];then
mkdir -p "$2/logs"
fi
current_date_time=`date +"%Y-%m-%d %k:%M:%S"`
prg=`basename "$0"`
local logfile="$2/logs/$common_current_date.log"
# Refuse to append through a symlinked log path: a writable logs/ dir lets an
# attacker plant a symlink so the root-run >> redirects into an arbitrary
# file. Skip the write silently (callers must stay robust), don't error out.
if [ -L "$logfile" ]; then
return 0
fi
echo "[$current_date_time from $prg] $1" >> "$logfile"
}
function writeFileToLog(){
#1 - file path
#2 - common path
if [ ! -e "$2/logs" ];then
mkdir -p "$2/logs"
fi
current_date_time=`date +"%Y-%m-%d %k:%M:%S"`
prg=`basename "$0"`
local logfile="$2/logs/$common_current_date.log"
# Refuse to append through a symlinked log path: a writable logs/ dir lets
# an attacker plant a symlink so the root-run >> redirects into an arbitrary
# file. Skip the write silently (callers must stay robust), don't error out.
if [ -L "$logfile" ]; then
return 0
fi
echo "[$current_date_time from $prg] ----------------File Content $1 BEG---------------" >> "$logfile"
if [ -e "$1" ];then
cat "$1" >> "$logfile"
fi
echo "[$current_date_time from $prg] ----------------File Content $1 End---------------" >> "$logfile"
}
function removeEmptyStringsFromFile(){
filename="$1"
# A privileged caller may hand $1 a path under an attacker-influenced
# directory; bash > redirection and sed both follow symlinks with no
# O_NOFOLLOW. Refuse to operate on a symlink so the truncating write cannot
# be redirected to an arbitrary target. Quote $filename against word-splitting.
if [ -L "$filename" ]; then
return 1
fi
res=`sed -e '/^$/d' "$filename"`
echo "$res" > "$filename"
}
# Guard for deleteAllExcept/deleteAllInclude: $3 is fed verbatim to sed as the
# PROGRAM, so restrict it to a single-address delete/print (/regex/d or /regex/p).
# Closes the GNU sed e/w/r/W/s///e arbitrary-write/exec surface. All shipped
# callers pass /#TOKEN/d (e.g. /#CageFS/d, /#l.v.e-manager/d).
function _isSafeSedAddressScript(){
#1 - candidate sed program
local _re='^/[^/]*/[dp]$'
[[ "$1" != *$'\n'* && "$1" =~ $_re ]]
}
function deleteAllExcept(){
#1 - hook
#2 - tmp name
#3 - pattern
#4 - common path
if [ ! -e "$4/tmp" ]; then
mkdir -p "$4/tmp"
fi
if [ -e "$1" ];then
# The cat read, the truncating `echo > "$1"` and the `cat >> "$1"`
# below follow a symlink at $1 with no O_NOFOLLOW; refuse so the
# root-run rewrite cannot be redirected to (and clobber) an arbitrary
# target via a planted link.
if [ -L "$1" ]; then
writeToLog "deleteAllExcept: refusing symlink hook path $1" "$4"
return 1
fi
if ! _isSafeSedAddressScript "$3"; then
writeToLog "deleteAllExcept: refusing unsafe sed program for $1: $3" "$4"
return 1
fi
cat "$1" | sed -n "$3" > "$4/tmp/$2.tmp.$$"
echo "#!/bin/bash" > "$1"
cat "$4/tmp/$2.tmp.$$" >> "$1"
rm -f "$4/tmp/$2.tmp.$$"
fi
}
function deleteAllInclude(){
#1 - hook
#2 - tmp name
#3 - pattern
#4 - common path
if [ ! -e "$4/tmp" ]; then
mkdir -p "$4/tmp"
fi
if [ -e "$1" ];then
# The cat read and the truncating `cat tmp > "$1"` below follow a
# symlink at $1 with no O_NOFOLLOW; refuse so the root-run rewrite
# cannot be redirected to (and clobber) an arbitrary target via a
# planted link.
if [ -L "$1" ]; then
writeToLog "deleteAllInclude: refusing symlink hook path $1" "$4"
return 1
fi
if ! _isSafeSedAddressScript "$3"; then
writeToLog "deleteAllInclude: refusing unsafe sed program for $1: $3" "$4"
return 1
fi
cat "$1" | sed "$3" > "$4/tmp/$2.tmp.$$"
cat "$4/tmp/$2.tmp.$$" > "$1"
rm -f "$4/tmp/$2.tmp.$$"
fi
}
function showBar {
# showBar feeds $1 into `let` arithmetic below; bash evaluates array
# subscripts during arithmetic, so require a plain integer first.
[[ "$1" =~ ^[0-9]+$ ]] || return 1
nmb=$(cat $0 | grep showBar | wc -l)
let "nmb = $nmb"
let "prct = $1 * 30 / $nmb"
let "prct_n = $1 * 100 / $nmb"
prg=`basename "$0"`
echo -n "$prg: [" >&2
for bar in {1..30}
do
if [ $bar -le $prct ];then
echo -n "#" >&2
else
echo -n " " >&2
fi
done
echo -ne "] ($prct_n%)\r" >&2
}
function get_command(){
command=$(which $1)
if [ $? != 0 ]; then
writeToLog "Can't execute command $1..."
exit 1;
fi
echo $command
}
function createHookHeader(){
#1 - hook name
#2 - modificator
#3 - common path
if [ ! -e "$3/tmp" ]; then
mkdir -p "$3/tmp"
fi
# Refuse a symlinked hook path: the > create, chmod and mv -f below all
# follow symlinks with no O_NOFOLLOW, so a planted link would redirect the
# root-run truncating write / mode change to an arbitrary target.
if [ -L "$1" ]; then
writeToLog "createHookHeader: refusing symlink hook path $1" "$3"
return 1
fi
# $1 (hook path) and $2 (modificator) are interpolated UNESCAPED into a
# root-run wrapper line and into the .bak path of mv. Restrict $2 to a safe
# token and reject shell metacharacters / control chars in $1 so neither can
# break out of the generated `