diff --git a/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh b/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh index 406b3aa..a9601d4 100755 --- a/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh +++ b/scripts/digital_wellbeing/pacman/install_pacman_wrapper.sh @@ -28,6 +28,11 @@ WORDS_DEST="${INSTALL_DIR}/words.txt" BLOCKED_DEST="${INSTALL_DIR}/pacman_blocked_keywords.txt" WHITELIST_DEST="${INSTALL_DIR}/pacman_whitelist.txt" GREYLIST_DEST="${INSTALL_DIR}/pacman_greylist.txt" +INTEGRITY_DIR="/var/lib/pacman-wrapper" +INTEGRITY_FILE="${INTEGRITY_DIR}/policy.sha256" +VBOX_ENFORCE_SOURCE="$(dirname "$0")/../virtualbox/enforce_vbox_hosts.sh" +VBOX_INSTALL_DIR="/usr/local/share/digital_wellbeing/virtualbox" +VBOX_ENFORCE_DEST="${VBOX_INSTALL_DIR}/enforce_vbox_hosts.sh" # Check if script is run as root if [ "$EUID" -ne 0 ]; then echo -e "${RED}Please run as root${NC}" @@ -78,8 +83,51 @@ fi # Update the PACMAN_BIN variable in the wrapper to point to the original sed -i 's|PACMAN_BIN="\/usr\/bin\/pacman"|PACMAN_BIN="\/usr\/bin\/pacman.orig"|g' "$WRAPPER_DEST" +# Create integrity directory if it doesn't exist +mkdir -p "$INTEGRITY_DIR" +chmod 755 "$INTEGRITY_DIR" + +# Generate checksums of policy files for integrity verification +echo -e "${BLUE}Generating integrity checksums for policy files...${NC}" +{ + sha256sum "$BLOCKED_DEST" 2>/dev/null || true + sha256sum "$GREYLIST_DEST" 2>/dev/null || true + sha256sum "$WHITELIST_DEST" 2>/dev/null || true +} > "$INTEGRITY_FILE" + +# Make integrity file immutable +chmod 400 "$INTEGRITY_FILE" +if command -v chattr > /dev/null 2>&1; then + chattr +i "$INTEGRITY_FILE" 2>/dev/null || echo -e "${YELLOW}Warning: Could not make integrity file immutable${NC}" +fi + +# Make policy files immutable to prevent easy tampering +echo -e "${BLUE}Protecting policy files from modification...${NC}" +if command -v chattr > /dev/null 2>&1; then + chattr +i "$BLOCKED_DEST" 2>/dev/null || echo -e "${YELLOW}Warning: Could not make blocked list immutable${NC}" + chattr +i "$GREYLIST_DEST" 2>/dev/null || echo -e "${YELLOW}Warning: Could not make greylist immutable${NC}" + # Note: whitelist is intentionally left modifiable for user convenience +else + echo -e "${YELLOW}Warning: chattr not available, policy files will not be immutable${NC}" +fi + +# Install VirtualBox enforcement script if available +if [ -f "$VBOX_ENFORCE_SOURCE" ]; then + echo -e "${BLUE}Installing VirtualBox hosts enforcement script...${NC}" + mkdir -p "$VBOX_INSTALL_DIR" + cp "$VBOX_ENFORCE_SOURCE" "$VBOX_ENFORCE_DEST" + chmod +x "$VBOX_ENFORCE_DEST" + echo -e "${GREEN}VirtualBox enforcement script installed to ${VBOX_ENFORCE_DEST}${NC}" +else + echo -e "${YELLOW}VirtualBox enforcement script not found, skipping...${NC}" +fi + # Create symbolic link echo -e "${BLUE}Creating symbolic link...${NC}" ln -sf "$WRAPPER_DEST" /usr/bin/pacman echo -e "${GREEN}Installation complete!${NC}" echo -e "Pacman is now wrapped. The original pacman is available at ${CYAN}/usr/bin/pacman.orig${NC}" +echo -e "${CYAN}Policy files are now protected with immutable attributes.${NC}" +if [ -f "$VBOX_ENFORCE_DEST" ]; then + echo -e "${CYAN}VirtualBox VMs will automatically be configured to use host's /etc/hosts.${NC}" +fi diff --git a/scripts/digital_wellbeing/pacman/pacman_wrapper.sh b/scripts/digital_wellbeing/pacman/pacman_wrapper.sh index 0cca9e1..894b925 100755 --- a/scripts/digital_wellbeing/pacman/pacman_wrapper.sh +++ b/scripts/digital_wellbeing/pacman/pacman_wrapper.sh @@ -17,6 +17,50 @@ declare -a BLOCKED_KEYWORDS_LIST=() declare -a WHITELISTED_NAMES_LIST=() declare -a GREYLISTED_KEYWORDS_LIST=() POLICY_LISTS_LOADED=0 +INTEGRITY_DIR="/var/lib/pacman-wrapper" +INTEGRITY_FILE="${INTEGRITY_DIR}/policy.sha256" + +# Verify integrity of policy files +verify_policy_integrity() { + if [[ ! -f $INTEGRITY_FILE ]]; then + echo -e "${RED}SECURITY WARNING: Policy integrity file missing!${NC}" >&2 + echo -e "${RED}The pacman wrapper may have been tampered with.${NC}" >&2 + echo -e "${RED}Please reinstall the wrapper using: sudo install_pacman_wrapper.sh${NC}" >&2 + return 1 + fi + + local script_dir + script_dir="$(dirname "$(readlink -f "$0")")" + local blocked_file="$script_dir/pacman_blocked_keywords.txt" + local greylist_file="$script_dir/pacman_greylist.txt" + local whitelist_file="$script_dir/pacman_whitelist.txt" + + # Verify checksums + local failed=0 + while IFS= read -r line; do + local expected_hash expected_file + expected_hash=$(echo "$line" | awk '{print $1}') + expected_file=$(echo "$line" | awk '{print $2}') + + if [[ -f $expected_file ]]; then + local actual_hash + actual_hash=$(sha256sum "$expected_file" 2>/dev/null | awk '{print $1}') + if [[ $actual_hash != "$expected_hash" ]]; then + echo -e "${RED}SECURITY WARNING: Policy file integrity check failed for $expected_file${NC}" >&2 + failed=1 + fi + fi + done < "$INTEGRITY_FILE" + + if [[ $failed -eq 1 ]]; then + echo -e "${RED}CRITICAL: Policy files have been tampered with!${NC}" >&2 + echo -e "${RED}This could be an attempt to bypass security restrictions.${NC}" >&2 + echo -e "${RED}Wrapper operation DENIED. Please reinstall using: sudo install_pacman_wrapper.sh${NC}" >&2 + return 1 + fi + + return 0 +} load_policy_lists() { if [[ $POLICY_LISTS_LOADED -eq 1 ]]; then @@ -409,11 +453,22 @@ function is_steam_package() { [[ $1 == "steam" ]] } +# Helper to check if a package name is VirtualBox (hardcoded, cannot be bypassed by editing policy files) +function is_virtualbox_package() { + local pkg_lower="${1,,}" + [[ $pkg_lower == *"virtualbox"* || $pkg_lower == *"vbox"* ]] +} + # Function to check if user is trying to install steam (challenge-eligible package) function check_for_steam() { check_install_for is_steam_package "$@" } +# Function to check if user is trying to install VirtualBox (hardcoded enforcement) +function check_for_virtualbox() { + check_install_for is_virtualbox_package "$@" +} + # Function to check if current day is a weekday (after 4PM Friday until midnight Sunday) function is_weekday() { local day_of_week @@ -581,12 +636,38 @@ function prompt_for_greylist_challenge() { run_word_challenge "Greylist" 6 120 90 30 15 20 } +# Function to prompt for VirtualBox installation (enhanced security, hardcoded) +function prompt_for_virtualbox_challenge() { + echo -e "${RED}═══════════════════════════════════════════════════════${NC}" + echo -e "${RED} VIRTUALBOX INSTALLATION ATTEMPT DETECTED ${NC}" + echo -e "${RED}═══════════════════════════════════════════════════════${NC}" + echo -e "${YELLOW}WARNING: You are trying to install VirtualBox.${NC}" + echo -e "${YELLOW}This package can be used to bypass /etc/hosts restrictions.${NC}" + echo -e "" + echo -e "${CYAN}Security measures will be automatically applied:${NC}" + echo -e " 1. VMs will use host's DNS resolution" + echo -e " 2. Host's /etc/hosts will be shared with VMs (read-only)" + echo -e " 3. Policy enforcement cannot be disabled via file editing" + echo -e "" + echo -e "${YELLOW}This is a HARDCODED restriction that cannot be bypassed by${NC}" + echo -e "${YELLOW}modifying policy files or reinstalling the wrapper.${NC}" + echo -e "" + + # More difficult challenge: word_length=7, words_count=150, timeout=120s, initial_delay=45, post_delay=30-50 + run_word_challenge "VirtualBox Security" 7 150 120 45 30 20 +} + # Check for wrapper-specific commands if [[ $1 == "--help-wrapper" ]]; then show_help exit 0 fi +# CRITICAL: Verify policy file integrity before any operations +if ! verify_policy_integrity; then + exit 1 +fi + # Before any pacman action, ensure maintenance services exist ensure_periodic_maintenance @@ -606,6 +687,13 @@ if check_for_steam "$@"; then fi fi +# Check for VirtualBox (HARDCODED - cannot be bypassed by editing policy files) +if check_for_virtualbox "$@"; then + if ! prompt_for_virtualbox_challenge; then + exit 1 + fi +fi + # Check for greylisted packages (challenge-eligible) if check_for_greylisted "$@"; then if ! prompt_for_greylist_challenge; then @@ -656,6 +744,52 @@ remove_installed_blocked_packages "$@" # Also remove installed greylisted packages remove_installed_greylisted_packages "$@" +# If VirtualBox was involved in this operation, enforce hosts file sharing +enforce_vbox_hosts_if_needed() { + # Only check after install operations + if [[ ${1:-} != "-S"* && ${1:-} != "-U"* ]]; then + return 0 + fi + + # Check if VirtualBox is installed + if ! "$PACMAN_BIN" -Qq virtualbox > /dev/null 2>&1; then + return 0 + fi + + # Locate the enforcement script + local script_dir + script_dir="$(dirname "$(readlink -f "$0")")" + local vbox_enforce_script="" + + # Try to find the enforcement script + if [[ -f "$script_dir/../virtualbox/enforce_vbox_hosts.sh" ]]; then + vbox_enforce_script="$script_dir/../virtualbox/enforce_vbox_hosts.sh" + elif [[ -f "$HOME/linux-configuration/scripts/digital_wellbeing/virtualbox/enforce_vbox_hosts.sh" ]]; then + vbox_enforce_script="$HOME/linux-configuration/scripts/digital_wellbeing/virtualbox/enforce_vbox_hosts.sh" + elif [[ -f "/usr/local/share/digital_wellbeing/virtualbox/enforce_vbox_hosts.sh" ]]; then + vbox_enforce_script="/usr/local/share/digital_wellbeing/virtualbox/enforce_vbox_hosts.sh" + fi + + if [[ -z $vbox_enforce_script ]]; then + return 0 + fi + + # Check if enforcement is already applied + if bash "$vbox_enforce_script" check > /dev/null 2>&1; then + return 0 + fi + + # VirtualBox is installed but enforcement not applied + echo -e "${YELLOW}VirtualBox detected. Applying /etc/hosts enforcement to VMs...${NC}" >&2 + if [[ $EUID -ne 0 ]]; then + sudo bash "$vbox_enforce_script" enforce || echo -e "${RED}Failed to enforce hosts on VirtualBox VMs${NC}" >&2 + else + bash "$vbox_enforce_script" enforce || echo -e "${RED}Failed to enforce hosts on VirtualBox VMs${NC}" >&2 + fi +} + +enforce_vbox_hosts_if_needed "$@" + # Display some helpful tips depending on the operation if [[ $1 == "-S" || $1 == "-S "* ]] && [ $exit_code -eq 0 ]; then echo -e "${CYAN}Tip:${NC} You may need to log out or restart to use some newly installed software." diff --git a/scripts/digital_wellbeing/virtualbox/enforce_vbox_hosts.sh b/scripts/digital_wellbeing/virtualbox/enforce_vbox_hosts.sh new file mode 100644 index 0000000..a69c13b --- /dev/null +++ b/scripts/digital_wellbeing/virtualbox/enforce_vbox_hosts.sh @@ -0,0 +1,279 @@ +#!/bin/bash +# filepath: enforce_vbox_hosts.sh +# Enforce host machine's /etc/hosts file on all VirtualBox VMs +# This prevents VMs from bypassing host-level content filtering + +set -euo pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +NC='\033[0m' # No Color + +# Auto-sudo functionality +if [ "$EUID" -ne 0 ]; then + echo -e "${YELLOW}Executing with sudo...${NC}" + exec sudo "$0" "$@" +fi + +# Check if VBoxManage is available +if ! command -v VBoxManage > /dev/null 2>&1; then + echo -e "${RED}VBoxManage not found. VirtualBox may not be installed.${NC}" + exit 1 +fi + +# Configuration +VBOX_SHARED_FOLDER_NAME="host_etc" +HOSTS_ENFORCEMENT_MARKER="/var/lib/vbox-hosts-enforced" + +# Get list of all VMs +get_all_vms() { + VBoxManage list vms | awk -F'"' '{print $2}' +} + +# Get list of running VMs +get_running_vms() { + VBoxManage list runningvms | awk -F'"' '{print $2}' +} + +# Configure a VM to use host DNS (NAT network) +configure_vm_dns() { + local vm_name="$1" + echo -e "${BLUE}Configuring DNS for VM: ${vm_name}${NC}" + + # Enable DNS proxy for NAT adapter (adapter 1 by default) + # This makes the VM use the host's DNS resolution + VBoxManage modifyvm "$vm_name" --natdnshostresolver1 on 2>/dev/null || true + VBoxManage modifyvm "$vm_name" --natdnsproxy1 on 2>/dev/null || true + + echo -e "${GREEN}DNS configuration applied to ${vm_name}${NC}" +} + +# Add shared folder for /etc directory (read-only) +configure_hosts_shared_folder() { + local vm_name="$1" + echo -e "${BLUE}Setting up /etc/hosts sharing for VM: ${vm_name}${NC}" + + # Remove existing shared folder if present + VBoxManage sharedfolder remove "$vm_name" --name "$VBOX_SHARED_FOLDER_NAME" 2>/dev/null || true + + # Add /etc as a shared folder (read-only) + VBoxManage sharedfolder add "$vm_name" \ + --name "$VBOX_SHARED_FOLDER_NAME" \ + --hostpath "/etc" \ + --readonly \ + --automount 2>/dev/null || { + echo -e "${YELLOW}Could not add shared folder to ${vm_name} (VM may be running)${NC}" + return 1 + } + + echo -e "${GREEN}Shared folder configured for ${vm_name}${NC}" + return 0 +} + +# Create a startup script that can be placed in VMs +generate_vm_startup_script() { + local output_file="${1:-/tmp/vbox_hosts_sync.sh}" + + cat > "$output_file" << 'EOF' +#!/bin/bash +# VirtualBox VM startup script to sync /etc/hosts from host machine +# This should be placed in the VM and run at startup + +set -e + +SHARED_FOLDER_MOUNT="/mnt/host_etc" +HOST_HOSTS_FILE="${SHARED_FOLDER_MOUNT}/hosts" +VM_HOSTS_FILE="/etc/hosts" +BACKUP_HOSTS_FILE="/etc/hosts.pre-vbox-sync" + +# Function to check if running in VirtualBox +is_virtualbox() { + if command -v dmidecode > /dev/null 2>&1; then + if sudo dmidecode -s system-product-name 2>/dev/null | grep -qi "VirtualBox"; then + return 0 + fi + fi + + if command -v systemd-detect-virt > /dev/null 2>&1; then + if systemd-detect-virt | grep -qi "oracle"; then + return 0 + fi + fi + + return 1 +} + +# Only run if we're in VirtualBox +if ! is_virtualbox; then + exit 0 +fi + +# Create mount point if it doesn't exist +mkdir -p "$SHARED_FOLDER_MOUNT" + +# Try to mount the shared folder (if Guest Additions are installed) +if ! mountpoint -q "$SHARED_FOLDER_MOUNT"; then + if command -v mount.vboxsf > /dev/null 2>&1; then + mount -t vboxsf -o ro host_etc "$SHARED_FOLDER_MOUNT" 2>/dev/null || { + echo "Could not mount VirtualBox shared folder" + exit 0 + } + else + echo "VirtualBox Guest Additions not installed, cannot sync hosts file" + exit 0 + fi +fi + +# Sync hosts file if the shared one exists +if [ -f "$HOST_HOSTS_FILE" ]; then + # Backup current hosts file if not already backed up + if [ ! -f "$BACKUP_HOSTS_FILE" ]; then + cp "$VM_HOSTS_FILE" "$BACKUP_HOSTS_FILE" + fi + + # Copy host's hosts file to VM + cp "$HOST_HOSTS_FILE" "$VM_HOSTS_FILE" + echo "Synced /etc/hosts from host machine" + + # Make it harder to modify (though not impossible in VM) + chmod 444 "$VM_HOSTS_FILE" +fi +EOF + + chmod +x "$output_file" + echo -e "${GREEN}Generated VM startup script at ${output_file}${NC}" + echo -e "${CYAN}Copy this script to your VMs and add it to their startup (e.g., /etc/rc.local or systemd)${NC}" +} + +# Apply enforcement to all VMs +enforce_all_vms() { + local -a vms + mapfile -t vms < <(get_all_vms) + + if [[ ${#vms[@]} -eq 0 ]]; then + echo -e "${YELLOW}No VirtualBox VMs found.${NC}" + return 0 + fi + + echo -e "${CYAN}Found ${#vms[@]} VM(s). Applying /etc/hosts enforcement...${NC}" + + local success=0 + local failed=0 + + for vm in "${vms[@]}"; do + echo -e "\n${BLUE}Processing VM: ${vm}${NC}" + + # Configure DNS settings (works even when VM is running) + configure_vm_dns "$vm" + + # Try to configure shared folder (only works when VM is stopped) + if configure_hosts_shared_folder "$vm"; then + ((success++)) + else + ((failed++)) + echo -e "${YELLOW}Note: Stop the VM and run this script again to add shared folder${NC}" + fi + done + + echo -e "\n${GREEN}Enforcement complete!${NC}" + echo -e "Successfully configured: ${success} VM(s)" + [[ $failed -gt 0 ]] && echo -e "${YELLOW}Needs VM shutdown for full config: ${failed} VM(s)${NC}" + + # Mark that enforcement has been applied + touch "$HOSTS_ENFORCEMENT_MARKER" +} + +# Check if enforcement is needed +check_enforcement_status() { + local -a vms + mapfile -t vms < <(get_all_vms) + + if [[ ${#vms[@]} -eq 0 ]]; then + echo -e "${GREEN}No VMs to enforce.${NC}" + return 0 + fi + + if [[ ! -f $HOSTS_ENFORCEMENT_MARKER ]]; then + echo -e "${YELLOW}Hosts enforcement has not been applied to VMs.${NC}" + return 1 + fi + + echo -e "${GREEN}Hosts enforcement marker found.${NC}" + return 0 +} + +# Show status +show_status() { + echo -e "${CYAN}VirtualBox Hosts Enforcement Status${NC}" + echo -e "${CYAN}====================================${NC}\n" + + local -a all_vms running_vms + mapfile -t all_vms < <(get_all_vms) + mapfile -t running_vms < <(get_running_vms) + + echo -e "Total VMs: ${#all_vms[@]}" + echo -e "Running VMs: ${#running_vms[@]}" + + if [[ -f $HOSTS_ENFORCEMENT_MARKER ]]; then + echo -e "Enforcement status: ${GREEN}Applied${NC}" + else + echo -e "Enforcement status: ${RED}Not applied${NC}" + fi + + echo -e "\n${CYAN}VMs:${NC}" + for vm in "${all_vms[@]}"; do + local running="" + if printf '%s\n' "${running_vms[@]}" | grep -qx "$vm"; then + running=" ${GREEN}[RUNNING]${NC}" + fi + echo -e " - ${vm}${running}" + done +} + +# Main function +main() { + local action="${1:-enforce}" + + case "$action" in + enforce|apply) + enforce_all_vms + ;; + check) + if check_enforcement_status; then + exit 0 + else + exit 1 + fi + ;; + status) + show_status + ;; + generate-script) + local output="${2:-/tmp/vbox_hosts_sync.sh}" + generate_vm_startup_script "$output" + ;; + *) + echo -e "${CYAN}VirtualBox /etc/hosts Enforcement Tool${NC}" + echo "" + echo "Usage: $0 [command]" + echo "" + echo "Commands:" + echo " enforce Apply /etc/hosts enforcement to all VMs (default)" + echo " check Check if enforcement has been applied" + echo " status Show current enforcement status" + echo " generate-script [path] Generate a script to place in VMs for hosts sync" + echo "" + echo "This tool configures VirtualBox VMs to:" + echo " 1. Use host's DNS resolution (via NAT DNS proxy)" + echo " 2. Share /etc from host (read-only) for hosts file access" + echo "" + exit 0 + ;; + esac +} + +main "$@"