#!/usr/bin/env bash set -euo pipefail # Provision quarantine and state directories for UploadShield (uploadshield.php) # Usage: sudo ./provision_dirs.sh [--config path/to/uploadshield.json] ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" CFG="${1:-$ROOT_DIR/uploadshield.json}" QUIET=0 if [[ "${2:-}" == "--quiet" ]]; then QUIET=1; fi info(){ if [[ $QUIET -ne 1 ]]; then echo "[INFO] $*"; fi } err(){ echo "[ERROR] $*" >&2; } if [[ $EUID -ne 0 ]]; then err "This script must be run as root to set ownership and SELinux contexts." err "Rerun as: sudo $0" exit 2 fi # Defaults QUARANTINE_DIR_DEFAULT="$ROOT_DIR/quarantine" STATE_DIR_DEFAULT="$ROOT_DIR/state" QUARANTINE_OWNER_DEFAULT="root" QUARANTINE_GROUP_DEFAULT="www-data" QUARANTINE_PERMS_DEFAULT="0700" STATE_PERMS_DEFAULT="0750" SELINUX_FCONTEXT_DEFAULT="httpd_sys_rw_content_t" QUARANTINE_DIR="$QUARANTINE_DIR_DEFAULT" STATE_DIR="$STATE_DIR_DEFAULT" QUARANTINE_OWNER="$QUARANTINE_OWNER_DEFAULT" QUARANTINE_GROUP="$QUARANTINE_GROUP_DEFAULT" QUARANTINE_PERMS="$QUARANTINE_PERMS_DEFAULT" STATE_PERMS="$STATE_PERMS_DEFAULT" SELINUX_FCONTEXT="$SELINUX_FCONTEXT_DEFAULT" if [[ -f "$CFG" ]]; then info "Loading config from $CFG" # Use jq-like parsing with grep/sed to avoid requiring jq on systems QUARANTINE_DIR=$(grep -oP '"quarantine_dir"\s*:\s*"\K[^"]+' "$CFG" || echo "$QUARANTINE_DIR") STATE_DIR=$(grep -oP '"state_dir"\s*:\s*"\K[^"]+' "$CFG" || echo "$STATE_DIR") QUARANTINE_PERMS=$(grep -oP '"quarantine_dir_perms"\s*:\s*"\K[^"]+' "$CFG" || echo "$QUARANTINE_PERMS") QUARANTINE_OWNER=$(grep -oP '"quarantine_owner"\s*:\s*"\K[^"]+' "$CFG" || echo "$QUARANTINE_OWNER") QUARANTINE_GROUP=$(grep -oP '"quarantine_group"\s*:\s*"\K[^"]+' "$CFG" || echo "$QUARANTINE_GROUP") fi info "Ensuring directories exist" mkdir -p -- "$QUARANTINE_DIR" mkdir -p -- "$STATE_DIR" info "Setting permissions and ownership" chmod ${QUARANTINE_PERMS} "$QUARANTINE_DIR" || true chown ${QUARANTINE_OWNER}:${QUARANTINE_GROUP} "$QUARANTINE_DIR" || true chmod ${STATE_PERMS} "$STATE_DIR" || true chown ${QUARANTINE_OWNER}:${QUARANTINE_GROUP} "$STATE_DIR" || true # Ensure existing files in quarantine are not world-readable/executable. if [[ -d "$QUARANTINE_DIR" ]]; then info "Hardening existing files in $QUARANTINE_DIR" # Set files to 0600 and directories to 0700 find "$QUARANTINE_DIR" -type d -print0 | xargs -0 -r chmod 0700 || true find "$QUARANTINE_DIR" -type f -print0 | xargs -0 -r chmod 0600 || true chown -R ${QUARANTINE_OWNER}:${QUARANTINE_GROUP} "$QUARANTINE_DIR" || true fi info "Verifying permissions" ls -ld "$QUARANTINE_DIR" "$STATE_DIR" # SELinux handling (best-effort) if command -v getenforce >/dev/null 2>&1 && [[ "$(getenforce)" != "Disabled" ]]; then info "SELinux enabled on system; attempting to configure file context" if command -v semanage >/dev/null 2>&1; then info "Registering fcontext for $QUARANTINE_DIR -> $SELINUX_FCONTEXT" semanage fcontext -a -t ${SELINUX_FCONTEXT} "${QUARANTINE_DIR}(/.*)?" || true info "Registering fcontext for $STATE_DIR -> ${SELINUX_FCONTEXT}" semanage fcontext -a -t ${SELINUX_FCONTEXT} "${STATE_DIR}(/.*)?" || true info "Applying contexts with restorecon" restorecon -Rv "$QUARANTINE_DIR" "$STATE_DIR" || true else info "semanage not available; skipping fcontext registration. Install policycoreutils-python-utils or provide manual guidance." fi else info "SELinux not enabled or getenforce unavailable; skipping SELinux steps" fi # Optional tmpfiles.d entry to recreate directories at boot (idempotent) TMPFILE="/etc/tmpfiles.d/uploadshield.conf" if [[ -w /etc/tmpfiles.d || $QUIET -eq 1 ]]; then info "Writing tmpfiles.d entry to ${TMPFILE}" cat > "$TMPFILE" <