#!/usr/bin/bash

# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved
#
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENSE.TXT
# CageFS script to patch and rebuild Apache's suexec

. /opt/cloudlinux/venv/usr/share/python-cllib/scripts/cl-common
common_path_of_cpanel="/usr/share/cagefs/cpanel"

if [ -z "$1" ]
then
    MIN_UID=500
else
    MIN_UID=$1
fi

ROOT_UID=0

if [ "$UID" -ne "$ROOT_UID" ]
then
  echo "ERROR: root privileges required"
  exit
fi

if is_ea4; then
    writeToLog "[CageFS] EasyApache 4 is active. Skip rebuilding suexec" "$common_path_of_cpanel"
    exit 0
fi

writeToLog "[CageFS] rebuild suexec" "$common_path_of_cpanel"

	# patch apache's suexec
	log=$(getLogFile "$common_path_of_cpanel")
	easyApacheDir=$(getEasyApacheDir)
	currentpath=$(pwd)
	cd $common_path_of_cpanel

	verify_source_integrity() {
	    local src_dir="$1"
	    local log_file="$2"
	    local dir_uid dir_perms file_uid file_perms
	    dir_uid=$(stat -c '%u' "$src_dir" 2>/dev/null)
	    if [ "$dir_uid" != "0" ]; then
	        echo "ERROR: Source directory $src_dir not owned by root (uid=$dir_uid)" >>"$log_file"
	        return 1
	    fi
	    dir_perms=$(stat -c '%a' "$src_dir" 2>/dev/null)
	    if [ $(( 0$dir_perms & 002 )) -ne 0 ]; then
	        echo "ERROR: Source directory $src_dir is world-writable" >>"$log_file"
	        return 1
	    fi
	    if [ -f "$src_dir/suexec.c" ]; then
	        file_uid=$(stat -c '%u' "$src_dir/suexec.c" 2>/dev/null)
	        if [ "$file_uid" != "0" ]; then
	            echo "ERROR: suexec.c not owned by root (uid=$file_uid)" >>"$log_file"
	            return 1
	        fi
	        file_perms=$(stat -c '%a' "$src_dir/suexec.c" 2>/dev/null)
	        if [ $(( 0$file_perms & 002 )) -ne 0 ]; then
	            echo "ERROR: suexec.c is world-writable" >>"$log_file"
	            return 1
	        fi
	    fi
	    return 0
	}

	# Bugbot: original pre-MR check verified the compiled binary contained
	# the cagefs sentinel strings (lve_jail, "or trusted user"). The MR's
	# initial verify_cagefs_patch checked only the SOURCE, leaving a
	# window where a tampered build tool could produce a binary missing
	# the patch. Verify BOTH: source pre-build and binary post-build.
	verify_cagefs_patch_source() {
	    local src_file="$1/suexec.c"
	    grep -q '#include <dlfcn.h>' "$src_file" 2>/dev/null && \
	    grep -q 'dlopen("liblve.so.0"' "$src_file" 2>/dev/null && \
	    grep -q 'dlsym(lib_handle, "lve_jail_uid")' "$src_file" 2>/dev/null
	}

	verify_cagefs_patch_binary() {
	    local bin="$1"
	    [ -f "$bin" ] || return 1
	    grep -q 'lve_jail' "$bin" 2>/dev/null && \
	    grep -q 'or trusted user' "$bin" 2>/dev/null
	}

	verify_cagefs_patch() {
	    verify_cagefs_patch_source "$1" && \
	    verify_cagefs_patch_binary "$1/suexec"
	}

	if [ -e "httpd2-cagefs_jail.patch" ];then
	    cd $easyApacheDir/src
	    for dirname in ./httpd*; do
		if [ -d "$dirname/support" ];then
                echo $dirname >>$log
				cd $dirname/support
                if ! verify_source_integrity "$(pwd)" "$log"; then
                    cd $currentpath
                    exit 1
                fi
                if [ "$2" == "restore" ]; then
					# apply patch if it has not been applied yet
    		        patch -N -i $common_path_of_cpanel/httpd2-cagefs_jail.patch >/dev/null
    		        patch -R -N -i $common_path_of_cpanel/httpd2-cagefs_jail.patch 1>>$log 2>>$log
                else
					# reverse patch if it is already applied
    		        patch -R -N -i $common_path_of_cpanel/httpd2-cagefs_jail.patch >/dev/null
    		        patch -N -i $common_path_of_cpanel/httpd2-cagefs_jail.patch 1>>$log 2>>$log
                fi
    		    if [ $? != 0 ];then
        			echo "Error applying patch. Please, contact support at http://www.cloudlinux.com/support/" >>$log
        			cd $currentpath
        			exit 1
    		    else
        			echo "Patch was applied correctly..." >>$log
    		    fi

				#../srclib/apr/libtool --mode=compile gcc -pthread -I/opt/pcre/include -DLINUX=2 -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE -DSECURELVE_MIN_UID=$MIN_UID   -I. -I"$easyApacheDir"/src/httpd-2.2.19/os/unix -I$easyApacheDir/src/httpd-2.2.19/server/mpm/prefork -I$easyApacheDir/src/httpd-2.2.19/modules/http -I$easyApacheDir/src/httpd-2.2.19/modules/filters -I$easyApacheDir/src/httpd-2.2.19/modules/proxy -I$easyApacheDir/src/httpd-2.2.19/include -I$easyApacheDir/src/httpd-2.2.19/modules/generators -I$easyApacheDir/src/httpd-2.2.19/modules/mappers -I$easyApacheDir/src/httpd-2.2.19/modules/database -I$easyApacheDir/src/httpd-2.2.19/srclib/apr/include -I$easyApacheDir/src/httpd-2.2.19/srclib/apr-util/include -I$easyApacheDir/src/httpd-2.2.19/modules/proxy/../generators -I/usr/include -I/usr/kerberos/include -I$easyApacheDir/src/httpd-2.2.19/modules/ssl -I$easyApacheDir/src/httpd-2.2.19/modules/dav/main  -prefer-non-pic -static -c suexec.c && touch suexec.lo
				#../srclib/apr/libtool --mode=link gcc -pthread -I/opt/pcre/include -L/opt/pcre/lib -lpcre -L/usr/lib -L/usr/kerberos/lib -ldl -o suexec suexec.lo

                cd ..
                CFLAGS="-O0 -DSECURELVE_MIN_UID=$MIN_UID" ./configure --enable-expires --enable-headers --enable-info	--enable-logio --enable-proxy --enable-rewrite --enable-ssl --enable-suexec --prefix=/usr/local/apache --with-included-apr --with-pcre=/opt/pcre --with-ssl=/usr --with-suexec-caller=nobody --with-suexec-docroot=/ --with-suexec-gidmin=100 --with-suexec-logfile=/usr/local/apache/logs/suexec_log --with-suexec-uidmin=100 --with-suexec-userdir=public_html LDFLAGS="-Wl,-rpath,/opt/pcre/lib" 1>>$log 2>>$log
                cd ./support

                make suexec 1>>$log 2>>$log

		if [ "$2" != "restore" ]; then
                # check that suexec is correct (source patch + binary sentinel)
                if verify_cagefs_patch "$(pwd)"; then
                    # install suexec atomically via temp file (TOCTOU-safe)
                    cp -f ./suexec /usr/local/apache/bin/suexec.tmp.$$ 1>>$log 2>>$log
                    chown root:root /usr/local/apache/bin/suexec.tmp.$$ 1>>$log 2>>$log
                    chmod ug+s /usr/local/apache/bin/suexec.tmp.$$ 1>>$log 2>>$log
                    mv -f /usr/local/apache/bin/suexec.tmp.$$ /usr/local/apache/bin/suexec 1>>$log 2>>$log

                    break
                else
                    echo "Error while rebuilding suexec"
                    echo "Please, try to rebuild suexec manually"
                    echo "If you use CPanel, you should run:"
                    echo "/scripts/upcp --force"
                    echo "/scripts/easyapache"
                    echo ""
                    cd $currentpath
                    exit 1
                fi
		else
                   # restore branch: confirm source no longer has cagefs
                   # patch tokens (binary check skipped — restore re-installs
                   # an upstream-clean suexec).
                   if verify_cagefs_patch_source "$(pwd)"; then
                       echo "ERROR: CageFS patch was not properly reversed" >>$log
                       cd $currentpath
                       exit 1
                   fi
                   # install atomically via temp file
                   cp -f ./suexec /usr/local/apache/bin/suexec.tmp.$$ 1>>$log 2>>$log
                   chown root:root /usr/local/apache/bin/suexec.tmp.$$ 1>>$log 2>>$log
                   chmod ug+s /usr/local/apache/bin/suexec.tmp.$$ 1>>$log 2>>$log
                   mv -f /usr/local/apache/bin/suexec.tmp.$$ /usr/local/apache/bin/suexec 1>>$log 2>>$log
		fi

                cd $easyApacheDir/src
		fi
	    done
	else
	    echo "Cannot find httpd2-cagefs_jail.patch. Contact support at http://www.cloudlinux.com/support/" >>$log
	    cd $currentpath
	    exit 1
	fi

	cd $currentpath

writeToLog "[CageFS] suexec was rebuilt successfully" "$common_path_of_cpanel"

exit 0
